mp:重构 message 发送消息的方法

pull/2/head
YunaiV 2023-01-06 09:25:19 +08:00
parent 47f071560f
commit 665f4b2b09
15 changed files with 453 additions and 84 deletions

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.mp.convert.message;
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpAutoReplyDO;
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface MpAutoReplyConvert {
MpAutoReplyConvert INSTANCE = Mappers.getMapper(MpAutoReplyConvert.class);
@Mappings({
@Mapping(source = "reply.appId", target = "appId"),
@Mapping(source = "reply.responseMessageType", target = "type"),
@Mapping(source = "reply.responseContent", target = "content"),
@Mapping(source = "reply.responseMediaId", target = "mediaId"),
@Mapping(source = "reply.responseMediaUrl", target = "mediaUrl"),
@Mapping(source = "reply.responseTitle", target = "title"),
@Mapping(source = "reply.responseDescription", target = "description"),
@Mapping(source = "reply.responseArticle", target = "article"),
})
MpMessageSendOutReqBO convert(String openid, MpAutoReplyDO reply);
}

View File

@ -1,20 +1,16 @@
package cn.iocoder.yudao.module.mp.convert.message;
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 cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
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;
@ -51,22 +47,26 @@ public interface MpMessageConvert {
return message;
}
default MpMessageDO convert(MpAutoReplyDO reply, MpAccountDO account, MpUserDO user) {
default MpMessageDO convert(MpMessageSendOutReqBO sendReqBO, 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 {
message.setType(sendReqBO.getType());
switch (sendReqBO.getType()) {
case WxConsts.XmlMsgType.TEXT: // 1. 文本
message.setContent(sendReqBO.getContent());
break;
case WxConsts.XmlMsgType.IMAGE: // 2. 图片
case WxConsts.XmlMsgType.VOICE: // 3. 语音
message.setMediaId(sendReqBO.getMediaId()).setMediaUrl(sendReqBO.getMediaUrl());
break;
case WxConsts.XmlMsgType.VIDEO: // 4. 视频
message.setMediaId(sendReqBO.getMediaId()).setMediaUrl(sendReqBO.getMediaUrl())
.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription());
break;
case WxConsts.XmlMsgType.NEWS: // 5. 图文
message.setArticles(Collections.singletonList(sendReqBO.getArticle()));
break;
default:
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
}

View File

@ -1,15 +1,19 @@
package cn.iocoder.yudao.module.mp.dal.dataobject.menu;
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.message.MpMessageDO;
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 com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.bean.menu.WxMenuButton;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
import java.util.List;
@ -48,8 +52,8 @@ public class MpMenuDO extends BaseDO {
/**
*
*/
@TableField(typeHandler = WxMenuButtonTypeHandler.class)
private List<WxMenuButton> buttons;
@TableField(typeHandler = ButtonTypeHandler.class)
private List<Button> buttons;
/**
*
*
@ -58,16 +62,113 @@ public class MpMenuDO extends BaseDO {
*/
private Boolean syncStatus;
/**
*
*/
@Data
public static class Button {
/**
*
*
* {@link MenuButtonType}
*/
private String type;
/**
*
*
* {@link #type} CLICKSCANCODE_WAITMSG
*
* {@link WxConsts.XmlMsgType} TEXTIMAGEVOICEVIDEONEWS
*/
private String messageType;
/**
*
*/
private String name;
/**
*
*/
private String key;
/**
*
*/
private List<Button> subButtons;
/**
*
*
* 1024
*
* {@link WxConsts.XmlMsgType} VIEWMINIPROGRAM
*/
private String url;
/**
* appId
*
* {@link WxConsts.XmlMsgType} MINIPROGRAM
*/
private String appId;
/**
*
*
* {@link WxConsts.XmlMsgType} MINIPROGRAM
*/
private String pagePath;
/**
*
*
* {@link WxConsts.XmlMsgType} TEXT
*/
private String content;
/**
* id
*
* {@link WxConsts.XmlMsgType} IMAGEVOICEVIDEO
*/
private String mediaId;
/**
* URL
*
* {@link WxConsts.XmlMsgType} IMAGEVOICEVIDEO
*/
private String mediaUrl;
/**
*
*
* {@link WxConsts.XmlMsgType} VIDEO
*/
private String title;
/**
*
*
* {@link WxConsts.XmlMsgType} VIDEO
*/
private String description;
/**
*
*
* {@link WxConsts.XmlMsgType} NEWS
*/
private MpMessageDO.Article article;
}
// TODO @芋艿:可以找一些新的思路
public static class WxMenuButtonTypeHandler extends AbstractJsonTypeHandler<List<WxMenuButton>> {
public static class ButtonTypeHandler extends AbstractJsonTypeHandler<List<Button>> {
@Override
protected List<WxMenuButton> parse(String json) {
return JsonUtils.parseArray(json, WxMenuButton.class);
protected List<Button> parse(String json) {
return JsonUtils.parseArray(json, Button.class);
}
@Override
protected String toJson(List<WxMenuButton> obj) {
protected String toJson(List<Button> obj) {
return JsonUtils.toJsonString(obj);
}

View File

@ -16,7 +16,6 @@ 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;
/**

View File

@ -12,7 +12,9 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
import lombok.*;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.builder.kefu.NewsBuilder;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.util.List;
@ -199,20 +201,24 @@ public class MpMessageDO extends BaseDO {
/**
*
*/
@NotEmpty(message = "图文消息标题不能为空", groups = NewsBuilder.class)
private String title;
/**
*
*/
@NotEmpty(message = "图文消息描述不能为空", groups = NewsBuilder.class)
private String description;
/**
*
*
* JPGPNG 360*200 200*200
* JPGPNG 360*200 200*200
*/
@NotEmpty(message = "图片链接不能为空", groups = NewsBuilder.class)
private String picUrl;
/**
*
*/
@NotEmpty(message = "点击图文消息跳转链接不能为空", groups = NewsBuilder.class)
private String url;
}

View File

@ -7,4 +7,8 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MpMenuMapper extends BaseMapperX<MpMenuDO> {
default MpMenuDO selectByAppId(String appId) {
return selectOne(MpMenuDO::getAppId, appId);
}
}

View File

@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.mp.framework.mp.core.util;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import javax.validation.Validator;
/**
*
*
* @author
*/
@Slf4j
public class MpUtils {
/**
* Text Group
*/
public interface TextGroup {}
/**
* Image Group
*/
public interface ImageGroup {}
/**
* Voice Group
*/
public interface VoiceGroup {}
/**
* Video Group
*/
public interface VideoGroup {}
/**
* News Group
*/
public interface NewsGroup {}
/**
*
*
* @param type
* @param message
*/
public static void validateMessage(Validator validator, String type, Object message) {
// 获得对应的校验 group
Class<?> group;
switch (type) {
case WxConsts.XmlMsgType.TEXT:
group = TextGroup.class;
break;
case WxConsts.XmlMsgType.IMAGE:
group = ImageGroup.class;
break;
case WxConsts.XmlMsgType.VOICE:
group = VoiceGroup.class;
break;
case WxConsts.XmlMsgType.VIDEO:
group = VideoGroup.class;
break;
case WxConsts.XmlMsgType.NEWS:
group = NewsGroup.class;
break;
default:
log.error("[validateMessage][未知的消息类型({})]", message);
throw new IllegalArgumentException("不支持的消息类型:" + type);
}
// 执行校验
ValidationUtils.validate(validator, message, group);
}
}

View File

@ -1,12 +1,16 @@
package cn.iocoder.yudao.module.mp.service.handler.menu;
import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
import cn.iocoder.yudao.module.mp.service.menu.MpMenuService;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMenuService;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
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.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
@ -14,24 +18,20 @@ import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
/**
*
*
* // TODO 芋艿:待实现
*
*
* @author
*/
@Component
public class MenuHandler implements WxMpMessageHandler {
@Resource
private MpMenuService mpMenuService;
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) {
String msg = wxMessage.getEventKey();
if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) {
return null;
}
return WxMpXmlOutMessage.TEXT().content(msg)
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
.build();
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService weixinService, WxSessionManager sessionManager) {
return mpMenuService.reply(MpContextHolder.getAppId(), wxMessage.getEventKey(), wxMessage.getFromUser());
}
}

View File

@ -29,7 +29,7 @@ public class MessageReceiveHandler implements WxMpMessageHandler {
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
WxMpService wxMpService, WxSessionManager sessionManager) {
log.info("[handle][接收到请求消息,内容:{}]", wxMessage);
mpMessageService.createFromUser(MpContextHolder.getAppId(), wxMessage);
mpMessageService.receiveMessage(MpContextHolder.getAppId(), wxMessage);
return null;
}

View File

@ -4,6 +4,8 @@ import javax.validation.*;
import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*;
import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
/**
* Service
@ -35,4 +37,14 @@ public interface MpMenuService {
*/
MpMenuDO getMenu(Long id);
/**
*
*
* @param appId AppId
* @param key
* @param openid openid
* @return
*/
WxMpXmlOutMessage reply(String appId, String key, String openid);
}

View File

@ -1,11 +1,18 @@
package cn.iocoder.yudao.module.mp.service.menu;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert;
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -27,6 +34,7 @@ import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
*/
@Service
@Validated
@Slf4j
public class MpMenuServiceImpl implements MpMenuService {
@Resource
@ -77,4 +85,49 @@ public class MpMenuServiceImpl implements MpMenuService {
return mpMenuMapper.selectById(id);
}
@Override
public WxMpXmlOutMessage reply(String appId, String key, String openid) {
// 获得菜单
MpMenuDO menu = mpMenuMapper.selectByAppId(appId);
if (menu == null) {
log.error("[reply][appId({}) 找不到对应的菜单]", appId);
return null;
}
// 匹配对应的按钮
MpMenuDO.Button button = getMenuButton(menu, key);
if (button == null) {
log.error("[reply][appId({}) key({}) 找不到对应的菜单按钮]", appId, key);
return null;
}
// 按钮必须要有消息类型,不然后续无法回复消息
if (StrUtil.isEmpty(button.getMessageType())) {
log.error("[reply][appId({}) key({}) 不存在消息类型({})]", appId, key, button);
return null;
}
// 回复消息
return null;
}
private MpMenuDO.Button getMenuButton(MpMenuDO menu, String key) {
// 先查询子按钮
for (MpMenuDO.Button button : menu.getButtons()) {
if (CollUtil.isEmpty(button.getSubButtons())) {
continue;
}
for (MpMenuDO.Button subButton : button.getSubButtons()) {
if (StrUtil.equals(subButton.getKey(), key)) {
return subButton;
}
}
}
// 再查询父按钮
for (MpMenuDO.Button button : menu.getButtons()) {
if (StrUtil.equals(button.getKey(), key)) {
return button;
}
}
return null;
}
}

View File

@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.mp.service.message;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.mp.convert.message.MpAutoReplyConvert;
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.MpAutoReplyTypeEnum;
import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
@ -58,7 +60,8 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
MpAutoReplyDO reply = CollUtil.getFirst(replies);
// 第二步,基于自动回复,创建消息
return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
MpMessageSendOutReqBO sendReqBO = MpAutoReplyConvert.INSTANCE.convert(wxMessage.getFromUser(), reply);
return mpMessageService.sendOutMessage(sendReqBO);
}
@Override
@ -69,7 +72,8 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
: buildDefaultSubscribeAutoReply(appId); // 如果不存在,提供一个默认末班
// 第二步,基于自动回复,创建消息
return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
MpMessageSendOutReqBO sendReqBO = MpAutoReplyConvert.INSTANCE.convert(wxMessage.getFromUser(), reply);
return mpMessageService.sendOutMessage(sendReqBO);
}
private MpAutoReplyDO buildDefaultSubscribeAutoReply(String appId) {

View File

@ -2,11 +2,13 @@ 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 cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import javax.validation.Valid;
/**
* Service
*
@ -24,20 +26,23 @@ public interface MpMessageService {
PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO);
/**
*
*
*
* @param appId appId
* @param wxMessage
*/
void createFromUser(String appId, WxMpXmlMessage wxMessage);
void receiveMessage(String appId, WxMpXmlMessage wxMessage);
/**
*
* 使
*
* @param openid openid
* @param reply
*
*
* WxMpXmlOutMessage
*
* @param sendReqBO
* @return XML
*/
WxMpXmlOutMessage createFromAutoReply(String openid, MpAutoReplyDO reply);
WxMpXmlOutMessage sendOutMessage(@Valid MpMessageSendOutReqBO sendReqBO);
}

View File

@ -2,21 +2,22 @@ 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.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO;
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.dal.mysql.message.MpMessageMapper;
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.framework.mp.core.util.MpUtils;
import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
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;
@ -24,20 +25,12 @@ 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 javax.annotation.Resource;
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.message.MpMessageMapper;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.io.File;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* Service
*
@ -64,13 +57,16 @@ public class MpMessageServiceImpl implements MpMessageService {
@Resource
private FileApi fileApi;
@Resource
private Validator validator;
@Override
public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) {
return mpMessageMapper.selectPage(pageReqVO);
}
@Override
public void createFromUser(String appId, WxMpXmlMessage wxMessage) {
public void receiveMessage(String appId, WxMpXmlMessage wxMessage) {
WxMpService mpService = mpServiceFactory.getRequiredMpService(appId);
// 获得关联信息
MpAccountDO account = mpAccountService.getAccountFromCache(appId);
@ -88,24 +84,21 @@ public class MpMessageServiceImpl implements MpMessageService {
message.setThumbMediaUrl(mediaDownload(mpService, message.getThumbMediaId()));
}
mpMessageMapper.insert(message);
// WxConsts.MenuButtonType.VIEW TODO 芋艿:待测试
// wxMessage.getEventKey()
// WxConsts.MenuButtonType.CLICK
// wxMessage.getEventKey()
}
@Override
public WxMpXmlOutMessage createFromAutoReply(String openid, MpAutoReplyDO reply) {
public WxMpXmlOutMessage sendOutMessage(MpMessageSendOutReqBO sendReqBO) {
// 校验消息格式
MpUtils.validateMessage(validator, sendReqBO.getType(), sendReqBO);
// 获得关联信息
MpAccountDO account = mpAccountService.getAccountFromCache(reply.getAppId());
Assert.notNull(account, "公众号账号({}) 不存在", reply.getAppId());
MpUserDO user = mpUserService.getUser(reply.getAppId(), openid);
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", reply.getAppId(), openid);
MpAccountDO account = mpAccountService.getAccountFromCache(sendReqBO.getAppId());
Assert.notNull(account, "公众号账号({}) 不存在", sendReqBO.getAppId());
MpUserDO user = mpUserService.getUser(sendReqBO.getAppId(), sendReqBO.getOpenid());
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", sendReqBO.getAppId(), sendReqBO.getOpenid());
// 记录消息
MpMessageDO message = MpMessageConvert.INSTANCE.convert(reply, account, user);
MpMessageDO message = MpMessageConvert.INSTANCE.convert(sendReqBO, account, user);
message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
mpMessageMapper.insert(message);

View File

@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.mp.service.message.bo;
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils;
import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*;
import lombok.Data;
import me.chanjar.weixin.common.api.WxConsts;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* Request BO
*
* BO MP 使 BO
*
* @author
*/
@Data
public class MpMessageSendOutReqBO {
/**
* appId
*/
@NotEmpty(message = "公众号 appId 不能为空")
private String appId;
/**
* openid
*/
@NotEmpty(message = "公众号用户 openid 不能为空")
private String openid;
// ========== 消息内容 ==========
/**
*
*
* {@link WxConsts.XmlMsgType} TEXTIMAGEVOICEVIDEONEWS
*/
@NotEmpty(message = "消息类型不能为空")
public String type;
/**
*
*
* {@link WxConsts.XmlMsgType} TEXT
*/
@NotEmpty(message = "消息内容不能为空", groups = TextGroup.class)
private String content;
/**
* id
*
* {@link WxConsts.XmlMsgType} IMAGEVOICEVIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class})
private String mediaId;
/**
* URL
*
* {@link WxConsts.XmlMsgType} IMAGEVOICEVIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class})
private String mediaUrl;
/**
*
*
* {@link WxConsts.XmlMsgType} VIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class)
private String title;
/**
*
*
* {@link WxConsts.XmlMsgType} VIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class)
private String description;
/**
*
*
* {@link WxConsts.XmlMsgType} NEWS
*/
@Valid
@NotNull(message = "图文消息不能为空", groups = NewsGroup.class)
private MpMessageDO.Article article;
}