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