mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-25 16:51:52 +08:00
mp:重构 message 发送消息的方法
This commit is contained in:
parent
47f071560f
commit
665f4b2b09
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -1,20 +1,16 @@
|
|||||||
package cn.iocoder.yudao.module.mp.convert.message;
|
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.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.controller.admin.message.vo.MpMessageRespVO;
|
||||||
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
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.message.MpMessageDO;
|
||||||
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.service.message.bo.MpMessageSendOutReqBO;
|
||||||
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 me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage;
|
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage;
|
||||||
import me.chanjar.weixin.mp.builder.outxml.BaseBuilder;
|
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.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.Mappings;
|
import org.mapstruct.Mappings;
|
||||||
@ -51,23 +47,27 @@ public interface MpMessageConvert {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
default MpMessageDO convert(MpAutoReplyDO reply, MpAccountDO account, MpUserDO user) {
|
default MpMessageDO convert(MpMessageSendOutReqBO sendReqBO, MpAccountDO account, MpUserDO user) {
|
||||||
// 构建消息
|
// 构建消息
|
||||||
MpMessageDO message = new MpMessageDO();
|
MpMessageDO message = new MpMessageDO();
|
||||||
message.setType(reply.getResponseMessageType());
|
message.setType(sendReqBO.getType());
|
||||||
// 1. 文本
|
switch (sendReqBO.getType()) {
|
||||||
if (StrUtil.equals(reply.getResponseMessageType(), WxConsts.XmlMsgType.TEXT)) {
|
case WxConsts.XmlMsgType.TEXT: // 1. 文本
|
||||||
message.setContent(reply.getResponseContent());
|
message.setContent(sendReqBO.getContent());
|
||||||
} else if (ObjectUtils.equalsAny(reply.getResponseMessageType(), WxConsts.XmlMsgType.IMAGE, // 2. 图片
|
break;
|
||||||
WxConsts.XmlMsgType.VOICE)) { // 3. 语音
|
case WxConsts.XmlMsgType.IMAGE: // 2. 图片
|
||||||
message.setMediaId(reply.getResponseMediaId()).setMediaUrl(reply.getResponseMediaUrl());
|
case WxConsts.XmlMsgType.VOICE: // 3. 语音
|
||||||
} else if (StrUtil.equals(reply.getResponseMessageType(), WxConsts.XmlMsgType.VIDEO)) { // 4. 视频
|
message.setMediaId(sendReqBO.getMediaId()).setMediaUrl(sendReqBO.getMediaUrl());
|
||||||
message.setMediaId(reply.getResponseMediaId()).setMediaUrl(reply.getResponseMediaUrl())
|
break;
|
||||||
.setTitle(reply.getResponseTitle()).setDescription(reply.getResponseDescription());
|
case WxConsts.XmlMsgType.VIDEO: // 4. 视频
|
||||||
} else if (StrUtil.equals(reply.getResponseMessageType(), WxConsts.XmlMsgType.NEWS)) { // 5. 图文
|
message.setMediaId(sendReqBO.getMediaId()).setMediaUrl(sendReqBO.getMediaUrl())
|
||||||
message.setArticles(Collections.singletonList(reply.getResponseArticle()));
|
.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription());
|
||||||
} else {
|
break;
|
||||||
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
|
case WxConsts.XmlMsgType.NEWS: // 5. 图文
|
||||||
|
message.setArticles(Collections.singletonList(sendReqBO.getArticle()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 其它字段
|
// 其它字段
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
package cn.iocoder.yudao.module.mp.dal.dataobject.menu;
|
package cn.iocoder.yudao.module.mp.dal.dataobject.menu;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
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.account.MpAccountDO;
|
||||||
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
|
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 com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||||
import lombok.*;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import lombok.ToString;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import me.chanjar.weixin.common.api.WxConsts;
|
||||||
import me.chanjar.weixin.common.bean.menu.WxMenu;
|
import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
|
||||||
import me.chanjar.weixin.common.bean.menu.WxMenuButton;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -48,8 +52,8 @@ public class MpMenuDO extends BaseDO {
|
|||||||
/**
|
/**
|
||||||
* 按钮列表
|
* 按钮列表
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = WxMenuButtonTypeHandler.class)
|
@TableField(typeHandler = ButtonTypeHandler.class)
|
||||||
private List<WxMenuButton> buttons;
|
private List<Button> buttons;
|
||||||
/**
|
/**
|
||||||
* 同步状态
|
* 同步状态
|
||||||
*
|
*
|
||||||
@ -58,16 +62,113 @@ public class MpMenuDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Boolean syncStatus;
|
private Boolean syncStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按钮
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class Button {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*
|
||||||
|
* 枚举 {@link MenuButtonType}
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
/**
|
||||||
|
* 消息类型
|
||||||
|
*
|
||||||
|
* 当 {@link #type} 为 CLICK、SCANCODE_WAITMSG
|
||||||
|
*
|
||||||
|
* 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS
|
||||||
|
*/
|
||||||
|
private String messageType;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 标识
|
||||||
|
*/
|
||||||
|
private String key;
|
||||||
|
/**
|
||||||
|
* 二级菜单列表
|
||||||
|
*/
|
||||||
|
private List<Button> subButtons;
|
||||||
|
/**
|
||||||
|
* 网页链接
|
||||||
|
*
|
||||||
|
* 用户点击菜单可打开链接,不超过 1024 字节
|
||||||
|
*
|
||||||
|
* 类型为 {@link WxConsts.XmlMsgType} 的 VIEW、MINIPROGRAM
|
||||||
|
*/
|
||||||
|
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} 的 IMAGE、VOICE、VIDEO
|
||||||
|
*/
|
||||||
|
private String mediaId;
|
||||||
|
/**
|
||||||
|
* 媒体 URL
|
||||||
|
*
|
||||||
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||||
|
*/
|
||||||
|
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 @芋艿:可以找一些新的思路
|
// TODO @芋艿:可以找一些新的思路
|
||||||
public static class WxMenuButtonTypeHandler extends AbstractJsonTypeHandler<List<WxMenuButton>> {
|
public static class ButtonTypeHandler extends AbstractJsonTypeHandler<List<Button>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<WxMenuButton> parse(String json) {
|
protected List<Button> parse(String json) {
|
||||||
return JsonUtils.parseArray(json, WxMenuButton.class);
|
return JsonUtils.parseArray(json, Button.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String toJson(List<WxMenuButton> obj) {
|
protected String toJson(List<Button> obj) {
|
||||||
return JsonUtils.toJsonString(obj);
|
return JsonUtils.toJsonString(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import lombok.ToString;
|
|||||||
import me.chanjar.weixin.common.api.WxConsts;
|
import me.chanjar.weixin.common.api.WxConsts;
|
||||||
import me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
|
import me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,9 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import me.chanjar.weixin.common.api.WxConsts;
|
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.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -199,20 +201,24 @@ public class MpMessageDO extends BaseDO {
|
|||||||
/**
|
/**
|
||||||
* 图文消息标题
|
* 图文消息标题
|
||||||
*/
|
*/
|
||||||
|
@NotEmpty(message = "图文消息标题不能为空", groups = NewsBuilder.class)
|
||||||
private String title;
|
private String title;
|
||||||
/**
|
/**
|
||||||
* 图文消息描述
|
* 图文消息描述
|
||||||
*/
|
*/
|
||||||
|
@NotEmpty(message = "图文消息描述不能为空", groups = NewsBuilder.class)
|
||||||
private String description;
|
private String description;
|
||||||
/**
|
/**
|
||||||
* 图片链接
|
* 图片链接
|
||||||
*
|
*
|
||||||
* 支持JPG、PNG格式,较好的效果为大图 360*200,小图 200*200
|
* 支持 JPG、PNG 格式,较好的效果为大图 360*200,小图 200*200
|
||||||
*/
|
*/
|
||||||
|
@NotEmpty(message = "图片链接不能为空", groups = NewsBuilder.class)
|
||||||
private String picUrl;
|
private String picUrl;
|
||||||
/**
|
/**
|
||||||
* 点击图文消息跳转链接
|
* 点击图文消息跳转链接
|
||||||
*/
|
*/
|
||||||
|
@NotEmpty(message = "点击图文消息跳转链接不能为空", groups = NewsBuilder.class)
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,4 +7,8 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public interface MpMenuMapper extends BaseMapperX<MpMenuDO> {
|
public interface MpMenuMapper extends BaseMapperX<MpMenuDO> {
|
||||||
|
|
||||||
|
default MpMenuDO selectByAppId(String appId) {
|
||||||
|
return selectOne(MpMenuDO::getAppId, appId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,12 +1,16 @@
|
|||||||
package cn.iocoder.yudao.module.mp.service.handler.menu;
|
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.common.session.WxSessionManager;
|
||||||
|
import me.chanjar.weixin.mp.api.WxMpMenuService;
|
||||||
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;
|
||||||
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.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
|
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
|
@Component
|
||||||
public class MenuHandler implements WxMpMessageHandler {
|
public class MenuHandler implements WxMpMessageHandler {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MpMenuService mpMenuService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
|
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
|
||||||
Map<String, Object> context, WxMpService weixinService,
|
WxMpService weixinService, WxSessionManager sessionManager) {
|
||||||
WxSessionManager sessionManager) {
|
return mpMenuService.reply(MpContextHolder.getAppId(), wxMessage.getEventKey(), wxMessage.getFromUser());
|
||||||
|
|
||||||
String msg = wxMessage.getEventKey();
|
|
||||||
if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WxMpXmlOutMessage.TEXT().content(msg)
|
|
||||||
.fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class MessageReceiveHandler implements WxMpMessageHandler {
|
|||||||
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) {
|
||||||
log.info("[handle][接收到请求消息,内容:{}]", wxMessage);
|
log.info("[handle][接收到请求消息,内容:{}]", wxMessage);
|
||||||
mpMessageService.createFromUser(MpContextHolder.getAppId(), wxMessage);
|
mpMessageService.receiveMessage(MpContextHolder.getAppId(), wxMessage);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import javax.validation.*;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*;
|
import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*;
|
||||||
import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
|
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 接口
|
* 微信菜单 Service 接口
|
||||||
@ -35,4 +37,14 @@ public interface MpMenuService {
|
|||||||
*/
|
*/
|
||||||
MpMenuDO getMenu(Long id);
|
MpMenuDO getMenu(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户点击菜单按钮时,回复对应的消息
|
||||||
|
*
|
||||||
|
* @param appId 公众号 AppId
|
||||||
|
* @param key 菜单按钮的标识
|
||||||
|
* @param openid 用户的 openid
|
||||||
|
* @return 消息
|
||||||
|
*/
|
||||||
|
WxMpXmlOutMessage reply(String appId, String key, String openid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
package cn.iocoder.yudao.module.mp.service.menu;
|
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.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.dal.dataobject.menu.MpMenuDO;
|
||||||
import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
|
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.bean.menu.WxMenu;
|
||||||
import me.chanjar.weixin.common.error.WxErrorException;
|
import me.chanjar.weixin.common.error.WxErrorException;
|
||||||
import me.chanjar.weixin.mp.api.WxMpService;
|
import me.chanjar.weixin.mp.api.WxMpService;
|
||||||
|
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -27,6 +34,7 @@ import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@Validated
|
@Validated
|
||||||
|
@Slf4j
|
||||||
public class MpMenuServiceImpl implements MpMenuService {
|
public class MpMenuServiceImpl implements MpMenuService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@ -77,4 +85,49 @@ public class MpMenuServiceImpl implements MpMenuService {
|
|||||||
return mpMenuMapper.selectById(id);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.mp.service.message;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
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.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.MpAutoReplyTypeEnum;
|
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.account.MpAccountService;
|
||||||
|
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
|
||||||
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;
|
||||||
@ -58,7 +60,8 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
|
|||||||
MpAutoReplyDO reply = CollUtil.getFirst(replies);
|
MpAutoReplyDO reply = CollUtil.getFirst(replies);
|
||||||
|
|
||||||
// 第二步,基于自动回复,创建消息
|
// 第二步,基于自动回复,创建消息
|
||||||
return mpMessageService.createFromAutoReply(wxMessage.getFromUser(), reply);
|
MpMessageSendOutReqBO sendReqBO = MpAutoReplyConvert.INSTANCE.convert(wxMessage.getFromUser(), reply);
|
||||||
|
return mpMessageService.sendOutMessage(sendReqBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -69,7 +72,8 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
|
|||||||
: buildDefaultSubscribeAutoReply(appId); // 如果不存在,提供一个默认末班
|
: 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) {
|
private MpAutoReplyDO buildDefaultSubscribeAutoReply(String appId) {
|
||||||
|
@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.mp.service.message;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.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.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.WxMpXmlMessage;
|
||||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 粉丝消息表 Service 接口
|
* 粉丝消息表 Service 接口
|
||||||
*
|
*
|
||||||
@ -24,20 +26,23 @@ public interface MpMessageService {
|
|||||||
PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO);
|
PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存粉丝消息,来自用户发送
|
* 从公众号,接收到用户消息
|
||||||
*
|
*
|
||||||
* @param appId 微信公众号 appId
|
* @param appId 微信公众号 appId
|
||||||
* @param wxMessage 消息
|
* @param wxMessage 消息
|
||||||
*/
|
*/
|
||||||
void createFromUser(String appId, WxMpXmlMessage wxMessage);
|
void receiveMessage(String appId, WxMpXmlMessage wxMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建粉丝消息,通过自动回复
|
* 使用公众号,给用户回复消息
|
||||||
*
|
*
|
||||||
* @param openid 公众号粉丝 openid
|
* 例如说:自动回复、客服消息、菜单回复消息等场景
|
||||||
* @param reply 自动回复
|
*
|
||||||
|
* 注意,该方法只是返回 WxMpXmlOutMessage 对象,不会真的发送消息
|
||||||
|
*
|
||||||
|
* @param sendReqBO 消息内容
|
||||||
* @return 微信回复消息 XML
|
* @return 微信回复消息 XML
|
||||||
*/
|
*/
|
||||||
WxMpXmlOutMessage createFromAutoReply(String openid, MpAutoReplyDO reply);
|
WxMpXmlOutMessage sendOutMessage(@Valid MpMessageSendOutReqBO sendReqBO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,22 @@ package cn.iocoder.yudao.module.mp.service.message;
|
|||||||
|
|
||||||
import cn.hutool.core.io.FileTypeUtil;
|
import cn.hutool.core.io.FileTypeUtil;
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
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.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.convert.message.MpMessageConvert;
|
||||||
import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
|
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.message.MpMessageDO;
|
||||||
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.dal.mysql.message.MpMessageMapper;
|
||||||
import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum;
|
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.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.account.MpAccountService;
|
||||||
|
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
|
||||||
import cn.iocoder.yudao.module.mp.service.user.MpUserService;
|
import cn.iocoder.yudao.module.mp.service.user.MpUserService;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.chanjar.weixin.common.error.WxErrorException;
|
import me.chanjar.weixin.common.error.WxErrorException;
|
||||||
import me.chanjar.weixin.mp.api.WxMpService;
|
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 me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*;
|
import javax.annotation.Resource;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import javax.validation.Validator;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.mp.dal.mysql.message.MpMessageMapper;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 粉丝消息表 Service 实现类
|
* 粉丝消息表 Service 实现类
|
||||||
*
|
*
|
||||||
@ -64,13 +57,16 @@ public class MpMessageServiceImpl implements MpMessageService {
|
|||||||
@Resource
|
@Resource
|
||||||
private FileApi fileApi;
|
private FileApi fileApi;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private Validator validator;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) {
|
public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) {
|
||||||
return mpMessageMapper.selectPage(pageReqVO);
|
return mpMessageMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createFromUser(String appId, WxMpXmlMessage wxMessage) {
|
public void receiveMessage(String appId, WxMpXmlMessage wxMessage) {
|
||||||
WxMpService mpService = mpServiceFactory.getRequiredMpService(appId);
|
WxMpService mpService = mpServiceFactory.getRequiredMpService(appId);
|
||||||
// 获得关联信息
|
// 获得关联信息
|
||||||
MpAccountDO account = mpAccountService.getAccountFromCache(appId);
|
MpAccountDO account = mpAccountService.getAccountFromCache(appId);
|
||||||
@ -88,24 +84,21 @@ public class MpMessageServiceImpl implements MpMessageService {
|
|||||||
message.setThumbMediaUrl(mediaDownload(mpService, message.getThumbMediaId()));
|
message.setThumbMediaUrl(mediaDownload(mpService, message.getThumbMediaId()));
|
||||||
}
|
}
|
||||||
mpMessageMapper.insert(message);
|
mpMessageMapper.insert(message);
|
||||||
|
|
||||||
// WxConsts.MenuButtonType.VIEW TODO 芋艿:待测试
|
|
||||||
// wxMessage.getEventKey()
|
|
||||||
|
|
||||||
// WxConsts.MenuButtonType.CLICK
|
|
||||||
// wxMessage.getEventKey()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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());
|
MpAccountDO account = mpAccountService.getAccountFromCache(sendReqBO.getAppId());
|
||||||
Assert.notNull(account, "公众号账号({}) 不存在", reply.getAppId());
|
Assert.notNull(account, "公众号账号({}) 不存在", sendReqBO.getAppId());
|
||||||
MpUserDO user = mpUserService.getUser(reply.getAppId(), openid);
|
MpUserDO user = mpUserService.getUser(sendReqBO.getAppId(), sendReqBO.getOpenid());
|
||||||
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", reply.getAppId(), openid);
|
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());
|
message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
|
||||||
mpMessageMapper.insert(message);
|
mpMessageMapper.insert(message);
|
||||||
|
|
||||||
|
@ -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} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "消息类型不能为空")
|
||||||
|
public String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息内容
|
||||||
|
*
|
||||||
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "消息内容不能为空", groups = TextGroup.class)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 媒体 id
|
||||||
|
*
|
||||||
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class})
|
||||||
|
private String mediaId;
|
||||||
|
/**
|
||||||
|
* 媒体 URL
|
||||||
|
*
|
||||||
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user