mp:完善【菜单】的回复功能

This commit is contained in:
YunaiV
2023-01-15 18:28:08 +08:00
parent 141e4e4c8b
commit 0499226c3d
12 changed files with 411 additions and 202 deletions

View File

@ -1,12 +1,18 @@
package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import me.chanjar.weixin.common.api.WxConsts;
import org.hibernate.validator.constraints.URL;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
// TODO 芋艿:完善 swagger 注解
import static cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*;
/**
* 微信菜单 Base VO提供给添加、修改、详细的子 VO 使用
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
@ -38,70 +44,51 @@ public class MpMenuBaseVO {
*/
private String type;
/**
* 网页链接
*
* 用户点击菜单可打开链接,不超过 1024 字节
*
* 类型为 {@link WxConsts.XmlMsgType} 的 VIEW、MINIPROGRAM
*/
@ApiModelProperty(value = "网页链接", example = "https://www.iocoder.cn/")
@NotEmpty(message = "网页链接不能为空", groups = {ViewButtonGroup.class, MiniProgramButtonGroup.class})
@URL(message = "网页链接必须是 URL 格式")
private String url;
/**
* 小程序的 appId
*
* 类型为 {@link WxConsts.MenuButtonType} 的 MINIPROGRAM
*/
@ApiModelProperty(value = "小程序的 appId", example = "wx1234567890")
@NotEmpty(message = "小程序的 appId 不能为空", groups = MiniProgramButtonGroup.class)
private String miniProgramAppId;
/**
* 小程序的页面路径
*
* 类型为 {@link WxConsts.MenuButtonType} 的 MINIPROGRAM
*/
@ApiModelProperty(value = "小程序的页面路径", example = "pages/index/index")
@NotEmpty(message = "小程序的页面路径不能为空", groups = MiniProgramButtonGroup.class)
private String miniProgramPagePath;
// ========== 消息内容 ==========
/**
* 消息类型
*
* 当 {@link #type} 为 CLICK、SCANCODE_WAITMSG
*
* 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS
*/
@ApiModelProperty(value = "消息类型", example = "text",
notes = "枚举 TEXT、IMAGE、VOICE、VIDEO、NEWS、MUSIC")
@NotEmpty(message = "消息类型不能为空", groups = {ClickButtonGroup.class, ScanCodeWaitMsgButtonGroup.class})
private String replyMessageType;
/**
* 回复的消息内容
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT
*/
@ApiModelProperty(value = "回复的消息内容", example = "欢迎关注")
@NotEmpty(message = "回复的消息内容不能为空", groups = {TextMessageGroup.class})
private String replyContent;
/**
* 回复的媒体 id
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
*/
@ApiModelProperty(value = "回复的媒体 id", example = "123456")
@NotEmpty(message = "回复的消息 mediaId 不能为空",
groups = {ImageMessageGroup.class, VoiceMessageGroup.class, VideoMessageGroup.class})
private String replyMediaId;
/**
* 回复的媒体 URL
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
*/
@ApiModelProperty(value = "回复的媒体 URL", example = "https://www.iocoder.cn/xxx.jpg")
@NotEmpty(message = "回复的消息 mediaId 不能为空",
groups = {ImageMessageGroup.class, VoiceMessageGroup.class, VideoMessageGroup.class})
private String replyMediaUrl;
/**
* 回复的标题
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
*/
@ApiModelProperty(value = "缩略图的媒体 id", example = "123456")
@NotEmpty(message = "回复的消息 thumbMediaId 不能为空", groups = {MusicMessageGroup.class})
private String replyThumbMediaId;
@ApiModelProperty(value = "缩略图的媒体 URL",example = "https://www.iocoder.cn/xxx.jpg")
@NotEmpty(message = "回复的消息 thumbMedia 地址不能为空", groups = {MusicMessageGroup.class})
private String replyThumbMediaUrl;
@ApiModelProperty(value = "回复的标题", example = "视频标题")
@NotEmpty(message = "回复的消息标题不能为空", groups = VideoMessageGroup.class)
private String replyTitle;
/**
* 回复的描述
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
*/
@ApiModelProperty(value = "回复的描述", example = "视频描述")
@NotEmpty(message = "消息描述不能为空", groups = VideoMessageGroup.class)
private String replyDescription;
/**
@ -109,6 +96,17 @@ public class MpMenuBaseVO {
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
*/
@NotNull(message = "回复的图文消息不能为空", groups = NewsMessageGroup.class)
@Valid
private List<MpMessageDO.Article> replyArticles;
@ApiModelProperty(value = "音乐链接", example = "https://www.iocoder.cn/xxx.mp3")
@NotEmpty(message = "回复的音乐链接不能为空", groups = MusicMessageGroup.class)
@URL(message = "回复的高质量音乐链接格式不正确", groups = MusicMessageGroup.class)
private String replyMusicUrl;
@ApiModelProperty(value = "高质量音乐链接", example = "https://www.iocoder.cn/xxx.mp3")
@NotEmpty(message = "回复的高质量音乐链接不能为空", groups = MusicMessageGroup.class)
@URL(message = "回复的高质量音乐链接格式不正确", groups = MusicMessageGroup.class)
private String replyHqMusicUrl;
}

