mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 15:21:53 +08:00
mp:实现关键字,自动回复
This commit is contained in:
parent
48bea21a46
commit
44f0bcd182
@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 微信消息自动回复的匹配模式
|
||||
* 公众号消息自动回复的匹配模式
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
|
@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 微信消息自动回复的类型
|
||||
* 公众号消息自动回复的类型
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@ -14,7 +14,7 @@ public enum MpAutoReplyTypeEnum {
|
||||
|
||||
SUBSCRIBE(1, "关注时回复"),
|
||||
MESSAGE(2, "收到消息回复"),
|
||||
KEYWORD(2, "关键词回复"),
|
||||
KEYWORD(3, "关键词回复"),
|
||||
;
|
||||
|
||||
/**
|
||||
|
@ -1,18 +1,27 @@
|
||||
package cn.iocoder.yudao.module.mp.convert.message;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
|
||||
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageRespVO;
|
||||
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.MpMessageDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
|
||||
import me.chanjar.weixin.common.api.WxConsts;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage;
|
||||
import me.chanjar.weixin.mp.builder.outxml.BaseBuilder;
|
||||
import me.chanjar.weixin.mp.builder.outxml.TextBuilder;
|
||||
import me.chanjar.weixin.mp.builder.outxml.VideoBuilder;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface MpMessageConvert {
|
||||
@ -41,4 +50,64 @@ public interface MpMessageConvert {
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
default MpMessageDO convert(MpAutoReplyDO reply, MpAccountDO account, MpUserDO user) {
|
||||
// 构建消息
|
||||
MpMessageDO message = new MpMessageDO();
|
||||
message.setType(reply.getResponseMessageType());
|
||||
// 1. 文本
|
||||
if (StrUtil.equals(reply.getResponseMessageType(), WxConsts.XmlMsgType.TEXT)) {
|
||||
message.setContent(reply.getResponseContent());
|
||||
} else if (ObjectUtils.equalsAny(reply.getResponseMessageType(), WxConsts.XmlMsgType.IMAGE, // 2. 图片
|
||||
WxConsts.XmlMsgType.VOICE)) { // 3. 语音
|
||||
message.setMediaId(reply.getResponseMediaId()).setMediaUrl(reply.getResponseMediaUrl());
|
||||
} else if (StrUtil.equals(reply.getResponseMessageType(), WxConsts.XmlMsgType.VIDEO)) { // 4. 视频
|
||||
message.setMediaId(reply.getResponseMediaId()).setMediaUrl(reply.getResponseMediaUrl())
|
||||
.setTitle(reply.getResponseTitle()).setDescription(reply.getResponseDescription());
|
||||
} else if (StrUtil.equals(reply.getResponseMessageType(), WxConsts.XmlMsgType.NEWS)) { // 5. 图文
|
||||
message.setArticles(Collections.singletonList(reply.getResponseArticle()));
|
||||
} else {
|
||||
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
|
||||
}
|
||||
|
||||
// 其它字段
|
||||
if (account != null) {
|
||||
message.setAccountId(account.getId()).setAppId(account.getAppId());
|
||||
}
|
||||
if (user != null) {
|
||||
message.setUserId(user.getId()).setOpenid(user.getOpenid());
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
default WxMpXmlOutMessage convert02(MpMessageDO message, MpAccountDO account) {
|
||||
BaseBuilder<?, ? extends WxMpXmlOutMessage> messageBuilder;
|
||||
// 个性化字段
|
||||
switch (message.getType()) {
|
||||
case WxConsts.XmlMsgType.TEXT:
|
||||
messageBuilder = WxMpXmlOutMessage.TEXT().content(message.getContent());
|
||||
break;
|
||||
case WxConsts.XmlMsgType.IMAGE:
|
||||
messageBuilder = WxMpXmlOutMessage.IMAGE().mediaId(message.getMediaId());
|
||||
break;
|
||||
case WxConsts.XmlMsgType.VOICE:
|
||||
messageBuilder = WxMpXmlOutMessage.VOICE().mediaId(message.getMediaId());
|
||||
break;
|
||||
case WxConsts.XmlMsgType.VIDEO:
|
||||
messageBuilder = WxMpXmlOutMessage.VIDEO().mediaId(message.getMediaId())
|
||||
.title(message.getTitle()).description(message.getDescription());
|
||||
break;
|
||||
case WxConsts.XmlMsgType.NEWS:
|
||||
messageBuilder = WxMpXmlOutMessage.NEWS().articles(convertList02(message.getArticles()));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
|
||||
}
|
||||
// 通用字段
|
||||
messageBuilder.fromUser(account.getAccount());
|
||||
messageBuilder.toUser(message.getOpenid());
|
||||
return messageBuilder.build();
|
||||
}
|
||||
List<WxMpXmlOutNewsMessage.Item> convertList02(List<MpMessageDO.Article> list);
|
||||
|
||||
}
|
||||
|
@ -6,18 +6,21 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyMatchEnum;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import me.chanjar.weixin.common.api.WxConsts;
|
||||
import me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 微信消息自动回复 DO
|
||||
* 公众号消息自动回复 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@ -99,4 +102,35 @@ public class MpAutoReplyDO extends BaseDO {
|
||||
*/
|
||||
private String responseContent;
|
||||
|
||||
/**
|
||||
* 回复的媒体 id
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||
*/
|
||||
private String responseMediaId;
|
||||
/**
|
||||
* 回复的媒体 URL
|
||||
*/
|
||||
private String responseMediaUrl;
|
||||
|
||||
/**
|
||||
* 回复的标题
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
||||
*/
|
||||
private String responseTitle;
|
||||
/**
|
||||
* 回复的描述
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
||||
*/
|
||||
private String responseDescription;
|
||||
|
||||
/**
|
||||
* 回复的图文消息
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private MpMessageDO.Article responseArticle;
|
||||
}
|
||||
|
@ -1,17 +1,23 @@
|
||||
package cn.iocoder.yudao.module.mp.dal.dataobject.message;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||
import lombok.*;
|
||||
import me.chanjar.weixin.common.api.WxConsts;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 微信消息 DO
|
||||
* 公众号消息 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@ -160,6 +166,14 @@ public class MpMessageDO extends BaseDO {
|
||||
*/
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* 图文消息数组
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
||||
*/
|
||||
@TableField(typeHandler = ArticleTypeHandler.class)
|
||||
private List<Article> articles;
|
||||
|
||||
// ========= 事件推送 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
|
||||
|
||||
/**
|
||||
@ -176,4 +190,45 @@ public class MpMessageDO extends BaseDO {
|
||||
*/
|
||||
private String eventKey;
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*/
|
||||
@Data
|
||||
public static class Article implements Serializable {
|
||||
|
||||
/**
|
||||
* 图文消息标题
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 图文消息描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 图片链接
|
||||
*
|
||||
* 支持JPG、PNG格式,较好的效果为大图 360*200,小图 200*200
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 点击图文消息跳转链接
|
||||
*/
|
||||
private String url;
|
||||
|
||||
}
|
||||
|
||||
// TODO @芋艿:可以找一些新的思路
|
||||
public static class ArticleTypeHandler extends AbstractJsonTypeHandler<List<Article>> {
|
||||
|
||||
@Override
|
||||
protected List<Article> parse(String json) {
|
||||
return JsonUtils.parseArray(json, Article.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJson(List<Article> obj) {
|
||||
return JsonUtils.toJsonString(obj);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyMatchEnum;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpAutoReplyTypeEnum;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface MpAutoReplyMapper extends BaseMapperX<MpAutoReplyDO> {
|
||||
|
||||
default List<MpAutoReplyDO> selectListByAppIdAndKeywordAll(String appId, String requestKeyword) {
|
||||
|
@ -1,12 +1,14 @@
|
||||
package cn.iocoder.yudao.module.mp.service.message;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.module.mp.builder.TextBuilder;
|
||||
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 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.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -17,18 +19,21 @@ import java.util.List;
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Resource
|
||||
@Service
|
||||
@Validated
|
||||
public class MpAutoReplyServiceImpl implements MpAutoReplyService {
|
||||
|
||||
@Resource
|
||||
private MpAutoReplyService mpAutoReplyService;
|
||||
private MpMessageService mpMessageService;
|
||||
|
||||
@Resource
|
||||
private MpAutoReplyMapper mpAutoReplyMapper;
|
||||
|
||||
@Override
|
||||
public WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage) {
|
||||
// if (true) {
|
||||
// return new TextBuilder().build("nihao", wxMessage, null);
|
||||
// }
|
||||
// 第一步,匹配自动回复
|
||||
List<MpAutoReplyDO> replies = null;
|
||||
// 1.1 关键字
|
||||
@ -47,12 +52,10 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
|
||||
if (CollUtil.isEmpty(replies)) {
|
||||
return null;
|
||||
}
|
||||
MpAutoReplyDO reply = CollUtil.getFirst(replies);
|
||||
|
||||
// 第二步,基于自动回复,创建消息
|
||||
|
||||
|
||||
// 第三步,将消息转换成 WxMpXmlOutMessage 返回
|
||||
return null;
|
||||
return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.mp.service.message;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||
|
||||
/**
|
||||
* 粉丝消息表 Service 接口
|
||||
@ -28,4 +30,14 @@ public interface MpMessageService {
|
||||
* @param wxMessage 消息
|
||||
*/
|
||||
void createFromUser(String appId, WxMpXmlMessage wxMessage);
|
||||
|
||||
/**
|
||||
* 创建粉丝消息,通过自动回复
|
||||
*
|
||||
* @param openid 公众号粉丝 openid
|
||||
* @param reply 自动回复
|
||||
* @return 微信回复消息 XML
|
||||
*/
|
||||
WxMpXmlOutMessage createFromAutoReply(String openid, MpAutoReplyDO reply);
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.io.FileUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
import cn.iocoder.yudao.module.mp.convert.message.MpMessageConvert;
|
||||
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.MpMessageDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum;
|
||||
@ -20,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
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;
|
||||
|
||||
@ -74,7 +76,7 @@ public class MpMessageServiceImpl implements MpMessageService {
|
||||
MpAccountDO account = mpAccountService.getAccountFromCache(appId);
|
||||
Assert.notNull(account, "公众号账号({}) 不存在", appId);
|
||||
MpUserDO user = mpUserService.getUser(appId, wxMessage.getFromUser());
|
||||
Assert.notNull(account, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser());
|
||||
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser());
|
||||
|
||||
// 记录消息
|
||||
MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user);
|
||||
@ -94,6 +96,23 @@ public class MpMessageServiceImpl implements MpMessageService {
|
||||
// wxMessage.getEventKey()
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxMpXmlOutMessage createFromAutoReply(String openid, MpAutoReplyDO reply) {
|
||||
// 获得关联信息
|
||||
MpAccountDO account = mpAccountService.getAccountFromCache(reply.getAppId());
|
||||
Assert.notNull(account, "公众号账号({}) 不存在", reply.getAppId());
|
||||
MpUserDO user = mpUserService.getUser(reply.getAppId(), openid);
|
||||
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", reply.getAppId(), openid);
|
||||
|
||||
// 记录消息
|
||||
MpMessageDO message = MpMessageConvert.INSTANCE.convert(reply, account, user);
|
||||
message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
|
||||
mpMessageMapper.insert(message);
|
||||
|
||||
// 转换返回 WxMpXmlOutMessage 对象
|
||||
return MpMessageConvert.INSTANCE.convert02(message, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载微信媒体文件的内容,并上传到文件服务
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user