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

This commit is contained in:
YunaiV 2023-01-05 19:36:13 +08:00
parent 44f0bcd182
commit 69e653cd94
8 changed files with 81 additions and 55 deletions

View File

@ -34,4 +34,11 @@ public interface MpAutoReplyMapper extends BaseMapperX<MpAutoReplyDO> {
.eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.MESSAGE.getType()) .eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.MESSAGE.getType())
.eq(MpAutoReplyDO::getRequestMessageType, requestMessageType)); .eq(MpAutoReplyDO::getRequestMessageType, requestMessageType));
} }
default List<MpAutoReplyDO> selectListByAppIdAndSubscribe(String appId) {
return selectList(new LambdaQueryWrapperX<MpAutoReplyDO>()
.eq(MpAutoReplyDO::getAppId, appId)
.eq(MpAutoReplyDO::getType, MpAutoReplyTypeEnum.SUBSCRIBE.getType()));
}
} }

View File

@ -36,7 +36,7 @@ public class MpConfiguration {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps, public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps,
WxMpProperties wxMpProperties, WxMpProperties wxMpProperties,
MessageReceiveHandler logHandler, MessageReceiveHandler messageReceiveHandler,
KfSessionHandler kfSessionHandler, KfSessionHandler kfSessionHandler,
StoreCheckNotifyHandler storeCheckNotifyHandler, StoreCheckNotifyHandler storeCheckNotifyHandler,
MenuHandler menuHandler, MenuHandler menuHandler,
@ -45,10 +45,10 @@ public class MpConfiguration {
UnsubscribeHandler unsubscribeHandler, UnsubscribeHandler unsubscribeHandler,
LocationHandler locationHandler, LocationHandler locationHandler,
ScanHandler scanHandler, ScanHandler scanHandler,
MessageAutoReplyHandler msgHandler) { MessageAutoReplyHandler messageAutoReplyHandler) {
return new DefaultMpServiceFactory(redisTemplateWxRedisOps, wxMpProperties, return new DefaultMpServiceFactory(redisTemplateWxRedisOps, wxMpProperties,
logHandler, kfSessionHandler, storeCheckNotifyHandler, menuHandler, messageReceiveHandler, kfSessionHandler, storeCheckNotifyHandler, menuHandler,
nullHandler, subscribeHandler, unsubscribeHandler, locationHandler, scanHandler, msgHandler); nullHandler, subscribeHandler, unsubscribeHandler, locationHandler, scanHandler, messageAutoReplyHandler);
} }
} }

View File

@ -49,7 +49,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
// ========== 各种 Handler ========== // ========== 各种 Handler ==========
private final MessageReceiveHandler logHandler; private final MessageReceiveHandler messageReceiveHandler;
private final KfSessionHandler kfSessionHandler; private final KfSessionHandler kfSessionHandler;
private final StoreCheckNotifyHandler storeCheckNotifyHandler; private final StoreCheckNotifyHandler storeCheckNotifyHandler;
private final MenuHandler menuHandler; private final MenuHandler menuHandler;
@ -58,7 +58,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
private final UnsubscribeHandler unsubscribeHandler; private final UnsubscribeHandler unsubscribeHandler;
private final LocationHandler locationHandler; private final LocationHandler locationHandler;
private final ScanHandler scanHandler; private final ScanHandler scanHandler;
private final MessageAutoReplyHandler msgHandler; private final MessageAutoReplyHandler messageAutoReplyHandler;
@Override @Override
public void init(List<MpAccountDO> list) { public void init(List<MpAccountDO> list) {
@ -108,7 +108,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) { private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) {
WxMpMessageRouter router = new WxMpMessageRouter(mpService); WxMpMessageRouter router = new WxMpMessageRouter(mpService);
// 记录所有事件的日志异步执行 // 记录所有事件的日志异步执行
router.rule().handler(logHandler).next(); router.rule().handler(messageReceiveHandler).next();
// 接收客服会话管理事件 // 接收客服会话管理事件
router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
@ -159,7 +159,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
.event(WxConsts.EventType.SCAN).handler(scanHandler).end(); .event(WxConsts.EventType.SCAN).handler(scanHandler).end();
// 默认 // 默认
router.rule().async(false).handler(msgHandler).end(); router.rule().async(false).handler(messageAutoReplyHandler).end();
return router; return router;
} }

View File

