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
fefad029ad
commit
c141ebef3f
@ -10,7 +10,7 @@ import lombok.Getter;
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MpMessageSendFrom {
|
||||
public enum MpMessageSendFromEnum {
|
||||
|
||||
USER_TO_MP(1, "用户发送给公众号"),
|
||||
MP_TO_USER(2, "公众号发给用户"),
|
@ -4,8 +4,13 @@ import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
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.*;
|
||||
|
||||
@ -20,4 +25,19 @@ public interface MpMessageConvert {
|
||||
|
||||
PageResult<MpMessageRespVO> convertPage(PageResult<MpMessageDO> page);
|
||||
|
||||
@Mappings(value = {
|
||||
@Mapping(source = "msgType", target = "type"),
|
||||
})
|
||||
MpMessageDO convert(WxMpXmlMessage wxMessage);
|
||||
|
||||
default MpMessageDO convert(WxMpXmlMessage wxMessage, MpAccountDO account, MpUserDO user) {
|
||||
MpMessageDO message = convert(wxMessage);
|
||||
if (account != null) {
|
||||
message.setAccountId(account.getId()).setAppId(account.getAppId());
|
||||
}
|
||||
if (user != null) {
|
||||
message.setUserId(user.getId()).setOpenid(user.getOpenid());
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,15 @@
|
||||
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.MpMessageSendFrom;
|
||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import me.chanjar.weixin.common.api.WxConsts;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 微信消息 DO
|
||||
*
|
||||
@ -35,6 +30,10 @@ public class MpMessageDO extends BaseDO {
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 微信公众号消息 id
|
||||
*/
|
||||
private Long msgId;
|
||||
/**
|
||||
* 微信公众号 ID
|
||||
*
|
||||
@ -56,17 +55,9 @@ public class MpMessageDO extends BaseDO {
|
||||
/**
|
||||
* 用户标识
|
||||
*
|
||||
* 冗余 {@link MpUserDO#getId()}
|
||||
* 冗余 {@link MpUserDO#getOpenid()}
|
||||
*/
|
||||
private String openId;
|
||||
// /**
|
||||
// * 昵称
|
||||
// */
|
||||
// private String nickname;
|
||||
// /**
|
||||
// * 头像
|
||||
// */
|
||||
// private String headImageUrl;
|
||||
private String openid;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
@ -77,11 +68,11 @@ public class MpMessageDO extends BaseDO {
|
||||
/**
|
||||
* 消息来源
|
||||
*
|
||||
* 枚举 {@link MpMessageSendFrom}
|
||||
* 枚举 {@link MpMessageSendFromEnum}
|
||||
*/
|
||||
private Integer sendFrom;
|
||||
|
||||
// ========= 消息内容 ==========
|
||||
// ========= 普通消息内容 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
@ -96,10 +87,26 @@ public class MpMessageDO extends BaseDO {
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||
*/
|
||||
private String mediaId;
|
||||
/**
|
||||
* 媒体文件的 URL
|
||||
*/
|
||||
private String mediaUrl;
|
||||
/**
|
||||
* 语音识别后文本
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VOICE
|
||||
*/
|
||||
private String recognition;
|
||||
/**
|
||||
* 语音格式,如 amr,speex 等
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VOICE
|
||||
*/
|
||||
private String format;
|
||||
/**
|
||||
* 标题
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC、LINK
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
@ -109,38 +116,25 @@ public class MpMessageDO extends BaseDO {
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 音乐链接
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
|
||||
*/
|
||||
private String musicURL;
|
||||
/**
|
||||
* 高质量音乐链接,WIFI 环境优先使用该链接播放音乐
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
|
||||
*/
|
||||
private String hqMusicUrl;
|
||||
/**
|
||||
* 缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
||||
*/
|
||||
private String thumbMediaId;
|
||||
/**
|
||||
* 缩略图的媒体 URL
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
||||
*/
|
||||
private String thumbMediaUrl;
|
||||
|
||||
/**
|
||||
* 图文消息个数,限制为 10 条以内
|
||||
* 点击图文消息跳转链接
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 LINK
|
||||
*/
|
||||
private Integer articleCount;
|
||||
/**
|
||||
* 图文消息数组
|
||||
*
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
||||
*/
|
||||
@TableField(typeHandler = ArticleTypeHandler.class)
|
||||
private List<Article> articles;
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 地理位置维度
|
||||
@ -160,47 +154,29 @@ public class MpMessageDO extends BaseDO {
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION
|
||||
*/
|
||||
private Double scale;
|
||||
|
||||
/**
|
||||
* 文章
|
||||
*/
|
||||
@Data
|
||||
public static class Article implements Serializable {
|
||||
|
||||
/**
|
||||
* 图文消息标题
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 图文消息描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 图片链接
|
||||
* 详细地址
|
||||
*
|
||||
* 支持JPG、PNG格式,较好的效果为大图 360*200,小图 200*200
|
||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION
|
||||
*
|
||||
* 例如说杨浦区黄兴路 221-4 号临
|
||||
*/
|
||||
private String picUrl;
|
||||
private String label;
|
||||
|
||||
// ========= 事件推送 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
|
||||
|
||||
/**
|
||||
* 点击图文消息跳转链接
|
||||
* 事件类型
|
||||
*
|
||||
* 枚举 {@link WxConsts.EventType}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
private String event;
|
||||
/**
|
||||
* 事件 Key
|
||||
*
|
||||
* 1. {@link WxConsts.EventType} 的 SCAN:qrscene_ 为前缀,后面为二维码的参数值
|
||||
* 2. {@link WxConsts.EventType} 的 CLICK:与自定义菜单接口中 KEY 值对应
|
||||
*/
|
||||
private String eventKey;
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.module.mp.dal.mysql.fansmsg;
|
||||
package cn.iocoder.yudao.module.mp.dal.mysql.message;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
@ -13,7 +13,7 @@ public interface MpMessageMapper extends BaseMapperX<MpMessageDO> {
|
||||
default PageResult<MpMessageDO> selectPage(MpMessagePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<MpMessageDO>()
|
||||
.eqIfPresent(MpMessageDO::getAccountId, reqVO.getAccountId())
|
||||
.eqIfPresent(MpMessageDO::getOpenId, reqVO.getOpenId())
|
||||
.eqIfPresent(MpMessageDO::getOpenid, reqVO.getOpenId())
|
||||
// .likeIfPresent(MpMessageDO::getNickname, reqVO.getNickname())
|
||||
.eqIfPresent(MpMessageDO::getType, reqVO.getType())
|
||||
.orderByDesc(MpMessageDO::getId));
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.module.mp.dal.mysql.accountfans;
|
||||
package cn.iocoder.yudao.module.mp.dal.mysql.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.mp.framework.mp.config;
|
||||
import cn.iocoder.yudao.module.mp.framework.mp.core.DefaultMpServiceFactory;
|
||||
import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.menu.MenuHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.message.MessageLogHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.message.MessageReceiveHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.message.MessageAutoReplyHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.other.KfSessionHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.other.NullHandler;
|
||||
@ -36,7 +36,7 @@ public class MpConfiguration {
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps,
|
||||
WxMpProperties wxMpProperties,
|
||||
MessageLogHandler logHandler,
|
||||
MessageReceiveHandler logHandler,
|
||||
KfSessionHandler kfSessionHandler,
|
||||
StoreCheckNotifyHandler storeCheckNotifyHandler,
|
||||
MenuHandler menuHandler,
|
||||
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.mp.framework.mp.core;
|
||||
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.menu.MenuHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.message.MessageLogHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.message.MessageReceiveHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.message.MessageAutoReplyHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.other.KfSessionHandler;
|
||||
import cn.iocoder.yudao.module.mp.service.handler.other.NullHandler;
|
||||
@ -49,7 +49,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
|
||||
|
||||
// ========== 各种 Handler ==========
|
||||
|
||||
private final MessageLogHandler logHandler;
|
||||
private final MessageReceiveHandler logHandler;
|
||||
private final KfSessionHandler kfSessionHandler;
|
||||
private final StoreCheckNotifyHandler storeCheckNotifyHandler;
|
||||
private final MenuHandler menuHandler;
|
||||
|
@ -52,91 +52,91 @@ public class MessageAutoReplyHandler implements WxMpMessageHandler {
|
||||
log.info("收到信息内容:{}", JsonUtils.toJsonString(wxMessage));
|
||||
log.info("关键字:{}", wxMessage.getContent());
|
||||
|
||||
if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) {
|
||||
//可以选择将消息保存到本地
|
||||
|
||||
// 获取微信用户基本信息
|
||||
try {
|
||||
WxMpUser wxmpUser = weixinService.getUserService()
|
||||
.userInfo(wxMessage.getFromUser(), null);
|
||||
if (wxmpUser != null) {
|
||||
MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser());
|
||||
if (wxAccount != null) {
|
||||
|
||||
if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) {
|
||||
WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO();
|
||||
wxFansMsg.setOpenid(wxmpUser.getOpenId());
|
||||
try {
|
||||
wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl());
|
||||
wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId()));
|
||||
wxFansMsg.setMsgType(wxMessage.getMsgType());
|
||||
wxFansMsg.setContent(wxMessage.getContent());
|
||||
wxFansMsg.setIsRes("1");
|
||||
|
||||
//组装回复消息
|
||||
String content = processContent(wxMessage);
|
||||
content = HtmlUtil.escape(content);
|
||||
wxFansMsg.setResContent(content);
|
||||
|
||||
mpMessageService.createWxFansMsg(wxFansMsg);
|
||||
return new TextBuilder().build(content, wxMessage, weixinService);
|
||||
|
||||
}
|
||||
if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.IMAGE)) {
|
||||
WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO();
|
||||
wxFansMsg.setOpenid(wxmpUser.getOpenId());
|
||||
try {
|
||||
wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl());
|
||||
wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId()));
|
||||
wxFansMsg.setMsgType(wxMessage.getMsgType());
|
||||
wxFansMsg.setMediaId(wxMessage.getMediaId());
|
||||
wxFansMsg.setPicUrl(wxMessage.getPicUrl());
|
||||
String downloadDirStr = fileApi.createFile(HttpUtil.downloadBytes(wxMessage.getPicUrl()));
|
||||
File downloadDir = new File(downloadDirStr);
|
||||
if (!downloadDir.exists()) {
|
||||
downloadDir.mkdirs();
|
||||
}
|
||||
String filepath = downloadDirStr + System.currentTimeMillis() + ".png";
|
||||
//微信pic url下载到本地,防止失效
|
||||
long size = HttpUtil.downloadFile(wxMessage.getPicUrl(), FileUtil.file(filepath));
|
||||
log.info("download pic size : {}", size);
|
||||
wxFansMsg.setPicPath(filepath);
|
||||
wxFansMsg.setIsRes("0");
|
||||
mpMessageService.createWxFansMsg(wxFansMsg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (WxErrorException e) {
|
||||
if (e.getError().getErrorCode() == 48001) {
|
||||
log.info("该公众号没有获取用户信息权限!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服
|
||||
try {
|
||||
if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服")
|
||||
&& weixinService.getKefuService().kfOnlineList()
|
||||
.getKfOnlineList().size() > 0) {
|
||||
return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE()
|
||||
.fromUser(wxMessage.getToUser())
|
||||
.toUser(wxMessage.getFromUser()).build();
|
||||
}
|
||||
} catch (WxErrorException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) {
|
||||
// //可以选择将消息保存到本地
|
||||
//
|
||||
// // 获取微信用户基本信息
|
||||
// try {
|
||||
// WxMpUser wxmpUser = weixinService.getUserService()
|
||||
// .userInfo(wxMessage.getFromUser(), null);
|
||||
// if (wxmpUser != null) {
|
||||
// MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser());
|
||||
// if (wxAccount != null) {
|
||||
//
|
||||
// if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) {
|
||||
// WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO();
|
||||
// wxFansMsg.setOpenid(wxmpUser.getOpenId());
|
||||
// try {
|
||||
// wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8"));
|
||||
// } catch (UnsupportedEncodingException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl());
|
||||
// wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId()));
|
||||
// wxFansMsg.setMsgType(wxMessage.getMsgType());
|
||||
// wxFansMsg.setContent(wxMessage.getContent());
|
||||
// wxFansMsg.setIsRes("1");
|
||||
//
|
||||
// //组装回复消息
|
||||
// String content = processContent(wxMessage);
|
||||
// content = HtmlUtil.escape(content);
|
||||
// wxFansMsg.setResContent(content);
|
||||
//
|
||||
// mpMessageService.createWxFansMsg(wxFansMsg);
|
||||
// return new TextBuilder().build(content, wxMessage, weixinService);
|
||||
//
|
||||
// }
|
||||
// if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.IMAGE)) {
|
||||
// WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO();
|
||||
// wxFansMsg.setOpenid(wxmpUser.getOpenId());
|
||||
// try {
|
||||
// wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8"));
|
||||
// } catch (UnsupportedEncodingException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl());
|
||||
// wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId()));
|
||||
// wxFansMsg.setMsgType(wxMessage.getMsgType());
|
||||
// wxFansMsg.setMediaId(wxMessage.getMediaId());
|
||||
// wxFansMsg.setPicUrl(wxMessage.getPicUrl());
|
||||
// String downloadDirStr = fileApi.createFile(HttpUtil.downloadBytes(wxMessage.getPicUrl()));
|
||||
// File downloadDir = new File(downloadDirStr);
|
||||
// if (!downloadDir.exists()) {
|
||||
// downloadDir.mkdirs();
|
||||
// }
|
||||
// String filepath = downloadDirStr + System.currentTimeMillis() + ".png";
|
||||
// //微信pic url下载到本地,防止失效
|
||||
// long size = HttpUtil.downloadFile(wxMessage.getPicUrl(), FileUtil.file(filepath));
|
||||
// log.info("download pic size : {}", size);
|
||||
// wxFansMsg.setPicPath(filepath);
|
||||
// wxFansMsg.setIsRes("0");
|
||||
// mpMessageService.createWxFansMsg(wxFansMsg);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// } catch (WxErrorException e) {
|
||||
// if (e.getError().getErrorCode() == 48001) {
|
||||
// log.info("该公众号没有获取用户信息权限!");
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服
|
||||
// try {
|
||||
// if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服")
|
||||
// && weixinService.getKefuService().kfOnlineList()
|
||||
// .getKfOnlineList().size() > 0) {
|
||||
// return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE()
|
||||
// .fromUser(wxMessage.getToUser())
|
||||
// .toUser(wxMessage.getFromUser()).build();
|
||||
// }
|
||||
// } catch (WxErrorException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
//组装默认回复消息
|
||||
return new TextBuilder().build("测试", wxMessage, weixinService);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.mp.service.handler.message;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
|
||||
import cn.iocoder.yudao.module.mp.service.message.MpMessageService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
|
||||
@ -9,21 +10,26 @@ 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 MessageLogHandler implements WxMpMessageHandler {
|
||||
public class MessageReceiveHandler implements WxMpMessageHandler {
|
||||
|
||||
@Resource
|
||||
private MpMessageService mpMessageService;
|
||||
|
||||
@Override
|
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
|
||||
WxMpService wxMpService, WxSessionManager sessionManager) {
|
||||
log.info("接收到请求消息,内容:{}", JsonUtils.toJsonString(wxMessage));
|
||||
log.info("[handle][接收到请求消息,内容:{}]", wxMessage);
|
||||
mpMessageService.createFromUser(MpContextHolder.getAppId(), wxMessage);
|
||||
return null;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ 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.MpMessageDO;
|
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
|
||||
|
||||
/**
|
||||
* 粉丝消息表 Service 接口
|
||||
@ -19,4 +20,11 @@ public interface MpMessageService {
|
||||
*/
|
||||
PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 保存粉丝消息,来自用户发送
|
||||
*
|
||||
* @param appId 微信公众号 appId
|
||||
* @param wxMessage 消息
|
||||
*/
|
||||
void createFromUser(String appId, WxMpXmlMessage wxMessage);
|
||||
}
|
||||
|
@ -1,6 +1,26 @@
|
||||
package cn.iocoder.yudao.module.mp.service.message;
|
||||
|
||||
import cn.hutool.core.io.FileTypeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.MpMessageDO;
|
||||
import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
|
||||
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum;
|
||||
import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
|
||||
import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
|
||||
import cn.iocoder.yudao.module.mp.service.user.MpUserService;
|
||||
import lombok.SneakyThrows;
|
||||
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 org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -10,7 +30,9 @@ import org.springframework.validation.annotation.Validated;
|
||||
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import cn.iocoder.yudao.module.mp.dal.mysql.fansmsg.MpMessageMapper;
|
||||
import cn.iocoder.yudao.module.mp.dal.mysql.message.MpMessageMapper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
|
||||
@ -21,14 +43,77 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class MpMessageServiceImpl implements MpMessageService {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private MpAccountService mpAccountService;
|
||||
@Resource
|
||||
private MpUserService mpUserService;
|
||||
|
||||
@Resource
|
||||
private MpMessageMapper mpMessageMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,解决循环依赖的问题
|
||||
private MpServiceFactory mpServiceFactory;
|
||||
|
||||
@Resource
|
||||
private FileApi fileApi;
|
||||
|
||||
@Override
|
||||
public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) {
|
||||
return mpMessageMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createFromUser(String appId, WxMpXmlMessage wxMessage) {
|
||||
WxMpService mpService = mpServiceFactory.getRequiredMpService(appId);
|
||||
// 获得关联信息
|
||||
MpAccountDO account = mpAccountService.getAccountFromCache(appId);
|
||||
Assert.notNull(account, "公众号账号({}) 不存在", appId);
|
||||
MpUserDO user = mpUserService.getUser(appId, wxMessage.getFromUser());
|
||||
Assert.notNull(account, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser());
|
||||
|
||||
// 记录消息
|
||||
MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user);
|
||||
message.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom());
|
||||
if (StrUtil.isNotEmpty(message.getMediaId())) {
|
||||
message.setMediaUrl(mediaDownload(mpService, message.getMediaId()));
|
||||
}
|
||||
if (StrUtil.isNotEmpty(message.getThumbMediaId())) {
|
||||
message.setThumbMediaUrl(mediaDownload(mpService, message.getThumbMediaId()));
|
||||
}
|
||||
mpMessageMapper.insert(message);
|
||||
|
||||
// WxConsts.MenuButtonType.VIEW
|
||||
// wxMessage.getEventKey()
|
||||
|
||||
// WxConsts.MenuButtonType.CLICK
|
||||
// wxMessage.getEventKey()
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载微信媒体文件的内容,并上传到文件服务
|
||||
*
|
||||
* 为什么要下载?媒体文件在微信后台保存时间为 3 天,即 3 天后 media_id 失效。
|
||||
*
|
||||
* @param mpService 微信公众号 Service
|
||||
* @param mediaId 媒体文件编号
|
||||
* @return 上传后的 URL
|
||||
*/
|
||||
private String mediaDownload(WxMpService mpService, String mediaId) {
|
||||
try {
|
||||
// 第一步,从公众号下载媒体文件
|
||||
File file = mpService.getMaterialService().mediaDownload(mediaId);
|
||||
// 第二步,上传到文件服务
|
||||
String path = mediaId + "." + FileTypeUtil.getType(file);
|
||||
return fileApi.createFile(path, FileUtil.readBytes(file));
|
||||
} catch (WxErrorException e) {
|
||||
log.error("[mediaDownload][media({}) 下载失败]", mediaId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,15 @@ public interface MpUserService {
|
||||
*/
|
||||
MpUserDO getUser(Long id);
|
||||
|
||||
/**
|
||||
* 使用 appId + openId,获得微信公众号粉丝
|
||||
*
|
||||
* @param appId 微信公众号 appId
|
||||
* @param openId 微信公众号 openId
|
||||
* @return 微信公众号粉丝
|
||||
*/
|
||||
MpUserDO getUser(String appId, String openId);
|
||||
|
||||
/**
|
||||
* 获得微信公众号粉丝列表
|
||||
*
|
||||
|
@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.user.vo.MpUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.mp.convert.user.MpUserConvert;
|
||||
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.dal.mysql.accountfans.MpUserMapper;
|
||||
import cn.iocoder.yudao.module.mp.dal.mysql.user.MpUserMapper;
|
||||
import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpUser;
|
||||
@ -41,6 +41,11 @@ public class MpUserServiceImpl implements MpUserService {
|
||||
return mpUserMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MpUserDO getUser(String appId, String openId) {
|
||||
return mpUserMapper.selectByAppIdAndOpenid(appId, openId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MpUserDO> getUserList(Collection<Long> ids) {
|
||||
return mpUserMapper.selectBatchIds(ids);
|
||||
|
Loading…
Reference in New Issue
Block a user