View File

@ -26,28 +26,28 @@ public class MpMessageSendReqVO {
public String type;
@ApiModelProperty(value = "消息内容", required = true, example = "你好呀")
@NotEmpty(message = "消息内容不能为空", groups = TextGroup.class)
@NotEmpty(message = "消息内容不能为空", groups = TextMessageGroup.class)
private String content;
@ApiModelProperty(value = "媒体 ID", required = true, example = "qqc_2Fot30Jse-HDoZmo5RrUDijz2nGUkP")
@NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class})
@NotEmpty(message = "消息内容不能为空", groups = {ImageMessageGroup.class, VoiceMessageGroup.class, VideoMessageGroup.class})
private String mediaId;
@ApiModelProperty(value = "标题", required = true, example = "没有标题")
@NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class)
@NotEmpty(message = "消息内容不能为空", groups = VideoMessageGroup.class)
private String title;
@ApiModelProperty(value = "描述", required = true, example = "你猜")
@NotEmpty(message = "消息描述不能为空", groups = VideoGroup.class)
@NotEmpty(message = "消息描述不能为空", groups = VideoMessageGroup.class)
private String description;
@ApiModelProperty(value = "缩略图的媒体 id", required = true, example = "qqc_2Fot30Jse-HDoZmo5RrUDijz2nGUkP")
@NotEmpty(message = "缩略图的媒体 id 不能为空", groups = MusicGroup.class)
@NotEmpty(message = "缩略图的媒体 id 不能为空", groups = MusicMessageGroup.class)
private String thumbMediaId;
@ApiModelProperty(value = "图文消息", required = true)
@Valid
@NotNull(message = "图文消息不能为空", groups = NewsGroup.class)
@NotNull(message = "图文消息不能为空", groups = NewsMessageGroup.class)
private List<MpMessageDO.Article> articles;
@ApiModelProperty(value = "音乐链接", example = "https://www.iocoder.cn/music.mp3", notes = "消息类型为 MUSIC 时")

View File

@ -26,9 +26,12 @@ public interface MpMenuConvert {
@Mapping(source = "menu.replyMessageType", target = "type"),
@Mapping(source = "menu.replyContent", target = "content"),
@Mapping(source = "menu.replyMediaId", target = "mediaId"),
@Mapping(source = "menu.replyThumbMediaId", target = "thumbMediaId"),
@Mapping(source = "menu.replyTitle", target = "title"),
@Mapping(source = "menu.replyDescription", target = "description"),
@Mapping(source = "menu.replyArticles", target = "articles"),
@Mapping(source = "menu.replyMusicUrl", target = "musicUrl"),
@Mapping(source = "menu.replyHqMusicUrl", target = "hqMusicUrl"),
})
MpMessageSendOutReqBO convert(String openid, MpMenuDO menu);

View File

