Commit 69e653cd941dea253e80f02e78714c43cd98b6cb

Authored by YunaiV
1 parent 44f0bcd1

mp:location 的自动回复;关注后的自动回复

yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/message/MpAutoReplyMapper.java
@@ -34,4 +34,11 @@ public interface MpAutoReplyMapper extends BaseMapperX<MpAutoReplyDO> { @@ -34,4 +34,11 @@ public interface MpAutoReplyMapper extends BaseMapperX<MpAutoReplyDO> {
34 .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.MESSAGE.getType()) 34 .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.MESSAGE.getType())
35 .eq(MpAutoReplyDO::getRequestMessageType, requestMessageType)); 35 .eq(MpAutoReplyDO::getRequestMessageType, requestMessageType));
36 } 36 }
  37 +
  38 + default List<MpAutoReplyDO> selectListByAppIdAndSubscribe(String appId) {
  39 + return selectList(new LambdaQueryWrapperX<MpAutoReplyDO>()
  40 + .eq(MpAutoReplyDO::getAppId, appId)
  41 + .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.SUBSCRIBE.getType()));
  42 + }
  43 +
37 } 44 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/config/MpConfiguration.java
@@ -36,7 +36,7 @@ public class MpConfiguration { @@ -36,7 +36,7 @@ public class MpConfiguration {
36 @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") 36 @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
37 public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps, 37 public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps,
38 WxMpProperties wxMpProperties, 38 WxMpProperties wxMpProperties,
39 - MessageReceiveHandler logHandler, 39 + MessageReceiveHandler messageReceiveHandler,
40 KfSessionHandler kfSessionHandler, 40 KfSessionHandler kfSessionHandler,
41 StoreCheckNotifyHandler storeCheckNotifyHandler, 41 StoreCheckNotifyHandler storeCheckNotifyHandler,
42 MenuHandler menuHandler, 42 MenuHandler menuHandler,
@@ -45,10 +45,10 @@ public class MpConfiguration { @@ -45,10 +45,10 @@ public class MpConfiguration {
45 UnsubscribeHandler unsubscribeHandler, 45 UnsubscribeHandler unsubscribeHandler,
46 LocationHandler locationHandler, 46 LocationHandler locationHandler,
47 ScanHandler scanHandler, 47 ScanHandler scanHandler,
48 - MessageAutoReplyHandler msgHandler) { 48 + MessageAutoReplyHandler messageAutoReplyHandler) {
49 return new DefaultMpServiceFactory(redisTemplateWxRedisOps, wxMpProperties, 49 return new DefaultMpServiceFactory(redisTemplateWxRedisOps, wxMpProperties,
50 - logHandler, kfSessionHandler, storeCheckNotifyHandler, menuHandler,  
51 - nullHandler, subscribeHandler, unsubscribeHandler, locationHandler, scanHandler, msgHandler); 50 + messageReceiveHandler, kfSessionHandler, storeCheckNotifyHandler, menuHandler,
  51 + nullHandler, subscribeHandler, unsubscribeHandler, locationHandler, scanHandler, messageAutoReplyHandler);
52 } 52 }
53 53
54 } 54 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/DefaultMpServiceFactory.java
@@ -49,7 +49,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { @@ -49,7 +49,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
49 49
50 // ========== 各种 Handler ========== 50 // ========== 各种 Handler ==========
51 51
52 - private final MessageReceiveHandler logHandler; 52 + private final MessageReceiveHandler messageReceiveHandler;
53 private final KfSessionHandler kfSessionHandler; 53 private final KfSessionHandler kfSessionHandler;
54 private final StoreCheckNotifyHandler storeCheckNotifyHandler; 54 private final StoreCheckNotifyHandler storeCheckNotifyHandler;
55 private final MenuHandler menuHandler; 55 private final MenuHandler menuHandler;
@@ -58,7 +58,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { @@ -58,7 +58,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
58 private final UnsubscribeHandler unsubscribeHandler; 58 private final UnsubscribeHandler unsubscribeHandler;
59 private final LocationHandler locationHandler; 59 private final LocationHandler locationHandler;
60 private final ScanHandler scanHandler; 60 private final ScanHandler scanHandler;
61 - private final MessageAutoReplyHandler msgHandler; 61 + private final MessageAutoReplyHandler messageAutoReplyHandler;
62 62
63 @Override 63 @Override
64 public void init(List<MpAccountDO> list) { 64 public void init(List<MpAccountDO> list) {
@@ -108,7 +108,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { @@ -108,7 +108,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
108 private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) { 108 private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) {
109 WxMpMessageRouter router = new WxMpMessageRouter(mpService); 109 WxMpMessageRouter router = new WxMpMessageRouter(mpService);
110 // 记录所有事件的日志(异步执行) 110 // 记录所有事件的日志(异步执行)
111 - router.rule().handler(logHandler).next(); 111 + router.rule().handler(messageReceiveHandler).next();
112 112
113 // 接收客服会话管理事件 113 // 接收客服会话管理事件
114 router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) 114 router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
@@ -159,7 +159,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { @@ -159,7 +159,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
159 .event(WxConsts.EventType.SCAN).handler(scanHandler).end(); 159 .event(WxConsts.EventType.SCAN).handler(scanHandler).end();
160 160
161 // 默认 161 // 默认
162 - router.rule().async(false).handler(msgHandler).end(); 162 + router.rule().async(false).handler(messageAutoReplyHandler).end();
163 return router; 163 return router;
164 } 164 }
165 165
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/LocationHandler.java
1 package cn.iocoder.yudao.module.mp.service.handler.user; 1 package cn.iocoder.yudao.module.mp.service.handler.user;
2 2
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  5 +import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
  6 +import cn.iocoder.yudao.module.mp.service.message.MpAutoReplyService;
3 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
  8 +import me.chanjar.weixin.common.api.WxConsts;
4 import me.chanjar.weixin.common.session.WxSessionManager; 9 import me.chanjar.weixin.common.session.WxSessionManager;
5 import me.chanjar.weixin.mp.api.WxMpMessageHandler; 10 import me.chanjar.weixin.mp.api.WxMpMessageHandler;
6 import me.chanjar.weixin.mp.api.WxMpService; 11 import me.chanjar.weixin.mp.api.WxMpService;
@@ -8,38 +13,37 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; @@ -8,38 +13,37 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
8 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 13 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
9 import org.springframework.stereotype.Component; 14 import org.springframework.stereotype.Component;
10 15
  16 +import javax.annotation.Resource;
11 import java.util.Map; 17 import java.util.Map;
12 18
13 /** 19 /**
14 * 上报地理位置的事件处理器 20 * 上报地理位置的事件处理器
15 * 21 *
16 - * // TODO @芋艿:需要实现一下~ 22 + * 触发操作:打开微信公众号 -> 点击 + 号 -> 选择「语音」
  23 + *
  24 + * 逻辑:用户上传地理位置时,也可以触发自动回复
  25 + *
  26 + * @author 芋道源码
17 */ 27 */
18 @Component 28 @Component
19 @Slf4j 29 @Slf4j
20 public class LocationHandler implements WxMpMessageHandler { 30 public class LocationHandler implements WxMpMessageHandler {
21 31
  32 + @Resource
  33 + private MpAutoReplyService mpAutoReplyService;
  34 +
22 @Override 35 @Override
23 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, 36 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
24 WxMpService wxMpService, WxSessionManager sessionManager) { 37 WxMpService wxMpService, WxSessionManager sessionManager) {
25 -// if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) {  
26 -// //TODO 接收处理用户发送的地理位置消息  
27 -// try {  
28 -// String content = "感谢反馈,您的的地理位置已收到!";  
29 -// return new TextBuilder().build(content, wxMessage, null);  
30 -// } catch (Exception e) {  
31 -// log.error("位置消息接收处理失败", e);  
32 -// return null;  
33 -// }  
34 -// }  
35 -//  
36 -// //上报地理位置事件  
37 -// log.info("上报地理位置,纬度 : {},经度 : {},精度 : {}",  
38 -// wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision()));  
39 -  
40 - //TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用 38 + // 防御性编程:必须是 LOCATION 消息
  39 + if (ObjectUtil.notEqual(wxMessage.getMsgType(), WxConsts.XmlMsgType.LOCATION)) {
  40 + return null;
  41 + }
  42 + log.info("[handle][上报地理位置,纬度({})、经度({})、精度({})", wxMessage.getLatitude(),
  43 + wxMessage.getLongitude(), wxMessage.getPrecision());
41 44
42 - return null; 45 + // 自动回复
  46 + return mpAutoReplyService.replyForMessage(MpContextHolder.getAppId(), wxMessage);
43 } 47 }
44 48
45 } 49 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/SubscribeHandler.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mp.service.handler.user; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mp.service.handler.user;
3 import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; 3 import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
4 import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder; 4 import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
5 import cn.iocoder.yudao.module.mp.service.account.MpAccountService; 5 import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
  6 +import cn.iocoder.yudao.module.mp.service.message.MpAutoReplyService;
6 import cn.iocoder.yudao.module.mp.service.user.MpUserService; 7 import cn.iocoder.yudao.module.mp.service.user.MpUserService;
7 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
8 import me.chanjar.weixin.common.error.WxErrorException; 9 import me.chanjar.weixin.common.error.WxErrorException;
@@ -22,19 +23,15 @@ import java.util.Map; @@ -22,19 +23,15 @@ import java.util.Map;
22 * 关注的事件处理器 23 * 关注的事件处理器
23 * 24 *
24 * @author 芋道源码 25 * @author 芋道源码
25 - *  
26 - * // TODO 芋艿:待实现  
27 */ 26 */
28 @Component 27 @Component
29 @Slf4j 28 @Slf4j
30 public class SubscribeHandler implements WxMpMessageHandler { 29 public class SubscribeHandler implements WxMpMessageHandler {
31 30
32 @Resource 31 @Resource
33 - @Lazy // 延迟加载,解决循环依赖的问题  
34 - private MpAccountService mpAccountService;  
35 -  
36 - @Resource  
37 private MpUserService mpUserService; 32 private MpUserService mpUserService;
  33 + @Resource
  34 + private MpAutoReplyService mpAutoReplyService;
38 35
39 @Override 36 @Override
40 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, 37 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
@@ -49,24 +46,10 @@ public class SubscribeHandler implements WxMpMessageHandler { @@ -49,24 +46,10 @@ public class SubscribeHandler implements WxMpMessageHandler {
49 } 46 }
50 47
51 // 第二步,保存用户信息 48 // 第二步,保存用户信息
52 - MpUserDO mpUser = null;  
53 - if (wxMpUser != null) {  
54 - mpUser = mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser);  
55 - }  
56 -  
57 - // 第三步,回复关注的欢迎语 TODO 芋艿:关注的欢迎语  
58 -// return new TextBuilder().build("感谢关注", wxMessage, weixinService);  
59 - return null;  
60 - }  
61 -  
62 - /**  
63 - * 处理特殊请求,比如如果是扫码进来的,可以做相应处理  
64 - */  
65 - private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)  
66 - throws Exception { 49 + mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser);
67 50
68 - //TODO  
69 - return null; 51 + // 第三步,回复关注的欢迎语
  52 + return mpAutoReplyService.replyForSubscribe(MpContextHolder.getAppId(), wxMessage);
70 } 53 }
71 54
72 } 55 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpAutoReplyService.java
@@ -19,4 +19,13 @@ public interface MpAutoReplyService { @@ -19,4 +19,13 @@ public interface MpAutoReplyService {
19 */ 19 */
20 WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage); 20 WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage);
21 21
  22 + /**
  23 + * 当用户关注时,自动回复
  24 + *
  25 + * @param appId 微信公众号 appId
  26 + * @param wxMessage 消息
  27 + * @return 回复的消息
  28 + */
  29 + WxMpXmlOutMessage replyForSubscribe(String appId, WxMpXmlMessage wxMessage);
  30 +
