Commit 5a87b33df2fd9fda3999b1558966557f2183fa05

Authored by YunaiV
1 parent 0343c4d2

fix:【MP 公众号】receiveMessage 记录消息时,兜底 MpUserDO 的创建边界

yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/user/MpUserMapper.java
@@ -21,7 +21,7 @@ public interface MpUserMapper extends BaseMapperX<MpUserDO> { @@ -21,7 +21,7 @@ public interface MpUserMapper extends BaseMapperX<MpUserDO> {
21 } 21 }
22 22
23 default MpUserDO selectByAppIdAndOpenid(String appId, String openid) { 23 default MpUserDO selectByAppIdAndOpenid(String appId, String openid) {
24 - return selectOne(MpUserDO::getAppId, appId, 24 + return selectFirstOne(MpUserDO::getAppId, appId,
25 MpUserDO::getOpenid, openid); 25 MpUserDO::getOpenid, openid);
26 } 26 }
27 27
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/menu/MenuHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mp.service.handler.menu; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.mp.service.handler.menu;
3 import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder; 3 import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
4 import cn.iocoder.yudao.module.mp.service.menu.MpMenuService; 4 import cn.iocoder.yudao.module.mp.service.menu.MpMenuService;
5 import me.chanjar.weixin.common.session.WxSessionManager; 5 import me.chanjar.weixin.common.session.WxSessionManager;
6 -import me.chanjar.weixin.mp.api.WxMpMenuService;  
7 import me.chanjar.weixin.mp.api.WxMpMessageHandler; 6 import me.chanjar.weixin.mp.api.WxMpMessageHandler;
8 import me.chanjar.weixin.mp.api.WxMpService; 7 import me.chanjar.weixin.mp.api.WxMpService;
9 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 8 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
@@ -13,8 +12,6 @@ import org.springframework.stereotype.Component; @@ -13,8 +12,6 @@ import org.springframework.stereotype.Component;
13 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
14 import java.util.Map; 13 import java.util.Map;
15 14
16 -import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;  
17 -  
18 /** 15 /**
19 * 自定义菜单的事件处理器 16 * 自定义菜单的事件处理器
20 * 17 *
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageReceiveHandler.java
@@ -29,7 +29,7 @@ public class MessageReceiveHandler implements WxMpMessageHandler { @@ -29,7 +29,7 @@ public class MessageReceiveHandler implements WxMpMessageHandler {
29 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, 29 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
30 WxMpService wxMpService, WxSessionManager sessionManager) { 30 WxMpService wxMpService, WxSessionManager sessionManager) {
31 log.info("[handle][接收到请求消息,内容:{}]", wxMessage); 31 log.info("[handle][接收到请求消息,内容:{}]", wxMessage);
32 - mpMessageService.receiveMessage(MpContextHolder.getAppId(), wxMessage); 32 + mpMessageService.receiveMessage(wxMpService, MpContextHolder.getAppId(), wxMessage);
33 return null; 33 return null;
34 } 34 }
35 35
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessageP @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessageP
5 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessageSendReqVO; 5 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessageSendReqVO;
6 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; 6 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
7 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; 7 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
  8 +import me.chanjar.weixin.mp.api.WxMpService;
8 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 9 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
9 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 10 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
10 11
@@ -31,7 +32,7 @@ public interface MpMessageService { @@ -31,7 +32,7 @@ public interface MpMessageService {
31 * @param appId 微信公众号 appId 32 * @param appId 微信公众号 appId
32 * @param wxMessage 消息 33 * @param wxMessage 消息
33 */ 34 */
34 - void receiveMessage(String appId, WxMpXmlMessage wxMessage); 35 + void receiveMessage(WxMpService weixinService, String appId, WxMpXmlMessage wxMessage);
35 36
36 /** 37 /**
37 * 使用公众号,给粉丝回复消息 38 * 使用公众号,给粉丝回复消息
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
1 package cn.iocoder.yudao.module.mp.service.message; 1 package cn.iocoder.yudao.module.mp.service.message;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 -import cn.hutool.core.util.ObjUtil; 4 +import cn.hutool.core.thread.ThreadUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessagePageReqVO; 7 import cn.iocoder.yudao.module.mp.controller.admin.message.vo.message.MpMessagePageReqVO;
@@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; @@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
20 import cn.iocoder.yudao.module.mp.service.user.MpUserService; 20 import cn.iocoder.yudao.module.mp.service.user.MpUserService;
21 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
22 import jakarta.validation.Validator; 22 import jakarta.validation.Validator;
  23 +import lombok.SneakyThrows;
23 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
24 import me.chanjar.weixin.common.api.WxConsts; 25 import me.chanjar.weixin.common.api.WxConsts;
25 import me.chanjar.weixin.common.error.WxErrorException; 26 import me.chanjar.weixin.common.error.WxErrorException;
@@ -27,10 +28,13 @@ import me.chanjar.weixin.mp.api.WxMpService; @@ -27,10 +28,13 @@ import me.chanjar.weixin.mp.api.WxMpService;
27 import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; 28 import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage;
28 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 29 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
29 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 30 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
  31 +import me.chanjar.weixin.mp.bean.result.WxMpUser;
30 import org.springframework.context.annotation.Lazy; 32 import org.springframework.context.annotation.Lazy;
31 import org.springframework.stereotype.Service; 33 import org.springframework.stereotype.Service;
32 import org.springframework.validation.annotation.Validated; 34 import org.springframework.validation.annotation.Validated;
33 35
  36 +import java.util.concurrent.TimeUnit;
  37 +
34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 38 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
35 import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MESSAGE_SEND_FAIL; 39 import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MESSAGE_SEND_FAIL;
36 40
@@ -68,18 +72,31 @@ public class MpMessageServiceImpl implements MpMessageService { @@ -68,18 +72,31 @@ public class MpMessageServiceImpl implements MpMessageService {
68 } 72 }
69 73
70 @Override 74 @Override
71 - public void receiveMessage(String appId, WxMpXmlMessage wxMessage) { 75 + @SneakyThrows
  76 + public void receiveMessage(WxMpService weixinService, String appId, WxMpXmlMessage wxMessage) {
72 // 获得关联信息 77 // 获得关联信息
73 MpAccountDO account = mpAccountService.getAccountFromCache(appId); 78 MpAccountDO account = mpAccountService.getAccountFromCache(appId);
74 Assert.notNull(account, "公众号账号({}) 不存在", appId); 79 Assert.notNull(account, "公众号账号({}) 不存在", appId);
75 80
76 - // 订阅事件不记录,因为此时公众号粉丝表中还没有此粉丝的数据  
77 - // TODO @芋艿:这个修复,后续看看还有啥问题  
78 - if (ObjUtil.equal(wxMessage.getEvent(), WxConsts.EventType.SUBSCRIBE)) {  
79 - return;  
80 - }  
81 - 81 + // 获取用户
82 MpUserDO user = mpUserService.getUser(appId, wxMessage.getFromUser()); 82 MpUserDO user = mpUserService.getUser(appId, wxMessage.getFromUser());
  83 + if (user == null) {
  84 + // 特殊情况:因为 receiveMessage 是异步记录,可能 SubscribeHandler 还没存储好 User,此时 sleep 轮询
  85 + for (int i = 0; i < 3; i++) {
  86 + ThreadUtil.sleep(5, TimeUnit.SECONDS);
  87 + user = mpUserService.getUser(appId, wxMessage.getFromUser());
  88 + if (user != null) {
  89 + break;
  90 + }
  91 + log.warn("[receiveMessage][粉丝({}/{}) 不存在,第 {} 次重试失败]", appId, wxMessage.getFromUser(), i + 1);
  92 + }
  93 + }
  94 + // 特殊情况:可能 SubscribeHandler 没处理正确(例如说发生异常),则主动创建
  95 + if (user == null) {
  96 + log.warn("[receiveMessage][粉丝({}/{}) 不存在,主动创建]", appId, wxMessage.getFromUser());
  97 + WxMpUser wxMpUser = weixinService.getUserService().userInfo(wxMessage.getFromUser());
  98 + user = mpUserService.saveUser(appId, wxMpUser);
  99 + }
83 Assert.notNull(user, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser()); 100 Assert.notNull(user, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser());
84 101
85 // 记录消息 102 // 记录消息