Commit 69e653cd941dea253e80f02e78714c43cd98b6cb
1 parent
44f0bcd1
mp:location 的自动回复;关注后的自动回复
Showing
8 changed files
with
81 additions
and
55 deletions
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 | 34 | .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.MESSAGE.getType()) |
| 35 | 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 | 36 | @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") |
| 37 | 37 | public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps, |
| 38 | 38 | WxMpProperties wxMpProperties, |
| 39 | - MessageReceiveHandler logHandler, | |
| 39 | + MessageReceiveHandler messageReceiveHandler, | |
| 40 | 40 | KfSessionHandler kfSessionHandler, |
| 41 | 41 | StoreCheckNotifyHandler storeCheckNotifyHandler, |
| 42 | 42 | MenuHandler menuHandler, |
| ... | ... | @@ -45,10 +45,10 @@ public class MpConfiguration { |
| 45 | 45 | UnsubscribeHandler unsubscribeHandler, |
| 46 | 46 | LocationHandler locationHandler, |
| 47 | 47 | ScanHandler scanHandler, |
| 48 | - MessageAutoReplyHandler msgHandler) { | |
| 48 | + MessageAutoReplyHandler messageAutoReplyHandler) { | |
| 49 | 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 | 49 | |
| 50 | 50 | // ========== 各种 Handler ========== |
| 51 | 51 | |
| 52 | - private final MessageReceiveHandler logHandler; | |
| 52 | + private final MessageReceiveHandler messageReceiveHandler; | |
| 53 | 53 | private final KfSessionHandler kfSessionHandler; |
| 54 | 54 | private final StoreCheckNotifyHandler storeCheckNotifyHandler; |
| 55 | 55 | private final MenuHandler menuHandler; |
| ... | ... | @@ -58,7 +58,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { |
| 58 | 58 | private final UnsubscribeHandler unsubscribeHandler; |
| 59 | 59 | private final LocationHandler locationHandler; |
| 60 | 60 | private final ScanHandler scanHandler; |
| 61 | - private final MessageAutoReplyHandler msgHandler; | |
| 61 | + private final MessageAutoReplyHandler messageAutoReplyHandler; | |
| 62 | 62 | |
| 63 | 63 | @Override |
| 64 | 64 | public void init(List<MpAccountDO> list) { |
| ... | ... | @@ -108,7 +108,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { |
| 108 | 108 | private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) { |
| 109 | 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 | 114 | router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |
| ... | ... | @@ -159,7 +159,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { |
| 159 | 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 | 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 | 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 | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | +import me.chanjar.weixin.common.api.WxConsts; | |
| 4 | 9 | import me.chanjar.weixin.common.session.WxSessionManager; |
| 5 | 10 | import me.chanjar.weixin.mp.api.WxMpMessageHandler; |
| 6 | 11 | import me.chanjar.weixin.mp.api.WxMpService; |
| ... | ... | @@ -8,38 +13,37 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
| 8 | 13 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
| 9 | 14 | import org.springframework.stereotype.Component; |
| 10 | 15 | |
| 16 | +import javax.annotation.Resource; | |
| 11 | 17 | import java.util.Map; |
| 12 | 18 | |
| 13 | 19 | /** |
| 14 | 20 | * 上报地理位置的事件处理器 |
| 15 | 21 | * |
| 16 | - * // TODO @芋艿:需要实现一下~ | |
| 22 | + * 触发操作:打开微信公众号 -> 点击 + 号 -> 选择「语音」 | |
| 23 | + * | |
| 24 | + * 逻辑:用户上传地理位置时,也可以触发自动回复 | |
| 25 | + * | |
| 26 | + * @author 芋道源码 | |
| 17 | 27 | */ |
| 18 | 28 | @Component |
| 19 | 29 | @Slf4j |
| 20 | 30 | public class LocationHandler implements WxMpMessageHandler { |
| 21 | 31 | |
| 32 | + @Resource | |
| 33 | + private MpAutoReplyService mpAutoReplyService; | |
| 34 | + | |
| 22 | 35 | @Override |
| 23 | 36 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, |
| 24 | 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 | 3 | import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; |
| 4 | 4 | import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder; |
| 5 | 5 | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; |
| 6 | +import cn.iocoder.yudao.module.mp.service.message.MpAutoReplyService; | |
| 6 | 7 | import cn.iocoder.yudao.module.mp.service.user.MpUserService; |
| 7 | 8 | import lombok.extern.slf4j.Slf4j; |
| 8 | 9 | import me.chanjar.weixin.common.error.WxErrorException; |
| ... | ... | @@ -22,19 +23,15 @@ import java.util.Map; |
| 22 | 23 | * 关注的事件处理器 |
| 23 | 24 | * |
| 24 | 25 | * @author 芋道源码 |
| 25 | - * | |
| 26 | - * // TODO 芋艿:待实现 | |
| 27 | 26 | */ |
| 28 | 27 | @Component |
| 29 | 28 | @Slf4j |
| 30 | 29 | public class SubscribeHandler implements WxMpMessageHandler { |
| 31 | 30 | |
| 32 | 31 | @Resource |
| 33 | - @Lazy // 延迟加载,解决循环依赖的问题 | |
| 34 | - private MpAccountService mpAccountService; | |
| 35 | - | |
| 36 | - @Resource | |
| 37 | 32 | private MpUserService mpUserService; |
| 33 | + @Resource | |
| 34 | + private MpAutoReplyService mpAutoReplyService; | |
| 38 | 35 | |
| 39 | 36 | @Override |
| 40 | 37 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, |
| ... | ... | @@ -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 | 19 | */ |
| 20 | 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 | 1 | package cn.iocoder.yudao.module.mp.service.message; |
| 2 | 2 | |
| 3 | 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 | 6 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO; |
| 6 | 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 | 10 | import me.chanjar.weixin.common.api.WxConsts; |
| 9 | 11 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
| 10 | 12 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
| 13 | +import org.springframework.context.annotation.Lazy; | |
| 11 | 14 | import org.springframework.stereotype.Service; |
| 12 | 15 | import org.springframework.validation.annotation.Validated; |
| 13 | 16 | |
| ... | ... | @@ -25,15 +28,15 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService { |
| 25 | 28 | |
| 26 | 29 | @Resource |
| 27 | 30 | private MpMessageService mpMessageService; |
| 31 | + @Resource | |
| 32 | + @Lazy // 延迟加载,避免循环依赖 | |
| 33 | + private MpAccountService mpAccountService; | |
| 28 | 34 | |
| 29 | 35 | @Resource |
| 30 | 36 | private MpAutoReplyMapper mpAutoReplyMapper; |
| 31 | 37 | |
| 32 | 38 | @Override |
| 33 | 39 | public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) { |
| 34 | -// if (true) { | |
| 35 | -// return new TextBuilder().build("nihao", wxMessage, null); | |
| 36 | -// } | |
| 37 | 40 | // 第一步,匹配自动回复 |
| 38 | 41 | List<MpAutoReplyDO> replies = null; |
| 39 | 42 | // 1.1 关键字 |
| ... | ... | @@ -58,4 +61,24 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService { |
| 58 | 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 | 89 | } |
| 90 | 90 | mpMessageMapper.insert(message); |
| 91 | 91 | |
| 92 | -// WxConsts.MenuButtonType.VIEW | |
| 92 | +// WxConsts.MenuButtonType.VIEW TODO 芋艿:待测试 | |
| 93 | 93 | // wxMessage.getEventKey() |
| 94 | 94 | |
| 95 | 95 | // WxConsts.MenuButtonType.CLICK | ... | ... |