mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 23:31:52 +08:00
mp:完成 menu 点击时,自动回复消息的逻辑
This commit is contained in:
parent
665f4b2b09
commit
71beeabe9c
@ -16,5 +16,19 @@ tenant-id: {{adminTenentId}}
|
|||||||
"name":"搜索",
|
"name":"搜索",
|
||||||
"type":"view",
|
"type":"view",
|
||||||
"url":"http://www.soso.com/"
|
"url":"http://www.soso.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "父按钮",
|
||||||
|
"subButtons": [
|
||||||
|
{
|
||||||
|
"type":"click",
|
||||||
|
"name":"归去来兮",
|
||||||
|
"key":"MUSIC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"不说",
|
||||||
|
"type":"view",
|
||||||
|
"url":"https://www.soso.com/"
|
||||||
|
}]
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,4 @@ public class MpMenuBaseVO {
|
|||||||
@NotNull(message = "公众号账号的编号不能为空")
|
@NotNull(message = "公众号账号的编号不能为空")
|
||||||
private Long accountId;
|
private Long accountId;
|
||||||
|
|
||||||
@NotNull(message = "按钮不能为空")
|
|
||||||
private List<WxMenuButton> buttons;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,10 @@ package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
|
|||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import io.swagger.annotations.*;
|
import io.swagger.annotations.*;
|
||||||
|
import me.chanjar.weixin.common.bean.menu.WxMenuButton;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@ApiModel("管理后台 - 微信菜单保存 Request VO")
|
@ApiModel("管理后台 - 微信菜单保存 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@ -9,4 +13,7 @@ import io.swagger.annotations.*;
|
|||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
public class MpMenuSaveReqVO extends MpMenuBaseVO {
|
public class MpMenuSaveReqVO extends MpMenuBaseVO {
|
||||||
|
|
||||||
|
@NotNull(message = "按钮不能为空")
|
||||||
|
private List<WxMenuButton> buttons;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.mp.convert.menu;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.Mappings;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
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;
|
||||||
@ -16,4 +19,16 @@ public interface MpMenuConvert {
|
|||||||
|
|
||||||
MpMenuRespVO convert(MpMenuDO bean);
|
MpMenuRespVO convert(MpMenuDO bean);
|
||||||
|
|
||||||
|
@Mappings({
|
||||||
|
@Mapping(source = "menu.appId", target = "appId"),
|
||||||
|
@Mapping(source = "menu.replyMessageType", target = "type"),
|
||||||
|
@Mapping(source = "menu.replyContent", target = "content"),
|
||||||
|
@Mapping(source = "menu.replyMediaId", target = "mediaId"),
|
||||||
|
@Mapping(source = "menu.replyMediaUrl", target = "mediaUrl"),
|
||||||
|
@Mapping(source = "menu.replyTitle", target = "title"),
|
||||||
|
@Mapping(source = "menu.replyDescription", target = "description"),
|
||||||
|
@Mapping(source = "menu.replyArticles", target = "articles"),
|
||||||
|
})
|
||||||
|
MpMessageSendOutReqBO convert(String openid, MpMenuDO menu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public interface MpAutoReplyConvert {
|
|||||||
@Mapping(source = "reply.responseMediaUrl", target = "mediaUrl"),
|
@Mapping(source = "reply.responseMediaUrl", target = "mediaUrl"),
|
||||||
@Mapping(source = "reply.responseTitle", target = "title"),
|
@Mapping(source = "reply.responseTitle", target = "title"),
|
||||||
@Mapping(source = "reply.responseDescription", target = "description"),
|
@Mapping(source = "reply.responseDescription", target = "description"),
|
||||||
@Mapping(source = "reply.responseArticle", target = "article"),
|
@Mapping(source = "reply.responseArticles", target = "articles"),
|
||||||
})
|
})
|
||||||
MpMessageSendOutReqBO convert(String openid, MpAutoReplyDO reply);
|
MpMessageSendOutReqBO convert(String openid, MpAutoReplyDO reply);
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public interface MpMessageConvert {
|
|||||||
.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription());
|
.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription());
|
||||||
break;
|
break;
|
||||||
case WxConsts.XmlMsgType.NEWS: // 5. 图文
|
case WxConsts.XmlMsgType.NEWS: // 5. 图文
|
||||||
message.setArticles(Collections.singletonList(sendReqBO.getArticle()));
|
message.setArticles(sendReqBO.getArticles());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
|
throw new IllegalArgumentException("不支持的消息类型:" + message.getType());
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
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.mybatis.core.dataobject.BaseDO;
|
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;
|
||||||
@ -8,7 +7,6 @@ import com.baomidou.mybatisplus.annotation.KeySequence;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
@ -20,8 +18,6 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* 微信菜单 DO
|
* 微信菜单 DO
|
||||||
*
|
*
|
||||||
* 一个公众号,只有一个 MpMenuDO 记录。一个公众号的多个菜单,对应到就是 {@link #buttons} 多个按钮
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@TableName(value = "mp_menu", autoResultMap = true)
|
@TableName(value = "mp_menu", autoResultMap = true)
|
||||||
@ -32,7 +28,12 @@ import java.util.List;
|
|||||||
public class MpMenuDO extends BaseDO {
|
public class MpMenuDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主键
|
* 编号 - 顶级菜单
|
||||||
|
*/
|
||||||
|
public static final Long ID_ROOT = 0L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号
|
||||||
*/
|
*/
|
||||||
@TableId
|
@TableId
|
||||||
private Long id;
|
private Long id;
|
||||||
@ -50,50 +51,33 @@ public class MpMenuDO extends BaseDO {
|
|||||||
private String appId;
|
private String appId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按钮列表
|
* 菜单名称
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = ButtonTypeHandler.class)
|
private String name;
|
||||||
private List<Button> buttons;
|
|
||||||
/**
|
/**
|
||||||
* 同步状态
|
* 菜单标识
|
||||||
*
|
*
|
||||||
* true - 已同步
|
* 支持多 DB 类型时,无法直接使用 key + @TableField("menuKey") 来实现转换,原因是 "menuKey" AS key 而存在报错
|
||||||
* false - 未同步
|
|
||||||
*/
|
*/
|
||||||
private Boolean syncStatus;
|
private String menuKey;
|
||||||
|
/**
|
||||||
|
* 父菜单编号
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
// ========== 按钮操作 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按钮
|
* 按钮类型
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Button {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型
|
|
||||||
*
|
*
|
||||||
* 枚举 {@link MenuButtonType}
|
* 枚举 {@link MenuButtonType}
|
||||||
*/
|
*/
|
||||||
private String type;
|
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;
|
|
||||||
/**
|
/**
|
||||||
* 网页链接
|
* 网页链接
|
||||||
*
|
*
|
||||||
@ -106,71 +90,66 @@ public class MpMenuDO extends BaseDO {
|
|||||||
/**
|
/**
|
||||||
* 小程序的 appId
|
* 小程序的 appId
|
||||||
*
|
*
|
||||||
* 类型为 {@link WxConsts.XmlMsgType} 的 MINIPROGRAM
|
* 类型为 {@link MenuButtonType} 的 MINIPROGRAM
|
||||||
*/
|
*/
|
||||||
private String appId;
|
private String miniProgramAppId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序的页面路径
|
* 小程序的页面路径
|
||||||
*
|
*
|
||||||
* 类型为 {@link WxConsts.XmlMsgType} 的 MINIPROGRAM
|
* 类型为 {@link MenuButtonType} 的 MINIPROGRAM
|
||||||
*/
|
*/
|
||||||
private String pagePath;
|
private String miniProgramPagePath;
|
||||||
|
|
||||||
|
// ========== 消息内容 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息内容
|
* 消息类型
|
||||||
|
*
|
||||||
|
* 当 {@link #type} 为 CLICK、SCANCODE_WAITMSG
|
||||||
|
*
|
||||||
|
* 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS
|
||||||
|
*/
|
||||||
|
private String replyMessageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回复的消息内容
|
||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT
|
||||||
*/
|
*/
|
||||||
private String content;
|
private String replyContent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 媒体 id
|
* 回复的媒体 id
|
||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||||
*/
|
*/
|
||||||
private String mediaId;
|
private String replyMediaId;
|
||||||
/**
|
/**
|
||||||
* 媒体 URL
|
* 回复的媒体 URL
|
||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
|
||||||
*/
|
*/
|
||||||
private String mediaUrl;
|
private String replyMediaUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 回复的标题
|
* 回复的标题
|
||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
||||||
*/
|
*/
|
||||||
private String title;
|
private String replyTitle;
|
||||||
/**
|
/**
|
||||||
* 回复的描述
|
* 回复的描述
|
||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
|
||||||
*/
|
*/
|
||||||
private String description;
|
private String replyDescription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图文消息
|
* 回复的图文消息数组
|
||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
||||||
*/
|
*/
|
||||||
private MpMessageDO.Article article;
|
@TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class)
|
||||||
|
private List<MpMessageDO.Article> replyArticles;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @芋艿:可以找一些新的思路
|
|
||||||
public static class ButtonTypeHandler extends AbstractJsonTypeHandler<List<Button>> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<Button> parse(String json) {
|
|
||||||
return JsonUtils.parseArray(json, Button.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String toJson(List<Button> obj) {
|
|
||||||
return JsonUtils.toJsonString(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,7 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,6 +131,6 @@ public class MpAutoReplyDO extends BaseDO {
|
|||||||
*
|
*
|
||||||
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
* 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
@TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class)
|
||||||
private MpMessageDO.Article responseArticle;
|
private List<MpMessageDO.Article> responseArticles;
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ 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) {
|
default MpMenuDO selectByAppIdAndMenuKey(String appId, String menuKey) {
|
||||||
return selectOne(MpMenuDO::getAppId, appId);
|
return selectOne(MpMenuDO::getAppId, appId,
|
||||||
|
MpMenuDO::getMenuKey, menuKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
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.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 cn.iocoder.yudao.module.mp.service.message.MpMessageService;
|
||||||
|
import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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;
|
||||||
@ -37,6 +35,9 @@ import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class MpMenuServiceImpl implements MpMenuService {
|
public class MpMenuServiceImpl implements MpMenuService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MpMessageService mpMessageService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy // 延迟加载,避免循环引用报错
|
@Lazy // 延迟加载,避免循环引用报错
|
||||||
private MpServiceFactory mpServiceFactory;
|
private MpServiceFactory mpServiceFactory;
|
||||||
@ -87,47 +88,21 @@ public class MpMenuServiceImpl implements MpMenuService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WxMpXmlOutMessage reply(String appId, String key, String openid) {
|
public WxMpXmlOutMessage reply(String appId, String key, String openid) {
|
||||||
// 获得菜单
|
// 第一步,获得菜单
|
||||||
MpMenuDO menu = mpMenuMapper.selectByAppId(appId);
|
MpMenuDO menu = mpMenuMapper.selectByAppIdAndMenuKey(appId, key);
|
||||||
if (menu == null) {
|
if (menu == null) {
|
||||||
log.error("[reply][appId({}) 找不到对应的菜单]", appId);
|
log.error("[reply][appId({}) key({}) 找不到对应的菜单]", appId, key);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 匹配对应的按钮
|
|
||||||
MpMenuDO.Button button = getMenuButton(menu, key);
|
|
||||||
if (button == null) {
|
|
||||||
log.error("[reply][appId({}) key({}) 找不到对应的菜单按钮]", appId, key);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 按钮必须要有消息类型,不然后续无法回复消息
|
// 按钮必须要有消息类型,不然后续无法回复消息
|
||||||
if (StrUtil.isEmpty(button.getMessageType())) {
|
if (StrUtil.isEmpty(menu.getReplyMessageType())) {
|
||||||
log.error("[reply][appId({}) key({}) 不存在消息类型({})]", appId, key, button);
|
log.error("[reply][menu({}) 不存在对应的消息类型]", menu);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回复消息
|
// 第二步,回复消息
|
||||||
return null;
|
MpMessageSendOutReqBO sendReqBO = MpMenuConvert.INSTANCE.convert(openid, menu);
|
||||||
}
|
return mpMessageService.sendOutMessage(sendReqBO);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import me.chanjar.weixin.common.api.WxConsts;
|
|||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公众号消息发送 Request BO
|
* 公众号消息发送 Request BO
|
||||||
@ -85,6 +86,6 @@ public class MpMessageSendOutReqBO {
|
|||||||
*/
|
*/
|
||||||
@Valid
|
@Valid
|
||||||
@NotNull(message = "图文消息不能为空", groups = NewsGroup.class)
|
@NotNull(message = "图文消息不能为空", groups = NewsGroup.class)
|
||||||
private MpMessageDO.Article article;
|
private List<MpMessageDO.Article> articles;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user