@ -17,7 +17,6 @@ public interface MpAutoReplyConvert {
@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.responseArticles", target = "articles"),

View File

@ -57,10 +57,14 @@ public interface MpMessageConvert {
break;
case WxConsts.XmlMsgType.IMAGE: // 2. 图片
case WxConsts.XmlMsgType.VOICE: // 3. 语音
message.setMediaId(sendReqBO.getMediaId()).setMediaUrl(sendReqBO.getMediaUrl());
message.setMediaId(sendReqBO.getMediaId())
// .setMediaUrl(sendReqBO.getMediaUrl()) TODO 芋艿:去 url
;
break;
case WxConsts.XmlMsgType.VIDEO: // 4. 视频
message.setMediaId(sendReqBO.getMediaId()).setMediaUrl(sendReqBO.getMediaUrl())
message.setMediaId(sendReqBO.getMediaId())
// .setMediaUrl(sendReqBO.getMediaUrl()) TODO 芋艿:去 url
.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription());
break;
case WxConsts.XmlMsgType.NEWS: // 5. 图文
@ -69,7 +73,7 @@ public interface MpMessageConvert {
message.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription())
.setMusicUrl(sendReqBO.getMusicUrl()).setHqMusicUrl(sendReqBO.getHqMusicUrl())
.setThumbMediaId(sendReqBO.getThumbMediaId());
// .setThumbMediaUrl(sendReqBO.getThumbMediaUrl()); TODO 芋艿url 待确定
// .setThumbMediaUrl(sendReqBO.getThumbMediaUrl()); TODO 芋艿:url
break;
default:
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());

View File

