感谢@JFinal提供的JFinal-weixin项目,使用到了里面的微信入口消息和出口消息及工具类。
https://gitee.com/jfinal/jfinal-weixin
jfianl-weixin项目非常适合于在Jfinal环境下开发微信消息处理,只需要继承MsgController基类复写相关方法即可。那么在Spring环境下如何处理呢?
见gitee上项目:https://gitee.com/xxssyyyyssxx/weixin-spring-boot-starter
直接上代码。
import cn.palmte.gpas.utils.Signature;
import com.jfinal.kit.HttpKit;
import com.jfinal.weixin.iot.msg.InEquDataMsg;
import com.jfinal.weixin.iot.msg.InEqubindEvent;
import com.jfinal.weixin.sdk.msg.InMsgParser;
import com.jfinal.weixin.sdk.msg.in.*;
import com.jfinal.weixin.sdk.msg.in.card.*;
import com.jfinal.weixin.sdk.msg.in.event.*;
import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 这是微信所有消息的总入口,需要在微信公众号后台配置URL
* @author xiongshiyan at 2018/7/26 , contact me with email yanshixiong@126.com or phone 15208384257
*/
@RequestMapping("/open/wx")
public abstract class BaseWxDevelopController {
/**
* 与接口配置信息中的Token 要一致
*/
private static String TOKEN = "xxxxxxxxxxxx";
private static final Logger logger = LoggerFactory.getLogger(BaseWxDevelopController.class);
@RequestMapping(value = "/develop",method ={ RequestMethod.GET})
public String develop(HttpServletRequest request) throws Exception{
logger.info(request.getParameter("signature"));
//每次请求都会带上这几个除了echostr的字符串,所以在post的时候也可以利用他们验证请求来自微信
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
// 通过检验signature 对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (Signature.checkSignature(TOKEN, signature, timestamp, nonce)) {
return echostr;
}else {
return "你不是微信服务器,请自重!!";
}
}
/**
* 以下是jfianl-weixin的处理方式 WxDevelopFinalController利用责任链,就有更好的处理方式
*/
@RequestMapping(value = "/develop",method ={ RequestMethod.POST})
public String develop(HttpServletRequest request , HttpServletResponse response) throws Exception{
//将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/xml;charset=utf-8");
String xml = HttpKit.readData(request);
logger.info(xml);
InMsg msg = InMsgParser.parse(xml);
if (msg instanceof InTextMsg)
return processInTextMsg((InTextMsg) msg);
else if (msg instanceof InImageMsg)
return processInImageMsg((InImageMsg) msg);
else if (msg instanceof InSpeechRecognitionResults) //update by unas at 2016-1-29, 由于继承InVoiceMsg,需要在InVoiceMsg前判断类型
return processInSpeechRecognitionResults((InSpeechRecognitionResults) msg);
else if (msg instanceof InVoiceMsg)
return processInVoiceMsg((InVoiceMsg) msg);
else if (msg instanceof InVideoMsg)
return processInVideoMsg((InVideoMsg) msg);
else if (msg instanceof InShortVideoMsg) //支持小视频
return processInShortVideoMsg((InShortVideoMsg) msg);
else if (msg instanceof InLocationMsg)
return processInLocationMsg((InLocationMsg) msg);
else if (msg instanceof InLinkMsg)
return processInLinkMsg((InLinkMsg) msg);
else if (msg instanceof InCustomEvent)
return processInCustomEvent((InCustomEvent) msg);
else if (msg instanceof InFollowEvent)
return processInFollowEvent((InFollowEvent) msg);
else if (msg instanceof InQrCodeEvent)
return processInQrCodeEvent((InQrCodeEvent) msg);
else if (msg instanceof InLocationEvent)
return processInLocationEvent((InLocationEvent) msg);
else if (msg instanceof InMassEvent)
return processInMassEvent((InMassEvent) msg);
else if (msg instanceof InMenuEvent)
return processInMenuEvent((InMenuEvent) msg);
else if (msg instanceof InTemplateMsgEvent)
return processInTemplateMsgEvent((InTemplateMsgEvent) msg);
else if (msg instanceof InShakearoundUserShakeEvent)
return processInShakearoundUserShakeEvent((InShakearoundUserShakeEvent) msg);
else if (msg instanceof InVerifySuccessEvent)
return processInVerifySuccessEvent((InVerifySuccessEvent) msg);
else if (msg instanceof InVerifyFailEvent)
return processInVerifyFailEvent((InVerifyFailEvent) msg);
else if (msg instanceof InPoiCheckNotifyEvent)
return processInPoiCheckNotifyEvent((InPoiCheckNotifyEvent) msg);
else if (msg instanceof InWifiEvent)
return processInWifiEvent((InWifiEvent) msg);
else if (msg instanceof InUserCardEvent)
return processInUserCardEvent((InUserCardEvent) msg);
else if (msg instanceof InUpdateMemberCardEvent)
return processInUpdateMemberCardEvent((InUpdateMemberCardEvent) msg);
else if (msg instanceof InUserPayFromCardEvent)
return processInUserPayFromCardEvent((InUserPayFromCardEvent) msg);
else if (msg instanceof InMerChantOrderEvent)
return processInMerChantOrderEvent((InMerChantOrderEvent) msg);
else if (msg instanceof InCardPassCheckEvent)
return processInCardPassCheckEvent((InCardPassCheckEvent) msg);
else if (msg instanceof InCardPayOrderEvent)
return processInCardPayOrderEvent((InCardPayOrderEvent) msg);
else if (msg instanceof InCardSkuRemindEvent)
return processInCardSkuRemindEvent((InCardSkuRemindEvent) msg);
else if (msg instanceof InUserConsumeCardEvent)
return processInUserConsumeCardEvent((InUserConsumeCardEvent) msg);
else if (msg instanceof InUserGetCardEvent)
return processInUserGetCardEvent((InUserGetCardEvent) msg);
else if (msg instanceof InUserGiftingCardEvent)
return processInUserGiftingCardEvent((InUserGiftingCardEvent) msg);
//===================微信智能硬件========================//
else if (msg instanceof InEqubindEvent)
return processInEqubindEvent((InEqubindEvent) msg);
else if (msg instanceof InEquDataMsg)
return processInEquDataMsg((InEquDataMsg) msg);
//===================微信智能硬件========================//
else if (msg instanceof InNotDefinedEvent) {
logger.error("未能识别的事件类型。 消息 xml 内容为:\n" + xml);
return processIsNotDefinedEvent((InNotDefinedEvent) msg);
} else if (msg instanceof InNotDefinedMsg) {
logger.error("未能识别的消息类型。 消息 xml 内容为:\n" + xml);
return processIsNotDefinedMsg((InNotDefinedMsg) msg);
}
return "error";
}
public String getInMsgXml(HttpServletRequest request) {
return HttpKit.readData(request);
}
/**
* 处理接收到的文本消息
* @param inTextMsg 处理接收到的文本消息
*/
protected String processInTextMsg(InTextMsg inTextMsg)throws Exception{
return "";
}
/**
* 处理接收到的图片消息
* @param inImageMsg 处理接收到的图片消息
*/
protected String processInImageMsg(InImageMsg inImageMsg)throws Exception{
return "";
}
/**
* 处理接收到的语音消息
* @param inVoiceMsg 处理接收到的语音消息
*/
protected String processInVoiceMsg(InVoiceMsg inVoiceMsg)throws Exception{
return "";
}
/**
* 处理接收到的视频消息
* @param inVideoMsg 处理接收到的视频消息
*/
protected String processInVideoMsg(InVideoMsg inVideoMsg)throws Exception{
return "";
}
/**
* 处理接收到的小视频消息
* @param inShortVideoMsg 处理接收到的小视频消息
*/
protected String processInShortVideoMsg(InShortVideoMsg inShortVideoMsg)throws Exception{
return "";
}
/**
* 处理接收到的地址位置消息
* @param inLocationMsg 处理接收到的地址位置消息
*/
protected String processInLocationMsg(InLocationMsg inLocationMsg)throws Exception{
return "";
}
/**
* 处理接收到的链接消息
* @param inLinkMsg 处理接收到的链接消息
*/
protected String processInLinkMsg(InLinkMsg inLinkMsg)throws Exception{
return "";
}
/**
* 处理接收到的多客服管理事件
* @param inCustomEvent 处理接收到的多客服管理事件
*/
protected String processInCustomEvent(InCustomEvent inCustomEvent)throws Exception{
return "";
}
/**
* 处理接收到的关注/取消关注事件
* @param inFollowEvent 处理接收到的关注/取消关注事件
*/
protected String processInFollowEvent(InFollowEvent inFollowEvent)throws Exception{
return "";
}
/**
* 处理接收到的扫描带参数二维码事件
* @param inQrCodeEvent 处理接收到的扫描带参数二维码事件
*/
protected String processInQrCodeEvent(InQrCodeEvent inQrCodeEvent)throws Exception{
return "";
}
/**
* 处理接收到的上报地理位置事件
* @param inLocationEvent 处理接收到的上报地理位置事件
*/
protected String processInLocationEvent(InLocationEvent inLocationEvent)throws Exception{
return "";
}
/**
* 处理接收到的群发任务结束时通知事件
* @param inMassEvent 处理接收到的群发任务结束时通知事件
*/
protected String processInMassEvent(InMassEvent inMassEvent)throws Exception{
return "";
}
/**
* 处理接收到的自定义菜单事件
* @param inMenuEvent 处理接收到的自定义菜单事件
*/
protected String processInMenuEvent(InMenuEvent inMenuEvent)throws Exception{
return "";
}
/**
* 处理接收到的语音识别结果
* @param inSpeechRecognitionResults 处理接收到的语音识别结果
*/
protected String processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults)throws Exception{
return "";
}
/**
* 处理接收到的模板消息是否送达成功通知事件
* @param inTemplateMsgEvent 处理接收到的模板消息是否送达成功通知事件
*/
protected String processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent)throws Exception{
return "";
}
/**
* 处理微信摇一摇事件
* @param inShakearoundUserShakeEvent 处理微信摇一摇事件
*/
protected String processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent)throws Exception{
return "";
}
/**
* 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知
* @param inVerifySuccessEvent 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知
*/
protected String processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent)throws Exception{
return "";
}
/**
* 资质认证失败 || 名称认证失败
* @param inVerifyFailEvent 资质认证失败 || 名称认证失败
*/
protected String processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent)throws Exception{
return "";
}
/**
* 门店在审核事件消息
* @param inPoiCheckNotifyEvent 门店在审核事件消息
*/
protected String processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent)throws Exception{
return "";
}
/**
* WIFI连网后下发消息 by unas at 2016-1-29
* @param inWifiEvent WIFI连网后下发消息
*/
protected String processInWifiEvent(InWifiEvent inWifiEvent)throws Exception{
return "";
}
/**
* 1. 微信会员卡二维码扫描领取接口
* 2. 微信会员卡激活接口
* 3. 卡券删除事件推送
* 4. 从卡券进入公众号会话事件推送
* @param inUserCardEvent InUserCardEvent
*/
protected String processInUserCardEvent(InUserCardEvent inUserCardEvent)throws Exception{
return "";
}
/**
* 微信会员卡积分变更
* @param inUpdateMemberCardEvent 微信会员卡积分变更
*/
protected String processInUpdateMemberCardEvent(InUpdateMemberCardEvent inUpdateMemberCardEvent)throws Exception{
return "";
}
/**
* 微信会员卡快速买单
* @param inUserPayFromCardEvent 微信会员卡快速买单
*/
protected String processInUserPayFromCardEvent(InUserPayFromCardEvent inUserPayFromCardEvent)throws Exception{
return "";
}
/**
* 微信小店订单支付成功接口消息
* @param inMerChantOrderEvent 微信小店订单支付成功接口消息
*/
protected String processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent)throws Exception{
return "";
}
//
/**
* 没有找到对应的事件消息
* @param inNotDefinedEvent 没有对应的事件消息
*/
protected String processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent)throws Exception{
return "";
}
/**
* 没有找到对应的消息
* @param inNotDefinedMsg 没有对应消息
*/
protected String processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg)throws Exception{
return "";
}
/**
* 卡券转赠事件推送
* @param msg 卡券转赠事件推送
*/
protected String processInUserGiftingCardEvent(InUserGiftingCardEvent msg)throws Exception{
return "";
}
/**
* 卡券领取事件推送
* @param msg 卡券领取事件推送
*/
protected String processInUserGetCardEvent(InUserGetCardEvent msg)throws Exception{
return "";
}
/**
* 卡券核销事件推送
* @param msg 卡券核销事件推送
*/
protected String processInUserConsumeCardEvent(InUserConsumeCardEvent msg)throws Exception{
return "";
}
/**
* 卡券库存报警事件
* @param msg 卡券库存报警事件
*/
protected String processInCardSkuRemindEvent(InCardSkuRemindEvent msg)throws Exception{
return "";
}
/**
* 券点流水详情事件
* @param msg 券点流水详情事件
*/
protected String processInCardPayOrderEvent(InCardPayOrderEvent msg)throws Exception{
return "";
}
/**
* 审核事件推送
* @param msg 审核事件推送
*/
protected String processInCardPassCheckEvent(InCardPassCheckEvent msg)throws Exception{
return "";
}
/**
* 处理微信硬件绑定和解绑事件
* @param msg 处理微信硬件绑定和解绑事件
*/
protected String processInEqubindEvent(InEqubindEvent msg)throws Exception{
return "";
}
/**
* 处理微信硬件发来数据
* @param msg 处理微信硬件发来数据
*/
protected String processInEquDataMsg(InEquDataMsg msg)throws Exception{
return "";
}
}
以上类定义了微信回调的入口URL和验签,开发只需要继承此类,复写相关方法即可。
/**
* 复写相应的方法,处理对应的消息
* @author xiongshiyan at 2018/7/26 , contact me with email yanshixiong@126.com or phone 15208384257
*/
@RestController
public class WxDevelopController extends BaseWxDevelopController{
@Autowired
private XXXService xxxService;
private static final Logger logger = LoggerFactory.getLogger(WxDevelopController.class);
@Override
protected String processInTextMsg(InTextMsg textMsg) throws Exception{
String answer = xxxService.xxxx(textMsg);
OutTextMsg outMsg = new OutTextMsg(textMsg);
outMsg.setContent(answer);
return outMsg.toXml();
}
}
但是这样做,如果以后功能比较多的时候,类就会急剧膨胀,成为一个超级大类。所以提供了更好用的类,该类使用责任链模式将容器中所有的消息处理器组装成一个责任链,开发只需要写消息处理器放入容器即可。
import cn.palmte.gpas.service.wx.BaseMessageHandler;
import cn.palmte.gpas.service.wx.FinalMessageHandler;
import com.jfinal.kit.HttpKit;
import com.jfinal.weixin.sdk.msg.InMsgParser;
import com.jfinal.weixin.sdk.msg.in.*;
import com.jfinal.weixin.sdk.msg.out.OutMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.OrderComparator;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
/**
* 将消息处理器放到容器中即可,容器自动发现并排序组成责任链
* @author xiongshiyan at 2018/7/26 , contact me with email yanshixiong@126.com or phone 15208384257
*/
@RestController
public class WxDevelopFinalController extends BaseWxDevelopController implements ApplicationContextAware{
private static final Logger logger = LoggerFactory.getLogger(WxDevelopController.class);
private ApplicationContext applicationContext;
private BaseMessageHandler firstMessageHandler;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@PostConstruct
public void init(){
//找到所有的消息处理器
Map<String, BaseMessageHandler> messageHandlerMap = applicationContext.getBeansOfType(BaseMessageHandler.class);
//没有就默认处理
if(null == messageHandlerMap || 0 == messageHandlerMap.size()){
this.firstMessageHandler = FinalMessageHandler.sharedFinalMessageHandler();
return;
}
Collection<BaseMessageHandler> values = messageHandlerMap.values();
List<BaseMessageHandler> handlers = new ArrayList<>(values);
sortPostProcessors(handlers);
//责任链
for (int i = handlers.size() - 1; i > 0; i--) {
BaseMessageHandler nextMessageHandler = handlers.get(i);
handlers.get(i-1).setNextMessageHandler(nextMessageHandler);
}
firstMessageHandler = handlers.get(0);
}
@Override
public String develop(HttpServletRequest request , HttpServletResponse response) throws Exception{
//将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/xml;charset=utf-8");
String xml = HttpKit.readData(request);
logger.info(xml);
InMsg inMsg = InMsgParser.parse(xml);
OutMsg outMsg = firstMessageHandler.handleMessage(inMsg);
return outMsg.toXml();
}
/**
* 按照Order接口或者注解排序
* @param messageHandlers 消息处理器
*/
private static void sortPostProcessors(List<?> messageHandlers) {
Collections.sort(messageHandlers, OrderComparator.INSTANCE);
}
}
首先定义了接口MessageHandler
import com.jfinal.weixin.sdk.msg.in.InMsg;
import com.jfinal.weixin.sdk.msg.out.OutMsg;
/**
* 处理消息的抽象接口,责任链模式
* @author xiongshiyan
*
*/
public interface MessageHandler {
/**
* 该你处理就处理消息的函数
* @param inMsg 微信来的消息
* @return outMsg 返回消息
* @throws Exception Exception
*/
OutMsg handleMessage(InMsg inMsg) throws Exception;
/**
* 设置你不能处理后续处理的对象
* @param nextMessageHandler 下一个处理器
*/
void setNextMessageHandler(MessageHandler nextMessageHandler);
}
定义一个基类完成责任链的处理,定义了本处理器能处理的条件和下一个处理器。
import com.jfinal.weixin.sdk.msg.in.InMsg;
import com.jfinal.weixin.sdk.msg.out.OutMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 默认的消息处理器,所有的消息处理器都必须继承于此类
* @author xiongshiyan
*/
public abstract class BaseMessageHandler implements MessageHandler {
private static final Logger logger = LoggerFactory.getLogger(BaseMessageHandler.class);
protected MessageHandler nextMessageHandler;
private MessageHandler finalMessageHandler;
public BaseMessageHandler() {
//默认的最终处理器,自己也可以通过setter方法改变
this.finalMessageHandler = FinalMessageHandler.sharedFinalMessageHandler();
}
public void setFinalMessageHandler(MessageHandler finalMessageHandler) {
this.finalMessageHandler = finalMessageHandler;
}
@Override
public void setNextMessageHandler(MessageHandler nextMessageHandler) {
this.nextMessageHandler = nextMessageHandler;
}
/**
* 模板方法模式实现主要的逻辑,是自己处理还是交给下游处理
*/
@Override
public OutMsg handleMessage(InMsg inMsg) throws Exception{
//1.我可以处理
if(canDo(inMsg)){
return handleByMe(inMsg);
}
//2.有下一个处理器
if(null != nextMessageHandler){
return nextMessageHandler.handleMessage(inMsg);
}
//3.最终处理器
return finalMessageHandler.handleMessage(inMsg);
}
/**
* 给出你处理的额条件
* @param inMsg 微信消息
* @return true if 你能处理
*/
public abstract boolean canDo(InMsg inMsg);
/**
* 你具体的处理
* @param inMsg 微信消息
* @return 返回消息
* @throws Exception Exception
*/
public abstract OutMsg handleByMe(InMsg inMsg) throws Exception;
}
其中最终处理器处理所有消息处理器都无法处理的消息。
import com.jfinal.weixin.sdk.msg.in.InMsg;
import com.jfinal.weixin.sdk.msg.out.OutMsg;
import com.jfinal.weixin.sdk.msg.out.OutTextMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 其他消息处理类处理不了的请求全部有这个类处理,返回一个失败信息
* @author 熊诗言
*
*/
public class FinalMessageHandler extends BaseMessageHandler {
private static final Logger logger = LoggerFactory.getLogger(FinalMessageHandler.class);
private static final FinalMessageHandler _instance = new FinalMessageHandler();
private FinalMessageHandler(){}
public static FinalMessageHandler sharedFinalMessageHandler(){
return _instance;
}
@Override
public boolean canDo(InMsg inMsg) {
return true;
}
@Override
public OutMsg handleByMe(InMsg inMsg) throws Exception{
logger.info(inMsg.getMsgType());
return new OutTextMsg(inMsg).setContent("未处理");
}
}
开发只需要继承此类,复写处理方法即可。
import cn.palmte.gpas.service.ZyzxService;
import com.jfinal.weixin.sdk.msg.in.InMsg;
import com.jfinal.weixin.sdk.msg.in.InTextMsg;
import com.jfinal.weixin.sdk.msg.out.OutMsg;
import com.jfinal.weixin.sdk.msg.out.OutTextMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author xiongshiyan at 2018/9/6 , contact me with email yanshixiong@126.com or phone 15208384257
*/
@Component
public class TextMessageHandler extends BaseMessageHandler{
private static final Logger logger = LoggerFactory.getLogger(TextMessageHandler.class);
@Autowired
private xxxService xxxService;
@Override
public OutMsg handleByMe(InMsg inMsg) throws Exception {
logger.info(inMsg.getMsgType() + " : " + inMsg.getFromUserName());
InTextMsg textMsg = (InTextMsg) inMsg;
String answer = xxxService.xxx(textMsg);
OutTextMsg outMsg = new OutTextMsg(textMsg);
outMsg.setContent(answer);
return outMsg;
}
@Override
public boolean canDo(InMsg inMsg) {
return inMsg instanceof InTextMsg;
}
}