@ -1,6 +1,11 @@
package cn.iocoder.yudao.module.mp.service.handler.user; 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 lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler; import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService; 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 me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map; import java.util.Map;
/** /**
* 上报地理位置的事件处理器 * 上报地理位置的事件处理器
* *
* // TODO @芋艿需要实现一下~ * 触发操作打开微信公众号 -> 点击 + -> 选择语音
*
* 逻辑用户上传地理位置时也可以触发自动回复
*
* @author 芋道源码
*/ */
@Component @Component
@Slf4j @Slf4j
public class LocationHandler implements WxMpMessageHandler { public class LocationHandler implements WxMpMessageHandler {
@Resource
private MpAutoReplyService mpAutoReplyService;
@Override @Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) { WxMpService wxMpService, WxSessionManager sessionManager) {
// if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) { // 防御性编程必须是 LOCATION 消息
// //TODO 接收处理用户发送的地理位置消息 if (ObjectUtil.notEqual(wxMessage.getMsgType(), WxConsts.XmlMsgType.LOCATION)) {
// 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()));
//TODO 可以将用户地理位置信息保存到本地数据库以便以后使用
return null; return null;
} }
log.info("[handle][上报地理位置,纬度({})、经度({})、精度({})", wxMessage.getLatitude(),
wxMessage.getLongitude(), wxMessage.getPrecision());
// 自动回复
return mpAutoReplyService.replyForMessage(MpContextHolder.getAppId(), wxMessage);
}
} }

View File

@ -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.dal.dataobject.user.MpUserDO;
import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder; 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.account.MpAccountService;
import cn.iocoder.yudao.module.mp.service.message.MpAutoReplyService;
import cn.iocoder.yudao.module.mp.service.user.MpUserService; import cn.iocoder.yudao.module.mp.service.user.MpUserService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.error.WxErrorException;
@ -22,19 +23,15 @@ import java.util.Map;
* 关注的事件处理器 * 关注的事件处理器
* *
* @author 芋道源码 * @author 芋道源码
*
* // TODO 芋艿待实现
*/ */
@Component @Component
@Slf4j @Slf4j
public class SubscribeHandler implements WxMpMessageHandler { public class SubscribeHandler implements WxMpMessageHandler {
@Resource
@Lazy // 延迟加载解决循环依赖的问题
private MpAccountService mpAccountService;
@Resource @Resource
private MpUserService mpUserService; private MpUserService mpUserService;
@Resource
private MpAutoReplyService mpAutoReplyService;
@Override @Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
@ -49,24 +46,10 @@ public class SubscribeHandler implements WxMpMessageHandler {
} }
// 第二步保存用户信息 // 第二步保存用户信息
MpUserDO mpUser = null; mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser);
if (wxMpUser != null) {
mpUser = mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser);
}
// 第三步回复关注的欢迎语 TODO 芋艿关注的欢迎语 // 第三步回复关注的欢迎语
// return new TextBuilder().build("感谢关注", wxMessage, weixinService); return mpAutoReplyService.replyForSubscribe(MpContextHolder.getAppId(), wxMessage);
return null;
}
/**
* 处理特殊请求比如如果是扫码进来的可以做相应处理
*/
private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)
throws Exception {
//TODO
return null;
} }
} }

View File

@ -19,4 +19,13 @@ public interface MpAutoReplyService {
*/ */
WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage); WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage);
/**
* 当用户关注时自动回复
*
* @param appId 微信公众号 appId
* @param wxMessage 消息
* @return 回复的消息
*/
WxMpXmlOutMessage replyForSubscribe(String appId, WxMpXmlMessage wxMessage);
} }

View File

@ -1,13 +1,16 @@
package cn.iocoder.yudao.module.mp.service.message; package cn.iocoder.yudao.module.mp.service.message;
import cn.hutool.core.collection.CollUtil; 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.dataobject.message.MpAutoReplyDO;
import cn.iocoder.yudao.module.mp.dal.mysql.message.MpAutoReplyMapper; 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.common.api.WxConsts;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -25,15 +28,15 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
@Resource @Resource
private MpMessageService mpMessageService; private MpMessageService mpMessageService;
@Resource
@Lazy // 延迟加载避免循环依赖
private MpAccountService mpAccountService;
@Resource @Resource
private MpAutoReplyMapper mpAutoReplyMapper; private MpAutoReplyMapper mpAutoReplyMapper;
@Override @Override
public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) { public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) {
// if (true) {
// return new TextBuilder().build("nihao", wxMessage, null);
// }
// 第一步匹配自动回复 // 第一步匹配自动回复
List<MpAutoReplyDO> replies = null; List<MpAutoReplyDO> replies = null;
// 1.1 关键字 // 1.1 关键字
@ -58,4 +61,24 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply); return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
} }
@Override
public WxMpXmlOutMessage replyForSubscribe(String appId, WxMpXmlMessage wxMessage) {
// 第一步匹配自动回复
List<MpAutoReplyDO> 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("感谢关注");
}
} }

View File

@ -89,7 +89,7 @@ public class MpMessageServiceImpl implements MpMessageService {
} }
mpMessageMapper.insert(message); mpMessageMapper.insert(message);
// WxConsts.MenuButtonType.VIEW // WxConsts.MenuButtonType.VIEW TODO 芋艿待测试
// wxMessage.getEventKey() // wxMessage.getEventKey()
// WxConsts.MenuButtonType.CLICK // WxConsts.MenuButtonType.CLICK