@ -103,7 +103,7 @@ public class MpMenuDO extends BaseDO {
*
* 当 {@link #type} 为 CLICK、SCANCODE_WAITMSG
*
* 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS
* 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS、MUSIC
*/
private String replyMessageType;
@ -140,6 +140,19 @@ public class MpMenuDO extends BaseDO {
*/
private String replyDescription;
/**
* 缩略图的媒体 id通过素材管理中的接口上传多媒体文件得到的 id
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
*/
private String replyThumbMediaId;
/**
* 缩略图的媒体 URL
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
*/
private String replyThumbMediaUrl;
/**
* 回复的图文消息数组
*
@ -148,4 +161,19 @@ public class MpMenuDO extends BaseDO {
@TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class)
private List<MpMessageDO.Article> replyArticles;
/**
* 回复的音乐链接
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
*/
private String replyMusicUrl;
/**
* 回复的高质量音乐链接
*
* WIFI 环境优先使用该链接播放音乐
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
*/
private String replyHqMusicUrl;
}

View File

@ -124,13 +124,13 @@ public class MpMessageDO extends BaseDO {
/**
* 缩略图的媒体 id通过素材管理中的接口上传多媒体文件得到的 id
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
*/
private String thumbMediaId;
/**
* 缩略图的媒体 URL
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
*/
private String thumbMediaUrl;

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mp.framework.mp.core.util;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
@ -14,36 +15,6 @@ import javax.validation.Validator;
@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 {}
/**
* Music 类型的消息,参数校验 Group
*/
public interface MusicGroup {}
/**
* 校验消息的格式是否符合要求
*
@ -55,22 +26,22 @@ public class MpUtils {
Class<?> group;
switch (type) {
case WxConsts.XmlMsgType.TEXT:
group = TextGroup.class;
group = TextMessageGroup.class;
break;
case WxConsts.XmlMsgType.IMAGE:
group = ImageGroup.class;
group = ImageMessageGroup.class;
break;
case WxConsts.XmlMsgType.VOICE:
group = VoiceGroup.class;
group = VoiceMessageGroup.class;
break;
case WxConsts.XmlMsgType.VIDEO:
group = VideoGroup.class;
group = VideoMessageGroup.class;
break;
case WxConsts.XmlMsgType.NEWS:
group = NewsGroup.class;
group = NewsMessageGroup.class;
break;
case WxConsts.XmlMsgType.MUSIC:
group = MusicGroup.class;
group = MusicMessageGroup.class;
break;
default:
log.error("[validateMessage][未知的消息类型({})]", message);
@ -80,6 +51,35 @@ public class MpUtils {
ValidationUtils.validate(validator, message, group);
}
public static void validateButton(Validator validator, String type, String messageType, Object button) {
if (StrUtil.isBlank(type)) {
return;
}
// 获得对应的校验 group
Class<?> group;
switch (type) {
case WxConsts.MenuButtonType.CLICK:
group = ClickButtonGroup.class;
validateMessage(validator, messageType, button); // 需要额外校验回复的消息格式
break;
case WxConsts.MenuButtonType.VIEW:
group = ViewButtonGroup.class;
break;
case WxConsts.MenuButtonType.MINIPROGRAM:
group = MiniProgramButtonGroup.class;
break;
case WxConsts.MenuButtonType.SCANCODE_WAITMSG:
group = ScanCodeWaitMsgButtonGroup.class;
validateMessage(validator, messageType, button); // 需要额外校验回复的消息格式
break;
default:
log.error("[validateButton][未知的按钮({})]", button);
throw new IllegalArgumentException("不支持的按钮类型:" + type);
}
// 执行校验
ValidationUtils.validate(validator, button, group);
}
/**
* 根据消息类型,获得对应的媒体文件类型
*
@ -101,4 +101,53 @@ public class MpUtils {
}
}
/**
* Text 类型的消息,参数校验 Group
*/
public interface TextMessageGroup {}
/**
* Image 类型的消息,参数校验 Group
*/
public interface ImageMessageGroup {}
/**
* Voice 类型的消息,参数校验 Group
*/
public interface VoiceMessageGroup {}
/**
* Video 类型的消息,参数校验 Group
*/
public interface VideoMessageGroup {}
/**
* News 类型的消息,参数校验 Group
*/
public interface NewsMessageGroup {}
/**
* Music 类型的消息,参数校验 Group
*/
public interface MusicMessageGroup {}
/**
* Click 类型的按钮,参数校验 Group
*/
public interface ClickButtonGroup {}
/**
* View 类型的按钮,参数校验 Group
*/
public interface ViewButtonGroup {}
/**
* MiniProgram 类型的按钮,参数校验 Group
*/
public interface MiniProgramButtonGroup {}
/**
* SCANCODE_WAITMSG 类型的按钮,参数校验 Group
*/
public interface ScanCodeWaitMsgButtonGroup {}
}

View File

@ -8,6 +8,7 @@ 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.mysql.menu.MpMenuMapper;
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.MpMessageService;
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -48,6 +50,9 @@ public class MpMenuServiceImpl implements MpMenuService {
@Lazy // 延迟加载,避免循环引用报错
private MpServiceFactory mpServiceFactory;
@Resource
private Validator validator;
@Resource
private MpMenuMapper mpMenuMapper;
@ -57,6 +62,9 @@ public class MpMenuServiceImpl implements MpMenuService {
MpAccountDO account = mpAccountService.getRequiredAccount(createReqVO.getAccountId());
WxMpService mpService = mpServiceFactory.getRequiredMpService(createReqVO.getAccountId());
// 参数校验
createReqVO.getMenus().forEach(this::validateMenu);
// 第一步,同步公众号
WxMenu wxMenu = new WxMenu();
wxMenu.setButtons(MpMenuConvert.INSTANCE.convert(createReqVO.getMenus()));
@ -79,6 +87,49 @@ public class MpMenuServiceImpl implements MpMenuService {
});
}
/**
* 校验菜单的格式是否正确
*
* @param menu 菜单
*/
private void validateMenu(MpMenuSaveReqVO.Menu menu) {
MpUtils.validateButton(validator, menu.getType(), menu.getReplyMessageType(), menu);
// 子菜单
if (CollUtil.isEmpty(menu.getChildren())) {
return;
}
menu.getChildren().forEach(this::validateMenu);
}
/**
* 创建菜单,并存储到数据库
*
* @param wxMenu 菜单信息
* @param parentMenu 父菜单
* @param account 公众号账号
* @return 创建后的菜单
*/
private MpMenuDO createMenu(MpMenuSaveReqVO.Menu wxMenu, MpMenuDO parentMenu, MpAccountDO account) {
// 创建菜单
MpMenuDO menu = CollUtil.isNotEmpty(wxMenu.getChildren())
? new MpMenuDO().setName(wxMenu.getName())
: MpMenuConvert.INSTANCE.convert02(wxMenu);
// 设置菜单的公众号账号信息
if (account != null) {
menu.setAccountId(account.getId()).setAppId(account.getAppId());
}
// 设置父编号
if (parentMenu != null) {
menu.setParentId(parentMenu.getId());
} else {
menu.setParentId(MpMenuDO.ID_ROOT);
}
// 插入到数据库
mpMenuMapper.insert(menu);
return menu;
}
@Override
public void deleteMenuByAccountId(Long accountId) {
WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);
@ -93,25 +144,6 @@ public class MpMenuServiceImpl implements MpMenuService {
mpMenuMapper.deleteByAccountId(accountId);
}
private MpMenuDO createMenu(MpMenuSaveReqVO.Menu wxMenu, MpMenuDO parentMenu, MpAccountDO account) {
MpMenuDO menu = CollUtil.isNotEmpty(wxMenu.getChildren())
? new MpMenuDO().setName(wxMenu.getName())
: MpMenuConvert.INSTANCE.convert02(wxMenu);
if (account != null) {
menu.setAccountId(account.getId()).setAppId(account.getAppId());
}
if (parentMenu != null) {
menu.setParentId(parentMenu.getId());
} else {
menu.setParentId(MpMenuDO.ID_ROOT);
}
if (StrUtil.isNotEmpty(wxMenu.getReplyMediaId())) {
throw new IllegalArgumentException("未实现");
}
mpMenuMapper.insert(menu);
return menu;
}
@Override
public WxMpXmlOutMessage reply(String appId, String key, String openid) {
// 第一步,获得菜单

View File

@ -76,8 +76,8 @@ public class MpMessageServiceImpl implements MpMessageService {
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser());
// 记录消息
MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user);
message.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom());
MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user)
.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom());
downloadMessageMedia(message);
mpMessageMapper.insert(message);
}
@ -94,9 +94,9 @@ public class MpMessageServiceImpl implements MpMessageService {
Assert.notNull(user, "公众号粉丝({}/{}) 不存在", sendReqBO.getAppId(), sendReqBO.getOpenid());
// 记录消息
MpMessageDO message = MpMessageConvert.INSTANCE.convert(sendReqBO, account, user);
message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
// TODO 芋艿:downloadMessageMedia
MpMessageDO message = MpMessageConvert.INSTANCE.convert(sendReqBO, account, user).
setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
downloadMessageMedia(message);
mpMessageMapper.insert(message);
// 转换返回 WxMpXmlOutMessage 对象
@ -122,8 +122,8 @@ public class MpMessageServiceImpl implements MpMessageService {
}
// 记录消息
MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user);
message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user)
.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom());
downloadMessageMedia(message);
mpMessageMapper.insert(message);
return message;

