微信公众号管理平台的实现
微信公众号管理平台是对多个公众号统一配置管理的平台,本文介绍了该平台的实现方案。项目地址:wechat-admin,该项目尚未开放,待对项目中的内容进行脱敏之后会公开。
核心问题
多个公众号如何复用同一个消息接口、同一套消息处理逻辑?
使用工具
微信Java开发工具包weixin-java-tools中的公众号开发工具weixin-java-mp。
具体实现
实现思路
- 多个公众号使用统一的消息接收接口,并附带公众号在管理平台的id作为标识;
- 统一消息接收接口获取id,动态配置仅用于本次消息处理的消息路由;
- 消息路由根据实际接收到的消息事件做出响应。
代码说明
- WxPortalController.java
java
@RestController
@RequestMapping("/portal/WxMpAccount/{id}")
public class WxPortalController {
@Autowired
private WxMpServiceHelper wxMpServiceHelper;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final String ENCRYPT_TYPE_AES = "aes";
@ResponseBody
@GetMapping(produces = "text/plain; charset=utf-8")
public String authGet(@PathVariable Long id,
@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
logger.info("\n接收到来自微信服务器的认证消息:[{},{},{},{}]", signature, timestamp, nonce, echostr);
if (wxMpServiceHelper.wxMpService(id).checkSignature(timestamp, nonce, signature)) {
return echostr;
}
return "非法请求";
}
@ResponseBody
@PostMapping(produces = "application/xml; charset=utf-8")
public String post(@PathVariable Long id,
@RequestBody String requestBody,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("signature") String signature,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature",required = false) String msgSignature) {
logger.debug("\n接收微信请求:[signature=[{}], encType=[{}], msgSignature=[{}], timestamp=[{}], nonce=[{}], " +
"requestBody=[\n{}\n] ", signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!wxMpServiceHelper.wxMpService(id).checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
String out = null;
if (encType == null) {
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
WxMpXmlOutMessage outMessage = wxMpServiceHelper.wxMpMessageRouter(id).route(inMessage);
out = outMessage == null ? "" : outMessage.toXml();
} else if (ENCRYPT_TYPE_AES.equalsIgnoreCase(encType)) {
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody,
wxMpServiceHelper.wxMpService(id).getWxMpConfigStorage(), timestamp, nonce, msgSignature);
logger.debug("\n消息解密后内容为:\n{}", inMessage.toString());
WxMpXmlOutMessage outMessage = wxMpServiceHelper.wxMpMessageRouter(id).route(inMessage);
out = outMessage == null ? "" : outMessage.toEncryptedXml(wxMpServiceHelper.wxMpService(id).getWxMpConfigStorage());
}
logger.debug("\n回复信息:{}", out);
return out;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
微信消息统一接收接口,此处最核心的就是wxMpServiceHelper.wxMpMessageRouter(id).route(inMessage);
。
- WxMpServiceHelper.java
java
@Component
public class WxMpServiceHelper {
@Autowired
private WxMpAccountRepository wxMpAccountRepository;
@Autowired
private SubscribeHandler subscribeHandler;
@Autowired
private WxEventHandlers handlers;
@Autowired
private MsgHandler msgHandler;
public BaseWxServiceImpl wxMpService(Long id) {
return new BaseWxServiceImpl(id, wxMpAccountRepository);
}
public WxMpMenuServiceImpl wxMpMenuService(Long id) {
return new WxMpMenuServiceImpl(wxMpService(id));
}
public WxMpMessageRouter wxMpMessageRouter(Long id) {
WxMpMessageRouter router = new WxMpMessageRouter(wxMpService(id));
//记录所有事件的日志
router.rule().handler(new LogHandler(id)).next();
//关注公众号
router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler.getHandler(id)).end();
//自定义处理逻辑
@SuppressWarnings("unchecked")
Map<String, WxMpMessageHandler> mpHandlerMap = (Map<String, WxMpMessageHandler>) MapUtils.getMap(handlers.getHandlerMap(), id);
if (MapUtils.isNotEmpty(mpHandlerMap)) {
for (Map.Entry<String, WxMpMessageHandler> handler : mpHandlerMap.entrySet()) {
router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(handler.getKey()).handler(handler.getValue()).end();
}
}
//自动回复消息
router.rule().async(false).handler(msgHandler.getHandler(id)).end();
return router;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
根据传入微信公众号在公众号管理平台中的id,动态获取该公众号的配置参数、消息类型与处理类路径的对应关系列表,并以此生成该公众号的消息路由。消息路由根据实际传入的消息事件作出响应。
评论区留言准则:
1. 本评论区禁止传播封建迷信、吸烟酗酒、低俗色情、赌博诈骗等任何违法违规内容。
2. 当他人以不正当方式诱导打赏、私下交易,请谨慎判断,以防人身财产损失。
3. 请勿轻信各类招聘征婚、代练代抽、私下交易、购买礼包码、游戏币等广告信息,谨防网络诈骗。