微信生态中关于消息处理的后端服务实现
业务场景
- 在微信公众号中,用户点击菜单、给公众号发消息、关注公众号等事件,微信都会将消息通过接口传给后台服务器,后台服务经过处理返回给微信,微信对应展示。
- 用户关注微信公众号,公众号自动回复用户“欢迎关注”此类的话术。还有用户对话框中输入消息,公众号自动回复对应话术这种场景
技术实现
-
使用binarywang的微信SDK,开源SDK,公司连不上GitHub,放个gitee的地址吧 https://gitee.com/binary
-
定义一个管理类,实现ApplicationRunner,项目启动后执行run方法,将微信的相关配置进行初始化。这时候会将微信消息处理的handler初始化到消息处理路由中。SDK中关于微信消息路由器的描述如下:
微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理
说明:
1. 配置路由规则时要按照从细到粗的原则,否则消息可能会被提前处理 2. 默认情况下消息只会被处理一次,除非使用 WxMpMessageRouterRule.next() 3. 规则的结束必须用WxMpMessageRouterRule.end()或者WxMpMessageRouterRule.next(),否则不会生效
路由器中可以放置对应的handler和拦截器进行消息的处理,拦截器在项目中没有实际应用,这里不再赘述。
微信消息拦截器,可以用来做验证
public interface WxMpMessageInterceptor { /** * 拦截微信消息 * * @param wxMessage * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param wxMpService * @param sessionManager * @return true代表OK,false代表不OK */ boolean intercept(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException; }
-
WxMpMessageHandler,SDK中定义的处理微信推送消息的处理器接口。项目中定义一个抽象类,实现WxMpMessageHandler接口,在实现handle的方法逻辑中再定义一个抽象方法。再分别定义各个实际处理的handler继承这个抽象类,重写抽象类中定义的抽象方法,这个抽象方法,是实际业务逻辑中处理消息的方法。
public interface WxMpMessageHandler { /** * 处理微信推送消息. * * @param wxMessage 微信推送消息 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 * @param wxMpService 服务类 * @param sessionManager session管理器 * @return xml格式的消息,如果在异步规则里处理的话,可以返回null * @throws WxErrorException 异常 */ WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException; }
-
在微信管理类执行run方法时,定义一个微信消息路由器WxMpMessageRouter,将自定义的handler和拦截器放进路由器中进行初始化。类似如下写法:
final WxMpMessageRouter wxMpMessageRouter = new WxMpMessageRouter(wxMpService); wxMpMessageRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SUBSCRIBE).handler(wxmpSubscribeHandler).interceptor(wxmpMessageInterceptorImpl).end();
实际执行逻辑
微信调用服务器接口
类似的请求报文体如下,还有签名、nonce、时间戳等参数
<xml>
<ToUserName><![CDATA[gh_28ba13224381]]></ToUserName
<FromUserName><![CDATA[微信公众号的openid]]></FromUserName> <CreateTime>1657779555</CreateTime>
<MsgType><![CDATA[text]]></MsgTyp
<Content><![CDATA[你好]]></Content
<MsgId>23733694539373153</MsgId
</xml>
验签并初始化消息
使用开源SDK中的方法,WxMpService.checkSignature()进行验签。
接着用WxMpXmlMessage 将xml报文转换成消息实体进行处理
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
消息处理
- 初始化的微信消息,放进微信消息路由器中进行处理。WxmpManager.getWxmpMessageRouter(appId).route(wxMessage);
- 遍历获取到的router,与传进去的微信消息实体进行匹配,比如msgtype、event等,详见WxMpMessageRouterRule中的test方法。
- 匹配到WxMpMessageRouterRule之后,执行rule.service方法并返回,这里也可以设置成异步的处理,异步处理只是开启一个新线程,new runnable,里面执行rule.service,外面executorService.submit,同时一个list接收返回。
- 在service方法中,先执行拦截器的逻辑,而后交给handler处理,返回执行结果。对应的每个handler在初始化路由器的时候已经放进去了,所以可以找到对应具体的handler执行逻辑。具体handler类似WxmpMenuHandler(微信公众号菜单事件handler)中只重写了具体的逻辑,所以handler中的hand方法还是要在父类WxmpAbstractMessageHandler中执行。
- 这里应该是用到了策略模式吧,大体是每个类型的消息通过条件匹配出来一个对应的执行路由,执行路由再通过初始化时的intercepter和handler,进行实际的逻辑处理,表现在controller中只需要将消息传给微信消息路由器并接收返回值即可。用着确实很方便,且如果需要新增事件类型处理的handler时,只需要写一个对应的handler并且微信消息路由器初始化时放进去即可。
项目中用到的消息路由
- 客服会话
- 自定义菜单
- 点击菜单连接
- 关注事件
- 取关事件
- 上报地理位置事件
- 接收地理位置事件
- 扫码事件
- 默认