View File

@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*;
import lombok.Data;
import me.chanjar.weixin.common.api.WxConsts;
import org.hibernate.validator.constraints.URL;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
@ -45,7 +46,7 @@ public class MpMessageSendOutReqBO {
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT
*/
@NotEmpty(message = "消息内容不能为空", groups = TextGroup.class)
@NotEmpty(message = "消息内容不能为空", groups = TextMessageGroup.class)
private String content;
/**
@ -53,39 +54,38 @@ public class MpMessageSendOutReqBO {
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class})
@NotEmpty(message = "消息 mediaId 不能为空", groups = {ImageMessageGroup.class, VoiceMessageGroup.class, VideoMessageGroup.class})
private String mediaId;
// TODO 芋艿:考虑去掉
/**
* 媒体 URL
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class})
private String mediaUrl;
// // TODO 芋艿:考虑去掉
// /**
// * 媒体 URL
// *
// * 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
// */
// @NotEmpty(message = "消息内容不能为空", groups = {ImageMessageGroup.class, VoiceMessageGroup.class, VideoMessageGroup.class})
// private String mediaUrl;
/**
* 缩略图的媒体 id
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC
*/
@NotEmpty(message = "消息内容不能为空", groups = {MusicGroup.class})
@NotEmpty(message = "消息 thumbMediaId 不能为空", groups = {MusicMessageGroup.class})
private String thumbMediaId;
// TODO 芋艿:考虑去掉
/**
* 标题
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class)
@NotEmpty(message = "消息标题不能为空", groups = VideoMessageGroup.class)
private String title;
/**
* 描述
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
*/
@NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class)
@NotEmpty(message = "消息描述不能为空", groups = VideoMessageGroup.class)
private String description;
/**
@ -94,7 +94,7 @@ public class MpMessageSendOutReqBO {
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
*/
@Valid
@NotNull(message = "图文消息不能为空", groups = NewsGroup.class)
@NotNull(message = "图文消息不能为空", groups = NewsMessageGroup.class)
private List<MpMessageDO.Article> articles;
/**
@ -102,6 +102,8 @@ public class MpMessageSendOutReqBO {
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
*/
@NotEmpty(message = "音乐链接不能为空", groups = MusicMessageGroup.class)
@URL(message = "高质量音乐链接格式不正确", groups = MusicMessageGroup.class)
private String musicUrl;
/**
@ -109,6 +111,8 @@ public class MpMessageSendOutReqBO {
*
* 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
*/
@NotEmpty(message = "高质量音乐链接不能为空", groups = MusicMessageGroup.class)
@URL(message = "高质量音乐链接格式不正确", groups = MusicMessageGroup.class)
private String hqMusicUrl;
}