Commit 5a87b33df2fd9fda3999b1558966557f2183fa05
1 parent
0343c4d2
fix:【MP 公众号】receiveMessage 记录消息时,兜底 MpUserDO 的创建边界
Showing
5 changed files
with
29 additions
and
14 deletions
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 | // 记录消息 |