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