22 } 31 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpAutoReplyServiceImpl.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.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.iocoder.yudao.module.mp.builder.TextBuilder; 4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
5 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO; 6 import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
6 import cn.iocoder.yudao.module.mp.dal.mysql.message.MpAutoReplyMapper; 7 import cn.iocoder.yudao.module.mp.dal.mysql.message.MpAutoReplyMapper;
7 -import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyMatchEnum; 8 +import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyTypeEnum;
  9 +import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
8 import me.chanjar.weixin.common.api.WxConsts; 10 import me.chanjar.weixin.common.api.WxConsts;
9 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 11 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
10 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 12 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
  13 +import org.springframework.context.annotation.Lazy;
11 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
12 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
13 16
@@ -25,15 +28,15 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService { @@ -25,15 +28,15 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
25 28
26 @Resource 29 @Resource
27 private MpMessageService mpMessageService; 30 private MpMessageService mpMessageService;
  31 + @Resource
  32 + @Lazy // 延迟加载,避免循环依赖
  33 + private MpAccountService mpAccountService;
28 34
29 @Resource 35 @Resource
30 private MpAutoReplyMapper mpAutoReplyMapper; 36 private MpAutoReplyMapper mpAutoReplyMapper;
31 37
32 @Override 38 @Override
33 public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) { 39 public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) {
34 -// if (true) {  
35 -// return new TextBuilder().build("nihao", wxMessage, null);  
36 -// }  
37 // 第一步,匹配自动回复 40 // 第一步,匹配自动回复
38 List<MpAutoReplyDO> replies = null; 41 List<MpAutoReplyDO> replies = null;
39 // 1.1 关键字 42 // 1.1 关键字
@@ -58,4 +61,24 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService { @@ -58,4 +61,24 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
58 return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply); 61 return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
59 } 62 }
60 63
  64 + @Override
  65 + public WxMpXmlOutMessage replyForSubscribe(String appId, WxMpXmlMessage wxMessage) {
  66 + // 第一步,匹配自动回复
  67 + List<MpAutoReplyDO> replies = mpAutoReplyMapper.selectListByAppIdAndSubscribe(appId);
  68 + MpAutoReplyDO reply = CollUtil.isNotEmpty(replies) ? CollUtil.getFirst(replies)
  69 + : buildDefaultSubscribeAutoReply(appId); // 如果不存在,提供一个默认末班
  70 +
  71 + // 第二步,基于自动回复,创建消息
  72 + return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
  73 + }
  74 +
  75 + private MpAutoReplyDO buildDefaultSubscribeAutoReply(String appId) {
  76 + MpAccountDO account = mpAccountService.getAccountFromCache(appId);
  77 + Assert.notNull(account, "公众号账号({}) 不存在", appId);
  78 + // 构建默认的【关注】自动回复
  79 + return new MpAutoReplyDO().setAppId(appId).setAccountId(account.getId())
  80 + .setType(MpAutoReplyTypeEnum.SUBSCRIBE.getType())
  81 + .setResponseMessageType(WxConsts.XmlMsgType.TEXT).setResponseContent("感谢关注");
  82 + }
  83 +
61 } 84 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
@@ -89,7 +89,7 @@ public class MpMessageServiceImpl implements MpMessageService { @@ -89,7 +89,7 @@ public class MpMessageServiceImpl implements MpMessageService {
89 } 89 }
90 mpMessageMapper.insert(message); 90 mpMessageMapper.insert(message);
91 91
92 -// WxConsts.MenuButtonType.VIEW 92 +// WxConsts.MenuButtonType.VIEW TODO 芋艿:待测试
93 // wxMessage.getEventKey() 93 // wxMessage.getEventKey()
94 94
95 // WxConsts.MenuButtonType.CLICK 95 // WxConsts.MenuButtonType.CLICK