Commit 68ef11ee8723013380a79c3c2c41e4007caddd1a

Authored by YunaiV
1 parent 6fc0b3fc

mp:完善微信公众号的消息

Showing 52 changed files with 378 additions and 494 deletions
yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java
@@ -15,8 +15,8 @@ public interface ErrorCodeConstants { @@ -15,8 +15,8 @@ public interface ErrorCodeConstants {
15 ErrorCode ACCOUNT_CLEAR_QUOTA_FAIL = new ErrorCode(1006000001, "清空公众号的 API 配额失败,原因:{}"); 15 ErrorCode ACCOUNT_CLEAR_QUOTA_FAIL = new ErrorCode(1006000001, "清空公众号的 API 配额失败,原因:{}");
16 16
17 // ========== 公众号统计 1006001000============ 17 // ========== 公众号统计 1006001000============
18 - ErrorCode STATISTICS_GET_USER_SUMMARY_FAIL = new ErrorCode(1006001000, "获取用户增减数据失败,原因:{}");  
19 - ErrorCode STATISTICS_GET_USER_CUMULATE_FAIL = new ErrorCode(1006001001, "获得用户累计数据失败,原因:{}"); 18 + ErrorCode STATISTICS_GET_USER_SUMMARY_FAIL = new ErrorCode(1006001000, "获取粉丝增减数据失败,原因:{}");
  19 + ErrorCode STATISTICS_GET_USER_CUMULATE_FAIL = new ErrorCode(1006001001, "获得粉丝累计数据失败,原因:{}");
20 ErrorCode STATISTICS_GET_UPSTREAM_MESSAGE_FAIL = new ErrorCode(1006001002, "获得消息发送概况数据失败,原因:{}"); 20 ErrorCode STATISTICS_GET_UPSTREAM_MESSAGE_FAIL = new ErrorCode(1006001002, "获得消息发送概况数据失败,原因:{}");
21 ErrorCode STATISTICS_GET_INTERFACE_SUMMARY_FAIL = new ErrorCode(1006001003, "获得接口分析数据失败,原因:{}"); 21 ErrorCode STATISTICS_GET_INTERFACE_SUMMARY_FAIL = new ErrorCode(1006001003, "获得接口分析数据失败,原因:{}");
22 22
@@ -28,8 +28,8 @@ public interface ErrorCodeConstants { @@ -28,8 +28,8 @@ public interface ErrorCodeConstants {
28 ErrorCode TAG_GET_FAIL = new ErrorCode(1006002001, "获得标签失败,原因:{}"); 28 ErrorCode TAG_GET_FAIL = new ErrorCode(1006002001, "获得标签失败,原因:{}");
29 29
30 // ========== 公众号粉丝 1006003000============ 30 // ========== 公众号粉丝 1006003000============
31 - ErrorCode USER_NOT_EXISTS = new ErrorCode(1006003000, "用户不存在");  
32 - ErrorCode USER_UPDATE_TAG_FAIL = new ErrorCode(1006003001, "更新用户标签失败,原因:{}"); 31 + ErrorCode USER_NOT_EXISTS = new ErrorCode(1006003000, "粉丝不存在");
  32 + ErrorCode USER_UPDATE_TAG_FAIL = new ErrorCode(1006003001, "更新粉丝标签失败,原因:{}");
33 33
34 // ========== 公众号素材 1006004000============ 34 // ========== 公众号素材 1006004000============
35 ErrorCode MATERIAL_NOT_EXISTS = new ErrorCode(1006004000, "素材不存在"); 35 ErrorCode MATERIAL_NOT_EXISTS = new ErrorCode(1006004000, "素材不存在");
yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/message/MpMessageSendFromEnum.java
@@ -12,8 +12,8 @@ import lombok.Getter; @@ -12,8 +12,8 @@ import lombok.Getter;
12 @AllArgsConstructor 12 @AllArgsConstructor
13 public enum MpMessageSendFromEnum { 13 public enum MpMessageSendFromEnum {
14 14
15 - USER_TO_MP(1, "用户发送给公众号"),  
16 - MP_TO_USER(2, "公众号发给用户"), 15 + USER_TO_MP(1, "粉丝发送给公众号"),
  16 + MP_TO_USER(2, "公众号发给粉丝"),
17 ; 17 ;
18 18
19 /** 19 /**
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuBaseVO.java
@@ -14,7 +14,7 @@ import java.util.List; @@ -14,7 +14,7 @@ import java.util.List;
14 import static cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*; 14 import static cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*;
15 15
16 /** 16 /**
17 - * 微信菜单 Base VO,提供给添加、修改、详细的子 VO 使用 17 + * 公众号菜单 Base VO,提供给添加、修改、详细的子 VO 使用
18 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 18 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
19 */ 19 */
20 @Data 20 @Data
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuRespVO.java
1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo; 1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
2 2
3 -import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;  
4 import io.swagger.annotations.ApiModel; 3 import io.swagger.annotations.ApiModel;
5 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
6 import lombok.Data; 5 import lombok.Data;
7 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
8 import lombok.ToString; 7 import lombok.ToString;
9 8
10 -import javax.validation.constraints.NotNull;  
11 import java.util.Date; 9 import java.util.Date;
12 10
13 -// TODO swagger 文档  
14 -@ApiModel("管理后台 - 微信菜单 Response VO") 11 +@ApiModel("管理后台 - 公众号菜单 Response VO")
15 @Data 12 @Data
16 @EqualsAndHashCode(callSuper = true) 13 @EqualsAndHashCode(callSuper = true)
17 @ToString(callSuper = true) 14 @ToString(callSuper = true)
18 public class MpMenuRespVO extends MpMenuBaseVO { 15 public class MpMenuRespVO extends MpMenuBaseVO {
19 16
20 - @ApiModelProperty(value = "主键", required = true) 17 + @ApiModelProperty(value = "主键", required = true, example = "1024")
21 private Long id; 18 private Long id;
22 19
23 @ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048") 20 @ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048")
24 - @NotNull(message = "公众号账号的编号不能为空")  
25 private Long accountId; 21 private Long accountId;
26 22
27 - /**  
28 - * 微信公众号 appid  
29 - *  
30 - * 冗余 {@link MpAccountDO#getAppId()}  
31 - */ 23 + @ApiModelProperty(value = "公众号 appId", required = true, example = "wx1234567890ox")
32 private String appId; 24 private String appId;
33 25
34 @ApiModelProperty(value = "创建时间", required = true) 26 @ApiModelProperty(value = "创建时间", required = true)
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuSaveReqVO.java
@@ -9,7 +9,6 @@ import javax.validation.constraints.NotEmpty; @@ -9,7 +9,6 @@ import javax.validation.constraints.NotEmpty;
9 import javax.validation.constraints.NotNull; 9 import javax.validation.constraints.NotNull;
10 import java.util.List; 10 import java.util.List;
11 11
12 -// TODO 芋艿:swagger 文档  
13 @ApiModel("管理后台 - 公众号菜单保存 Request VO") 12 @ApiModel("管理后台 - 公众号菜单保存 Request VO")
14 @Data 13 @Data
15 public class MpMenuSaveReqVO { 14 public class MpMenuSaveReqVO {
@@ -22,9 +21,13 @@ public class MpMenuSaveReqVO { @@ -22,9 +21,13 @@ public class MpMenuSaveReqVO {
22 @Valid 21 @Valid
23 private List<Menu> menus; 22 private List<Menu> menus;
24 23
  24 + @ApiModel("管理后台 - 公众号菜单保存时的每个菜单")
25 @Data 25 @Data
26 public static class Menu extends MpMenuBaseVO { 26 public static class Menu extends MpMenuBaseVO {
27 27
  28 + /**
  29 + * 子菜单数组
  30 + */
28 private List<Menu> children; 31 private List<Menu> children;
29 32
30 } 33 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.java
@@ -40,7 +40,7 @@ public class MpAutoReplyController { @@ -40,7 +40,7 @@ public class MpAutoReplyController {
40 40
41 @GetMapping("/get") 41 @GetMapping("/get")
42 @ApiOperation("获得公众号自动回复") 42 @ApiOperation("获得公众号自动回复")
43 - @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024") 43 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
44 @PreAuthorize("@ss.hasPermission('mp:auto-reply:query')") 44 @PreAuthorize("@ss.hasPermission('mp:auto-reply:query')")
45 public CommonResult<MpAutoReplyRespVO> getAutoReply(@RequestParam("id") Long id) { 45 public CommonResult<MpAutoReplyRespVO> getAutoReply(@RequestParam("id") Long id) {
46 MpAutoReplyDO autoReply = mpAutoReplyService.getAutoReply(id); 46 MpAutoReplyDO autoReply = mpAutoReplyService.getAutoReply(id);
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.java
@@ -29,10 +29,10 @@ public class MpMessageController { @@ -29,10 +29,10 @@ public class MpMessageController {
29 private MpMessageService mpMessageService; 29 private MpMessageService mpMessageService;
30 30
31 @GetMapping("/page") 31 @GetMapping("/page")
32 - @ApiOperation("获得粉丝消息分页") 32 + @ApiOperation("获得公众号消息分页")
33 @PreAuthorize("@ss.hasPermission('mp:message:query')") 33 @PreAuthorize("@ss.hasPermission('mp:message:query')")
34 - public CommonResult<PageResult<MpMessageRespVO>> getWxFansMsgPage(@Valid MpMessagePageReqVO pageVO) {  
35 - PageResult<MpMessageDO> pageResult = mpMessageService.getWxFansMsgPage(pageVO); 34 + public CommonResult<PageResult<MpMessageRespVO>> getMessagePage(@Valid MpMessagePageReqVO pageVO) {
  35 + PageResult<MpMessageDO> pageResult = mpMessageService.getMessagePage(pageVO);
36 return success(MpMessageConvert.INSTANCE.convertPage(pageResult)); 36 return success(MpMessageConvert.INSTANCE.convertPage(pageResult));
37 } 37 }
38 38
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/autoreply/MpAutoReplyCreateReqVO.java
@@ -14,8 +14,8 @@ import javax.validation.constraints.NotNull; @@ -14,8 +14,8 @@ import javax.validation.constraints.NotNull;
14 @ToString(callSuper = true) 14 @ToString(callSuper = true)
15 public class MpAutoReplyCreateReqVO extends MpAutoReplyBaseVO { 15 public class MpAutoReplyCreateReqVO extends MpAutoReplyBaseVO {
16 16
17 - @ApiModelProperty(value = "微信公众号 ID", required = true, example = "1024")  
18 - @NotNull(message = "微信公众号 ID不能为空") 17 + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "1024")
  18 + @NotNull(message = "公众号账号的编号不能为空")
19 private Long accountId; 19 private Long accountId;
20 20
21 } 21 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/autoreply/MpAutoReplyRespVO.java
@@ -17,9 +17,9 @@ public class MpAutoReplyRespVO extends MpAutoReplyBaseVO { @@ -17,9 +17,9 @@ public class MpAutoReplyRespVO extends MpAutoReplyBaseVO {
17 @ApiModelProperty(value = "主键", required = true, example = "1024") 17 @ApiModelProperty(value = "主键", required = true, example = "1024")
18 private Long id; 18 private Long id;
19 19
20 - @ApiModelProperty(value = "微信公众号 ID", required = true, example = "1024") 20 + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "1024")
21 private Long accountId; 21 private Long accountId;
22 - @ApiModelProperty(value = "微信公众号 appid", required = true, example = "wx1234567890") 22 + @ApiModelProperty(value = "公众号 appId", required = true, example = "wx1234567890")
23 private String appId; 23 private String appId;
24 24
25 @ApiModelProperty(value = "创建时间", required = true) 25 @ApiModelProperty(value = "创建时间", required = true)
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/message/MpMessageBaseVO.java deleted
1 -package cn.iocoder.yudao.module.mp.controller.admin.message.vo.message;  
2 -  
3 -import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;  
4 -import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;  
5 -import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;  
6 -import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum;  
7 -import com.baomidou.mybatisplus.annotation.TableField;  
8 -import lombok.Data;  
9 -import me.chanjar.weixin.common.api.WxConsts;  
10 -  
11 -import java.util.List;  
12 -  
13 -// TODO 芋艿:VO 的注解  
14 -/**  
15 - * 粉丝消息 Base VO,提供给添加、修改、详细的子 VO 使用  
16 - * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成  
17 - */  
18 -@Data  
19 -public class MpMessageBaseVO {  
20 -  
21 - /**  
22 - * 微信公众号消息 id  
23 - */  
24 - private Long msgId;  
25 - /**  
26 - * 微信公众号 ID  
27 - *  
28 - * 关联 {@link MpAccountDO#getId()}  
29 - */  
30 - private Long accountId;  
31 - /**  
32 - * 微信公众号 appid  
33 - *  
34 - * 冗余 {@link MpAccountDO#getAppId()}  
35 - */  
36 - private String appId;  
37 - /**  
38 - * 微信用户编号  
39 - *  
40 - * 关联 {@link MpUserDO#getId()}  
41 - */  
42 - private Long userId;  
43 - /**  
44 - * 用户标识  
45 - *  
46 - * 冗余 {@link MpUserDO#getOpenid()}  
47 - */  
48 - private String openid;  
49 -  
50 - /**  
51 - * 消息类型  
52 - *  
53 - * 枚举 {@link WxConsts.XmlMsgType}  
54 - */  
55 - private String type;  
56 - /**  
57 - * 消息来源  
58 - *  
59 - * 枚举 {@link MpMessageSendFromEnum}  
60 - */  
61 - private Integer sendFrom;  
62 -  
63 - // ========= 普通消息内容 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html  
64 -  
65 - /**  
66 - * 消息内容  
67 - *  
68 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT  
69 - */  
70 - private String content;  
71 -  
72 - /**  
73 - * 通过素材管理中的接口上传多媒体文件,得到的 id  
74 - *  
75 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO  
76 - */  
77 - private String mediaId;  
78 - /**  
79 - * 媒体文件的 URL  
80 - */  
81 - private String mediaUrl;  
82 - /**  
83 - * 语音识别后文本  
84 - *  
85 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VOICE  
86 - */  
87 - private String recognition;  
88 - /**  
89 - * 语音格式,如 amr,speex 等  
90 - *  
91 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VOICE  
92 - */  
93 - private String format;  
94 - /**  
95 - * 标题  
96 - *  
97 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC、LINK  
98 - */  
99 - private String title;  
100 - /**  
101 - * 描述  
102 - *  
103 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC  
104 - */  
105 - private String description;  
106 -  
107 - /**  
108 - * 缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id  
109 - *  
110 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO  
111 - */  
112 - private String thumbMediaId;  
113 - /**  
114 - * 缩略图的媒体 URL  
115 - *  
116 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO  
117 - */  
118 - private String thumbMediaUrl;  
119 -  
120 - /**  
121 - * 点击图文消息跳转链接  
122 - *  
123 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 LINK  
124 - */  
125 - private String url;  
126 -  
127 - /**  
128 - * 地理位置维度  
129 - *  
130 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION  
131 - */  
132 - private Double locationX;  
133 - /**  
134 - * 地理位置经度  
135 - *  
136 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION  
137 - */  
138 - private Double locationY;  
139 - /**  
140 - * 地图缩放大小  
141 - *  
142 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION  
143 - */  
144 - private Double scale;  
145 - /**  
146 - * 详细地址  
147 - *  
148 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION  
149 - *  
150 - * 例如说杨浦区黄兴路 221-4 号临  
151 - */  
152 - private String label;  
153 -  
154 - /**  
155 - * 图文消息数组  
156 - *  
157 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS  
158 - */  
159 - @TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class)  
160 - private List<MpMessageDO.Article> articles;  
161 -  
162 - /**  
163 - * 音乐链接  
164 - *  
165 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC  
166 - */  
167 - private String musicUrl;  
168 - /**  
169 - * 高质量音乐链接  
170 - *  
171 - * WIFI 环境优先使用该链接播放音乐  
172 - *  
173 - * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC  
174 - */  
175 - private String hqMusicUrl;  
176 -  
177 - // ========= 事件推送 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html  
178 -  
179 - /**  
180 - * 事件类型  
181 - *  
182 - * 枚举 {@link WxConsts.EventType}  
183 - */  
184 - private String event;  
185 - /**  
186 - * 事件 Key  
187 - *  
188 - * 1. {@link WxConsts.EventType} 的 SCAN:qrscene_ 为前缀,后面为二维码的参数值  
189 - * 2. {@link WxConsts.EventType} 的 CLICK:与自定义菜单接口中 KEY 值对应  
190 - */  
191 - private String eventKey;  
192 -  
193 -}  
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/message/MpMessagePageReqVO.java
@@ -4,23 +4,31 @@ import lombok.*; @@ -4,23 +4,31 @@ import lombok.*;
4 4
5 import io.swagger.annotations.*; 5 import io.swagger.annotations.*;
6 import cn.iocoder.yudao.framework.common.pojo.PageParam; 6 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
7 8
8 -@ApiModel("管理后台 - 粉丝消息分页 Request VO") 9 +import javax.validation.constraints.NotNull;
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@ApiModel("管理后台 - 公众号消息分页 Request VO")
9 @Data 15 @Data
10 @EqualsAndHashCode(callSuper = true) 16 @EqualsAndHashCode(callSuper = true)
11 @ToString(callSuper = true) 17 @ToString(callSuper = true)
12 public class MpMessagePageReqVO extends PageParam { 18 public class MpMessagePageReqVO extends PageParam {
13 19
14 - @ApiModelProperty(value = "用户标识")  
15 - private String openId;  
16 -  
17 - @ApiModelProperty(value = "昵称")  
18 - private String nickname;  
19 -  
20 - @ApiModelProperty(value = "微信账号ID") 20 + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "1024")
  21 + @NotNull(message = "公众号账号的编号不能为空")
21 private Long accountId; 22 private Long accountId;
22 23
23 - @ApiModelProperty(value = "消息类型") 24 + @ApiModelProperty(value = "消息类型", example = "text", notes = "参见 WxConsts.XmlMsgType 枚举")
24 private String type; 25 private String type;
25 26
  27 + @ApiModelProperty(value = "公众号粉丝标识", example = "o6_bmjrPTlm6_2sgVt7hMZOPfL2M")
  28 + private String openid;
  29 +
  30 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  31 + @ApiModelProperty(value = "创建时间")
  32 + private LocalDateTime[] createTime;
  33 +
26 } 34 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/message/MpMessageRespVO.java
1 package cn.iocoder.yudao.module.mp.controller.admin.message.vo.message; 1 package cn.iocoder.yudao.module.mp.controller.admin.message.vo.message;
2 2
  3 +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
  4 +import com.baomidou.mybatisplus.annotation.TableField;
3 import io.swagger.annotations.ApiModel; 5 import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 6 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 7 import lombok.Data;
6 -import lombok.EqualsAndHashCode;  
7 -import lombok.ToString; 8 +import me.chanjar.weixin.common.api.WxConsts;
8 9
9 import java.util.Date; 10 import java.util.Date;
  11 +import java.util.List;
10 12
11 -@ApiModel("管理后台 - 粉丝消息 Response VO") 13 +@ApiModel("管理后台 - 公众号消息 Response VO")
12 @Data 14 @Data
13 -@EqualsAndHashCode(callSuper = true)  
14 -@ToString(callSuper = true)  
15 -public class MpMessageRespVO extends MpMessageBaseVO { 15 +public class MpMessageRespVO {
16 16
17 - @ApiModelProperty(value = "主键", required = true) 17 + @ApiModelProperty(value = "主键", required = true, example = "1024")
18 private Integer id; 18 private Integer id;
19 19
  20 + @ApiModelProperty(value = "微信公众号消息 id", required = true, example = "23953173569869169")
  21 + private Long msgId;
  22 +
  23 + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "1")
  24 + private Long accountId;
  25 + @ApiModelProperty(value = "公众号账号的 appid", required = true, example = "wx1234567890")
  26 + private String appId;
  27 +
  28 + @ApiModelProperty(value = "公众号粉丝编号", required = true, example = "2048")
  29 + private Long userId;
  30 + @ApiModelProperty(value = "公众号粉丝标志", required = true, example = "o6_bmjrPTlm6_2sgVt7hMZOPfL2M")
  31 + private String openid;
  32 +
  33 + @ApiModelProperty(value = "消息类型", required = true, example = "text", notes = "参见 WxConsts.XmlMsgType 枚举")
  34 + private String type;
  35 + @ApiModelProperty(value = "消息来源", required = true, example = "1", notes = "参见 MpMessageSendFromEnum 枚举")
  36 + private Integer sendFrom;
  37 +
  38 + // ========= 普通消息内容 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
  39 +
  40 + @ApiModelProperty(value = "消息内容", example = "你好呀", notes = "消息类型为 text 时,才有值")
  41 + private String content;
  42 +
  43 + @ApiModelProperty(value = "媒体素材的编号", example = "1234567890", notes = "消息类型为 image、voice、video 时,才有值")
  44 + private String mediaId;
  45 + @ApiModelProperty(value = "媒体文件的 URL", example = "https://www.iocoder.cn/xxx.png",
  46 + notes = "消息类型为 image、voice、video 时,才有值")
  47 + private String mediaUrl;
  48 +
  49 + @ApiModelProperty(value = "语音识别后文本", example = "语音识别后文本", notes = "消息类型为 voice 时,才有值")
  50 + private String recognition;
  51 + @ApiModelProperty(value = "语音格式", example = "amr", notes = "消息类型为 voice 时,才有值")
  52 + private String format;
  53 +
  54 + @ApiModelProperty(value = "标题", example = "我是标题", notes = "消息类型为 video、music、link 时,才有值")
  55 + private String title;
  56 +
  57 + @ApiModelProperty(value = "描述", example = "我是描述", notes = "消息类型为 video、music 时,才有值")
  58 + private String description;
  59 +
  60 + @ApiModelProperty(value = "缩略图的媒体 id", example = "1234567890", notes = "消息类型为 video、music 时,才有值")
  61 + private String thumbMediaId;
  62 + @ApiModelProperty(value = "缩略图的媒体 URL", example = "https://www.iocoder.cn/xxx.png",
  63 + notes = "消息类型为 video、music 时,才有值")
  64 + private String thumbMediaUrl;
  65 +
  66 + @ApiModelProperty(value = "点击图文消息跳转链接", example = "https://www.iocoder.cn", notes = "消息类型为 link 时,才有值")
  67 + private String url;
  68 +
  69 + @ApiModelProperty(value = "地理位置维度", example = "23.137466", notes = "消息类型为 location 时,才有值")
  70 + private Double locationX;
  71 +
  72 + @ApiModelProperty(value = "地理位置经度", example = "113.352425", notes = "消息类型为 location 时,才有值")
  73 + private Double locationY;
  74 +
  75 + @ApiModelProperty(value = "地图缩放大小", example = "13", notes = "消息类型为 location 时,才有值")
  76 + private Double scale;
  77 +
  78 + @ApiModelProperty(value = "详细地址", example = "杨浦区黄兴路 221-4 号临", notes = "消息类型为 location 时,才有值")
  79 + private String label;
  80 +
  81 + /**
  82 + * 图文消息数组
  83 + *
  84 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
  85 + */
  86 + @TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class)
  87 + private List<MpMessageDO.Article> articles;
  88 +
  89 + @ApiModelProperty(value = "音乐链接", example = "https://www.iocoder.cn/xxx.mp3", notes = "消息类型为 music 时,才有值")
  90 + private String musicUrl;
  91 + @ApiModelProperty(value = "高质量音乐链接", example = "https://www.iocoder.cn/xxx.mp3", notes = "消息类型为 music 时,才有值")
  92 + private String hqMusicUrl;
  93 +
  94 + // ========= 事件推送 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
  95 +
  96 + @ApiModelProperty(value = "事件类型", example = "subscribe", notes = "参见 WxConsts.EventType 枚举")
  97 + private String event;
  98 + @ApiModelProperty(value = "事件 Key", example = "qrscene_123456", notes = "参见 WxConsts.EventType 枚举")
  99 + private String eventKey;
  100 +
20 @ApiModelProperty(value = "创建时间", required = true) 101 @ApiModelProperty(value = "创建时间", required = true)
21 private Date createTime; 102 private Date createTime;
22 103
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/vo/MpOpenHandleMessageReqVO.java
@@ -4,7 +4,6 @@ package cn.iocoder.yudao.module.mp.controller.admin.open.vo; @@ -4,7 +4,6 @@ package cn.iocoder.yudao.module.mp.controller.admin.open.vo;
4 import io.swagger.annotations.ApiModel; 4 import io.swagger.annotations.ApiModel;
5 import io.swagger.annotations.ApiModelProperty; 5 import io.swagger.annotations.ApiModelProperty;
6 import lombok.Data; 6 import lombok.Data;
7 -import org.springframework.web.bind.annotation.RequestParam;  
8 7
9 import javax.validation.constraints.NotEmpty; 8 import javax.validation.constraints.NotEmpty;
10 9
@@ -26,8 +25,8 @@ public class MpOpenHandleMessageReqVO { @@ -26,8 +25,8 @@ public class MpOpenHandleMessageReqVO {
26 @NotEmpty(message = "随机数不能为空") 25 @NotEmpty(message = "随机数不能为空")
27 private String nonce; 26 private String nonce;
28 27
29 - @ApiModelProperty(value = "用户 openid", required = true, example = "oz-Jdtyn-WGm4C4I5Z-nvBMO_ZfY")  
30 - @NotEmpty(message = "用户 openid 不能为空") 28 + @ApiModelProperty(value = "粉丝 openid", required = true, example = "oz-Jdtyn-WGm4C4I5Z-nvBMO_ZfY")
  29 + @NotEmpty(message = "粉丝 openid 不能为空")
31 private String openid; 30 private String openid;
32 31
33 @ApiModelProperty(value = "消息加密类型", example = "aes") 32 @ApiModelProperty(value = "消息加密类型", example = "aes")
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/statistics/MpStatisticsController.java
@@ -31,7 +31,7 @@ public class MpStatisticsController { @@ -31,7 +31,7 @@ public class MpStatisticsController {
31 private MpStatisticsService mpStatisticsService; 31 private MpStatisticsService mpStatisticsService;
32 32
33 @GetMapping("/user-summary") 33 @GetMapping("/user-summary")
34 - @ApiOperation("获得用户增减数据") 34 + @ApiOperation("获得粉丝增减数据")
35 @PreAuthorize("@ss.hasPermission('mp:statistics:query')") 35 @PreAuthorize("@ss.hasPermission('mp:statistics:query')")
36 public CommonResult<List<MpStatisticsUserSummaryRespVO>> getUserSummary(MpStatisticsGetReqVO getReqVO) { 36 public CommonResult<List<MpStatisticsUserSummaryRespVO>> getUserSummary(MpStatisticsGetReqVO getReqVO) {
37 List<WxDataCubeUserSummary> list = mpStatisticsService.getUserSummary( 37 List<WxDataCubeUserSummary> list = mpStatisticsService.getUserSummary(
@@ -40,7 +40,7 @@ public class MpStatisticsController { @@ -40,7 +40,7 @@ public class MpStatisticsController {
40 } 40 }
41 41
42 @GetMapping("/user-cumulate") 42 @GetMapping("/user-cumulate")
43 - @ApiOperation("获得用户累计数据") 43 + @ApiOperation("获得粉丝累计数据")
44 @PreAuthorize("@ss.hasPermission('mp:statistics:query')") 44 @PreAuthorize("@ss.hasPermission('mp:statistics:query')")
45 public CommonResult<List<MpStatisticsUserCumulateRespVO>> getUserCumulate(MpStatisticsGetReqVO getReqVO) { 45 public CommonResult<List<MpStatisticsUserCumulateRespVO>> getUserCumulate(MpStatisticsGetReqVO getReqVO) {
46 List<WxDataCubeUserCumulate> list = mpStatisticsService.getUserCumulate( 46 List<WxDataCubeUserCumulate> list = mpStatisticsService.getUserCumulate(
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/statistics/vo/MpStatisticsInterfaceSummaryRespVO.java
@@ -13,7 +13,7 @@ public class MpStatisticsInterfaceSummaryRespVO { @@ -13,7 +13,7 @@ public class MpStatisticsInterfaceSummaryRespVO {
13 @ApiModelProperty(value = "日期", required = true) 13 @ApiModelProperty(value = "日期", required = true)
14 private Date refDate; 14 private Date refDate;
15 15
16 - @ApiModelProperty(value = "通过服务器配置地址获得消息后,被动回复用户消息的次数", required = true, example = "10") 16 + @ApiModelProperty(value = "通过服务器配置地址获得消息后,被动回复粉丝消息的次数", required = true, example = "10")
17 private Integer callbackCount; 17 private Integer callbackCount;
18 18
19 @ApiModelProperty(value = "上述动作的失败次数", required = true, example = "20") 19 @ApiModelProperty(value = "上述动作的失败次数", required = true, example = "20")
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/statistics/vo/MpStatisticsUpstreamMessageRespVO.java
@@ -6,14 +6,14 @@ import lombok.Data; @@ -6,14 +6,14 @@ import lombok.Data;
6 6
7 import java.util.Date; 7 import java.util.Date;
8 8
9 -@ApiModel("管理后台 - 某一天的用户增减数据 Response VO") 9 +@ApiModel("管理后台 - 某一天的粉丝增减数据 Response VO")
10 @Data 10 @Data
11 public class MpStatisticsUpstreamMessageRespVO { 11 public class MpStatisticsUpstreamMessageRespVO {
12 12
13 @ApiModelProperty(value = "日期", required = true) 13 @ApiModelProperty(value = "日期", required = true)
14 private Date refDate; 14 private Date refDate;
15 15
16 - @ApiModelProperty(value = "上行发送了(向公众号发送了)消息的用户数", required = true, example = "10") 16 + @ApiModelProperty(value = "上行发送了(向公众号发送了)消息的粉丝数", required = true, example = "10")
17 private Integer messageUser; 17 private Integer messageUser;
18 18
19 @ApiModelProperty(value = "上行发送了消息的消息总数", required = true, example = "20") 19 @ApiModelProperty(value = "上行发送了消息的消息总数", required = true, example = "20")
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/statistics/vo/MpStatisticsUserCumulateRespVO.java
@@ -13,7 +13,7 @@ public class MpStatisticsUserCumulateRespVO { @@ -13,7 +13,7 @@ public class MpStatisticsUserCumulateRespVO {
13 @ApiModelProperty(value = "日期", required = true) 13 @ApiModelProperty(value = "日期", required = true)
14 private Date refDate; 14 private Date refDate;
15 15
16 - @ApiModelProperty(value = "累计用户量", required = true, example = "10") 16 + @ApiModelProperty(value = "累计粉丝量", required = true, example = "10")
17 private Integer cumulateUser; 17 private Integer cumulateUser;
18 18
19 } 19 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/statistics/vo/MpStatisticsUserSummaryRespVO.java
@@ -6,20 +6,20 @@ import lombok.Data; @@ -6,20 +6,20 @@ import lombok.Data;
6 6
7 import java.util.Date; 7 import java.util.Date;
8 8
9 -@ApiModel("管理后台 - 某一天的用户增减数据 Response VO") 9 +@ApiModel("管理后台 - 某一天的粉丝增减数据 Response VO")
10 @Data 10 @Data
11 public class MpStatisticsUserSummaryRespVO { 11 public class MpStatisticsUserSummaryRespVO {
12 12
13 @ApiModelProperty(value = "日期", required = true) 13 @ApiModelProperty(value = "日期", required = true)
14 private Date refDate; 14 private Date refDate;
15 15
16 - @ApiModelProperty(value = "用户来源", required = true, example = "0") 16 + @ApiModelProperty(value = "粉丝来源", required = true, example = "0")
17 private Integer userSource; 17 private Integer userSource;
18 18
19 - @ApiModelProperty(value = "新关注的用户数量", required = true, example = "10") 19 + @ApiModelProperty(value = "新关注的粉丝数量", required = true, example = "10")
20 private Integer newUser; 20 private Integer newUser;
21 21
22 - @ApiModelProperty(value = "取消关注的用户数量", required = true, example = "20") 22 + @ApiModelProperty(value = "取消关注的粉丝数量", required = true, example = "20")
23 private Integer cancelUser; 23 private Integer cancelUser;
24 24
25 } 25 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/vo/MpUserPageReqVO.java
@@ -19,10 +19,10 @@ public class MpUserPageReqVO extends PageParam { @@ -19,10 +19,10 @@ public class MpUserPageReqVO extends PageParam {
19 @NotNull(message = "公众号账号的编号不能为空") 19 @NotNull(message = "公众号账号的编号不能为空")
20 private Long accountId; 20 private Long accountId;
21 21
22 - @ApiModelProperty(value = "公众号用户标识", example = "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", notes = "模糊匹配") 22 + @ApiModelProperty(value = "公众号粉丝标识", example = "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", notes = "模糊匹配")
23 private String openid; 23 private String openid;
24 24
25 - @ApiModelProperty(value = "公众号用户昵称", example = "芋艿", notes = "模糊匹配") 25 + @ApiModelProperty(value = "公众号粉丝昵称", example = "芋艿", notes = "模糊匹配")
26 private String nickname; 26 private String nickname;
27 27
28 } 28 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/vo/MpUserRespVO.java
@@ -15,7 +15,7 @@ public class MpUserRespVO { @@ -15,7 +15,7 @@ public class MpUserRespVO {
15 @ApiModelProperty(value = "编号", required = true, example = "1024") 15 @ApiModelProperty(value = "编号", required = true, example = "1024")
16 private Long id; 16 private Long id;
17 17
18 - @ApiModelProperty(value = "公众号用户标识", required = true, example = "o6_bmjrPTlm6_2sgVt7hMZOPfL2M") 18 + @ApiModelProperty(value = "公众号粉丝标识", required = true, example = "o6_bmjrPTlm6_2sgVt7hMZOPfL2M")
19 private String openid; 19 private String openid;
20 20
21 @ApiModelProperty(value = "关注状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") 21 @ApiModelProperty(value = "关注状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举")
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/material/MpMaterialDO.java
@@ -32,13 +32,13 @@ public class MpMaterialDO extends BaseDO { @@ -32,13 +32,13 @@ public class MpMaterialDO extends BaseDO {
32 @TableId 32 @TableId
33 private Long id; 33 private Long id;
34 /** 34 /**
35 - * 微信公众号 ID 35 + * 公众号账号的编号
36 * 36 *
37 * 关联 {@link MpAccountDO#getId()} 37 * 关联 {@link MpAccountDO#getId()}
38 */ 38 */
39 private Long accountId; 39 private Long accountId;
40 /** 40 /**
41 - * 微信公众号 appid 41 + * 公众号 appId
42 * 42 *
43 * 冗余 {@link MpAccountDO#getAppId()} 43 * 冗余 {@link MpAccountDO#getAppId()}
44 */ 44 */
@@ -71,8 +71,8 @@ public class MpMaterialDO extends BaseDO { @@ -71,8 +71,8 @@ public class MpMaterialDO extends BaseDO {
71 * 71 *
72 * 永久素材:非空 72 * 永久素材:非空
73 * 临时素材:可能为空。 73 * 临时素材:可能为空。
74 - * 1. 为空的情况:用户主动发送的图片、语音等  
75 - * 2. 非空的情况:主动发送给用户的图片、语音等 74 + * 1. 为空的情况:粉丝主动发送的图片、语音等
  75 + * 2. 非空的情况:主动发送给粉丝的图片、语音等
76 */ 76 */
77 private String name; 77 private String name;
78 78
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/menu/MpMenuDO.java
@@ -16,7 +16,7 @@ import me.chanjar.weixin.common.api.WxConsts.MenuButtonType; @@ -16,7 +16,7 @@ import me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
16 import java.util.List; 16 import java.util.List;
17 17
18 /** 18 /**
19 - * 微信菜单 DO 19 + * 公众号菜单 DO
20 * 20 *
21 * @author 芋道源码 21 * @author 芋道源码
22 */ 22 */
@@ -38,13 +38,13 @@ public class MpMenuDO extends BaseDO { @@ -38,13 +38,13 @@ public class MpMenuDO extends BaseDO {
38 @TableId 38 @TableId
39 private Long id; 39 private Long id;
40 /** 40 /**
41 - * 微信公众号 ID 41 + * 公众号账号的编号
42 * 42 *
43 * 关联 {@link MpAccountDO#getId()} 43 * 关联 {@link MpAccountDO#getId()}
44 */ 44 */
45 private Long accountId; 45 private Long accountId;
46 /** 46 /**
47 - * 微信公众号 appid 47 + * 公众号 appId
48 * 48 *
49 * 冗余 {@link MpAccountDO#getAppId()} 49 * 冗余 {@link MpAccountDO#getAppId()}
50 */ 50 */
@@ -77,7 +77,7 @@ public class MpMenuDO extends BaseDO { @@ -77,7 +77,7 @@ public class MpMenuDO extends BaseDO {
77 /** 77 /**
78 * 网页链接 78 * 网页链接
79 * 79 *
80 - * 用户点击菜单可打开链接,不超过 1024 字节 80 + * 粉丝点击菜单可打开链接,不超过 1024 字节
81 * 81 *
82 * 类型为 {@link WxConsts.XmlMsgType} 的 VIEW、MINIPROGRAM 82 * 类型为 {@link WxConsts.XmlMsgType} 的 VIEW、MINIPROGRAM
83 */ 83 */
@@ -146,13 +146,13 @@ public class MpMenuDO extends BaseDO { @@ -146,13 +146,13 @@ public class MpMenuDO extends BaseDO {
146 private String replyDescription; 146 private String replyDescription;
147 147
148 /** 148 /**
149 - * 缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id 149 + * 回复的缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id
150 * 150 *
151 * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO 151 * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
152 */ 152 */
153 private String replyThumbMediaId; 153 private String replyThumbMediaId;
154 /** 154 /**
155 - * 缩略图的媒体 URL 155 + * 回复的缩略图的媒体 URL
156 * 156 *
157 * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO 157 * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
158 */ 158 */
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpAutoReplyDO.java
@@ -40,13 +40,13 @@ public class MpAutoReplyDO extends BaseDO { @@ -40,13 +40,13 @@ public class MpAutoReplyDO extends BaseDO {
40 @TableId 40 @TableId
41 private Long id; 41 private Long id;
42 /** 42 /**
43 - * 微信公众号 ID 43 + * 公众号账号的编号
44 * 44 *
45 * 关联 {@link MpAccountDO#getId()} 45 * 关联 {@link MpAccountDO#getId()}
46 */ 46 */
47 private Long accountId; 47 private Long accountId;
48 /** 48 /**
49 - * 微信公众号 appid 49 + * 公众号 appId
50 * 50 *
51 * 冗余 {@link MpAccountDO#getAppId()} 51 * 冗余 {@link MpAccountDO#getAppId()}
52 */ 52 */
@@ -126,6 +126,19 @@ public class MpAutoReplyDO extends BaseDO { @@ -126,6 +126,19 @@ public class MpAutoReplyDO extends BaseDO {
126 private String responseDescription; 126 private String responseDescription;
127 127
128 /** 128 /**
  129 + * 回复的缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id
  130 + *
  131 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
  132 + */
  133 + private String responseThumbMediaId;
  134 + /**
  135 + * 回复的缩略图的媒体 URL
  136 + *
  137 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC、VIDEO
  138 + */
  139 + private String responseThumbMediaUrl;
  140 +
  141 + /**
129 * 回复的图文消息 142 * 回复的图文消息
130 * 143 *
131 * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS 144 * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
@@ -133,4 +146,19 @@ public class MpAutoReplyDO extends BaseDO { @@ -133,4 +146,19 @@ public class MpAutoReplyDO extends BaseDO {
133 @TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class) 146 @TableField(typeHandler = MpMessageDO.ArticleTypeHandler.class)
134 private List<MpMessageDO.Article> responseArticles; 147 private List<MpMessageDO.Article> responseArticles;
135 148
  149 + /**
  150 + * 回复的音乐链接
  151 + *
  152 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
  153 + */
  154 + private String responseMusicUrl;
  155 + /**
  156 + * 回复的高质量音乐链接
  157 + *
  158 + * WIFI 环境优先使用该链接播放音乐
  159 + *
  160 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC
  161 + */
  162 + private String responseHqMusicUrl;
  163 +
136 } 164 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java
@@ -40,25 +40,25 @@ public class MpMessageDO extends BaseDO { @@ -40,25 +40,25 @@ public class MpMessageDO extends BaseDO {
40 */ 40 */
41 private Long msgId; 41 private Long msgId;
42 /** 42 /**
43 - * 微信公众号 ID 43 + * 公众号账号的 ID
44 * 44 *
45 * 关联 {@link MpAccountDO#getId()} 45 * 关联 {@link MpAccountDO#getId()}
46 */ 46 */
47 private Long accountId; 47 private Long accountId;
48 /** 48 /**
49 - * 微信公众号 appid 49 + * 公众号 appid
50 * 50 *
51 * 冗余 {@link MpAccountDO#getAppId()} 51 * 冗余 {@link MpAccountDO#getAppId()}
52 */ 52 */
53 private String appId; 53 private String appId;
54 /** 54 /**
55 - * 微信用户编号 55 + * 公众号粉丝的编号
56 * 56 *
57 * 关联 {@link MpUserDO#getId()} 57 * 关联 {@link MpUserDO#getId()}
58 */ 58 */
59 private Long userId; 59 private Long userId;
60 /** 60 /**
61 - * 用户标识 61 + * 公众号粉丝标志
62 * 62 *
63 * 冗余 {@link MpUserDO#getOpenid()} 63 * 冗余 {@link MpUserDO#getOpenid()}
64 */ 64 */
@@ -87,7 +87,7 @@ public class MpMessageDO extends BaseDO { @@ -87,7 +87,7 @@ public class MpMessageDO extends BaseDO {
87 private String content; 87 private String content;
88 88
89 /** 89 /**
90 - * 通过素材管理中的接口上传多媒体文件,得到的 id 90 + * 媒体文件的编号
91 * 91 *
92 * 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO 92 * 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
93 */ 93 */
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/tag/MpTagDO.java
@@ -43,13 +43,13 @@ public class MpTagDO extends BaseDO { @@ -43,13 +43,13 @@ public class MpTagDO extends BaseDO {
43 private Integer count; 43 private Integer count;
44 44
45 /** 45 /**
46 - * 微信公众号 ID 46 + * 公众号账号的编号
47 * 47 *
48 * 关联 {@link MpAccountDO#getId()} 48 * 关联 {@link MpAccountDO#getId()}
49 */ 49 */
50 private Long accountId; 50 private Long accountId;
51 /** 51 /**
52 - * 微信公众号 appid 52 + * 公众号 appId
53 * 53 *
54 * 冗余 {@link MpAccountDO#getAppId()} 54 * 冗余 {@link MpAccountDO#getAppId()}
55 */ 55 */
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/user/MpUserDO.java
@@ -35,7 +35,7 @@ public class MpUserDO extends BaseDO { @@ -35,7 +35,7 @@ public class MpUserDO extends BaseDO {
35 @TableId 35 @TableId
36 private Long id; 36 private Long id;
37 /** 37 /**
38 - * 用户标识 38 + * 粉丝标识
39 */ 39 */
40 private String openid; 40 private String openid;
41 /** 41 /**
@@ -95,13 +95,13 @@ public class MpUserDO extends BaseDO { @@ -95,13 +95,13 @@ public class MpUserDO extends BaseDO {
95 private List<Long> tagIds; 95 private List<Long> tagIds;
96 96
97 /** 97 /**
98 - * 微信公众号 ID 98 + * 公众号账号的编号
99 * 99 *
100 * 关联 {@link MpAccountDO#getId()} 100 * 关联 {@link MpAccountDO#getId()}
101 */ 101 */
102 private Long accountId; 102 private Long accountId;
103 /** 103 /**
104 - * 微信公众号 appid 104 + * 公众号 appId
105 * 105 *
106 * 冗余 {@link MpAccountDO#getAppId()} 106 * 冗余 {@link MpAccountDO#getAppId()}
107 */ 107 */
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/message/MpMessageMapper.java
@@ -13,9 +13,9 @@ public interface MpMessageMapper extends BaseMapperX&lt;MpMessageDO&gt; { @@ -13,9 +13,9 @@ public interface MpMessageMapper extends BaseMapperX&lt;MpMessageDO&gt; {
13 default PageResult<MpMessageDO> selectPage(MpMessagePageReqVO reqVO) { 13 default PageResult<MpMessageDO> selectPage(MpMessagePageReqVO reqVO) {
14 return selectPage(reqVO, new LambdaQueryWrapperX<MpMessageDO>() 14 return selectPage(reqVO, new LambdaQueryWrapperX<MpMessageDO>()
15 .eqIfPresent(MpMessageDO::getAccountId, reqVO.getAccountId()) 15 .eqIfPresent(MpMessageDO::getAccountId, reqVO.getAccountId())
16 - .eqIfPresent(MpMessageDO::getOpenid, reqVO.getOpenId())  
17 -// .likeIfPresent(MpMessageDO::getNickname, reqVO.getNickname())  
18 .eqIfPresent(MpMessageDO::getType, reqVO.getType()) 16 .eqIfPresent(MpMessageDO::getType, reqVO.getType())
  17 + .eqIfPresent(MpMessageDO::getOpenid, reqVO.getOpenid())
  18 + .betweenIfPresent(MpMessageDO::getCreateTime, reqVO.getCreateTime())
19 .orderByDesc(MpMessageDO::getId)); 19 .orderByDesc(MpMessageDO::getId));
20 } 20 }
21 21
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/DefaultMpServiceFactory.java
@@ -103,7 +103,6 @@ public class DefaultMpServiceFactory implements MpServiceFactory { @@ -103,7 +103,6 @@ public class DefaultMpServiceFactory implements MpServiceFactory {
103 103
104 private WxMpService buildMpService(MpAccountDO account) { 104 private WxMpService buildMpService(MpAccountDO account) {
105 // 第一步,创建 WxMpRedisConfigImpl 对象 105 // 第一步,创建 WxMpRedisConfigImpl 对象
106 - // TODO 芋艿:需要确认下,redis key 的存储结构  
107 WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl( 106 WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(
108 redisTemplateWxRedisOps, mpProperties.getConfigStorage().getKeyPrefix()); 107 redisTemplateWxRedisOps, mpProperties.getConfigStorage().getKeyPrefix());
109 configStorage.setAppId(account.getAppId()); 108 configStorage.setAppId(account.getAppId());
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/menu/MenuHandler.java
@@ -18,7 +18,7 @@ import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType; @@ -18,7 +18,7 @@ import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
18 /** 18 /**
19 * 自定义菜单的事件处理器 19 * 自定义菜单的事件处理器
20 * 20 *
21 - * 逻辑:用户点击菜单时,触发对应的回复 21 + * 逻辑:粉丝点击菜单时,触发对应的回复
22 * 22 *
23 * @author 芋道源码 23 * @author 芋道源码
24 */ 24 */
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/other/KfSessionHandler.java
@@ -20,8 +20,7 @@ public class KfSessionHandler implements WxMpMessageHandler { @@ -20,8 +20,7 @@ public class KfSessionHandler implements WxMpMessageHandler {
20 @Override 20 @Override
21 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, 21 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
22 WxMpService wxMpService, WxSessionManager sessionManager) { 22 WxMpService wxMpService, WxSessionManager sessionManager) {
23 - // TODO 对会话做处理  
24 - return null; 23 + throw new UnsupportedOperationException("未实现该处理,请自行重写");
25 } 24 }
26 25
27 } 26 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/other/NullHandler.java
@@ -18,7 +18,7 @@ public class NullHandler implements WxMpMessageHandler { @@ -18,7 +18,7 @@ public class NullHandler implements WxMpMessageHandler {
18 @Override 18 @Override
19 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, 19 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
20 WxMpService wxMpService, WxSessionManager sessionManager) { 20 WxMpService wxMpService, WxSessionManager sessionManager) {
21 - return null; 21 + throw new UnsupportedOperationException("未实现该处理,请自行重写");
22 } 22 }
23 23
24 } 24 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/other/ScanHandler.java
@@ -17,9 +17,9 @@ import java.util.Map; @@ -17,9 +17,9 @@ import java.util.Map;
17 public class ScanHandler implements WxMpMessageHandler { 17 public class ScanHandler implements WxMpMessageHandler {
18 18
19 @Override 19 @Override
20 - public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, 20 + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> context,
21 WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { 21 WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
22 - // 扫码事件处理  
23 - return null; 22 + throw new UnsupportedOperationException("未实现该处理,请自行重写");
24 } 23 }
  24 +
25 } 25 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/other/StoreCheckNotifyHandler.java
@@ -16,11 +16,9 @@ import java.util.Map; @@ -16,11 +16,9 @@ import java.util.Map;
16 public class StoreCheckNotifyHandler implements WxMpMessageHandler { 16 public class StoreCheckNotifyHandler implements WxMpMessageHandler {
17 17
18 @Override 18 @Override
19 - public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,  
20 - Map<String, Object> context, WxMpService wxMpService,  
21 - WxSessionManager sessionManager) {  
22 - // TODO 处理门店审核事件  
23 - return null; 19 + public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
  20 + WxMpService wxMpService, WxSessionManager sessionManager) {
  21 + throw new UnsupportedOperationException("未实现该处理,请自行重写");
24 } 22 }
25 23
26 } 24 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/LocationHandler.java
@@ -21,7 +21,7 @@ import java.util.Map; @@ -21,7 +21,7 @@ import java.util.Map;
21 * 21 *
22 * 触发操作:打开微信公众号 -> 点击 + 号 -> 选择「语音」 22 * 触发操作:打开微信公众号 -> 点击 + 号 -> 选择「语音」
23 * 23 *
24 - * 逻辑:用户上传地理位置时,也可以触发自动回复 24 + * 逻辑:粉丝上传地理位置时,也可以触发自动回复
25 * 25 *
26 * @author 芋道源码 26 * @author 芋道源码
27 */ 27 */
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/SubscribeHandler.java
1 package cn.iocoder.yudao.module.mp.service.handler.user; 1 package cn.iocoder.yudao.module.mp.service.handler.user;
2 2
3 -import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;  
4 import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder; 3 import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder;
5 -import cn.iocoder.yudao.module.mp.service.account.MpAccountService;  
6 import cn.iocoder.yudao.module.mp.service.message.MpAutoReplyService; 4 import cn.iocoder.yudao.module.mp.service.message.MpAutoReplyService;
7 import cn.iocoder.yudao.module.mp.service.user.MpUserService; 5 import cn.iocoder.yudao.module.mp.service.user.MpUserService;
8 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
@@ -13,7 +11,6 @@ import me.chanjar.weixin.mp.api.WxMpService; @@ -13,7 +11,6 @@ import me.chanjar.weixin.mp.api.WxMpService;
13 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 11 import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
14 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 12 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
15 import me.chanjar.weixin.mp.bean.result.WxMpUser; 13 import me.chanjar.weixin.mp.bean.result.WxMpUser;
16 -import org.springframework.context.annotation.Lazy;  
17 import org.springframework.stereotype.Component; 14 import org.springframework.stereotype.Component;
18 15
19 import javax.annotation.Resource; 16 import javax.annotation.Resource;
@@ -36,16 +33,16 @@ public class SubscribeHandler implements WxMpMessageHandler { @@ -36,16 +33,16 @@ public class SubscribeHandler implements WxMpMessageHandler {
36 @Override 33 @Override
37 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, 34 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
38 WxMpService weixinService, WxSessionManager sessionManager) throws WxErrorException { 35 WxMpService weixinService, WxSessionManager sessionManager) throws WxErrorException {
39 - // 第一步,从公众号平台,获取用户信息  
40 - log.info("[handle][用户({}) 关注]", wxMessage.getFromUser()); 36 + // 第一步,从公众号平台,获取粉丝信息
  37 + log.info("[handle][粉丝({}) 关注]", wxMessage.getFromUser());
41 WxMpUser wxMpUser = null; 38 WxMpUser wxMpUser = null;
42 try { 39 try {
43 wxMpUser = weixinService.getUserService().userInfo(wxMessage.getFromUser()); 40 wxMpUser = weixinService.getUserService().userInfo(wxMessage.getFromUser());
44 } catch (WxErrorException e) { 41 } catch (WxErrorException e) {
45 - log.error("[handle][用户({})] 获取用户信息失败!", wxMessage.getFromUser(), e); 42 + log.error("[handle][粉丝({})] 获取粉丝信息失败!", wxMessage.getFromUser(), e);
46 } 43 }
47 44
48 - // 第二步,保存用户信息 45 + // 第二步,保存粉丝信息
49 mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser); 46 mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser);
50 47
51 // 第三步,回复关注的欢迎语 48 // 第三步,回复关注的欢迎语
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/UnsubscribeHandler.java
@@ -31,7 +31,7 @@ public class UnsubscribeHandler implements WxMpMessageHandler { @@ -31,7 +31,7 @@ public class UnsubscribeHandler implements WxMpMessageHandler {
31 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, 31 public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
32 Map<String, Object> context, WxMpService wxMpService, 32 Map<String, Object> context, WxMpService wxMpService,
33 WxSessionManager sessionManager) { 33 WxSessionManager sessionManager) {
34 - log.info("[handle][用户({}) 取消关注]", wxMessage.getFromUser()); 34 + log.info("[handle][粉丝({}) 取消关注]", wxMessage.getFromUser());
35 mpUserService.updateUserUnsubscribe(MpContextHolder.getAppId(), wxMessage.getFromUser()); 35 mpUserService.updateUserUnsubscribe(MpContextHolder.getAppId(), wxMessage.getFromUser());
36 return null; 36 return null;
37 } 37 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/menu/MpMenuService.java
@@ -29,11 +29,11 @@ public interface MpMenuService { @@ -29,11 +29,11 @@ public interface MpMenuService {
29 void deleteMenuByAccountId(Long accountId); 29 void deleteMenuByAccountId(Long accountId);
30 30
31 /** 31 /**
32 - * 用户点击菜单按钮时,回复对应的消息 32 + * 粉丝点击菜单按钮时,回复对应的消息
33 * 33 *
34 * @param appId 公众号 AppId 34 * @param appId 公众号 AppId
35 * @param key 菜单按钮的标识 35 * @param key 菜单按钮的标识
36 - * @param openid 用户的 openid 36 + * @param openid 粉丝的 openid
37 * @return 消息 37 * @return 消息
38 */ 38 */
39 WxMpXmlOutMessage reply(String appId, String key, String openid); 39 WxMpXmlOutMessage reply(String appId, String key, String openid);
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpAutoReplyService.java
@@ -64,7 +64,7 @@ public interface MpAutoReplyService { @@ -64,7 +64,7 @@ public interface MpAutoReplyService {
64 WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage); 64 WxMpXmlOutMessage replyForMessage(String appId, WxMpXmlMessage wxMessage);
65 65
66 /** 66 /**
67 - * 当用户关注时,自动回复 67 + * 当粉丝关注时,自动回复
68 * 68 *
69 * @param appId 微信公众号 appId 69 * @param appId 微信公众号 appId
70 * @param wxMessage 消息 70 * @param wxMessage 消息
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpAutoReplyServiceImpl.java
@@ -136,7 +136,7 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService { @@ -136,7 +136,7 @@ public class MpAutoReplyServiceImpl implements MpAutoReplyService {
136 136
137 @Override 137 @Override
138 public void deleteAutoReply(Long id) { 138 public void deleteAutoReply(Long id) {
139 - // 校验用户存在 139 + // 校验粉丝存在
140 validateAutoReplyExists(id); 140 validateAutoReplyExists(id);
141 141
142 // 删除自动回复 142 // 删除自动回复
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java
@@ -11,23 +11,22 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; @@ -11,23 +11,22 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
11 import javax.validation.Valid; 11 import javax.validation.Valid;
12 12
13 /** 13 /**
14 - * 粉丝消息 Service 接口 14 + * 公众号消息 Service 接口
15 * 15 *
16 * @author 芋道源码 16 * @author 芋道源码
17 */ 17 */
18 public interface MpMessageService { 18 public interface MpMessageService {
19 19
20 - // TODO 芋艿:方法名要优化下  
21 /** 20 /**
22 - * 获得粉丝消息分页 21 + * 获得公众号消息分页
23 * 22 *
24 * @param pageReqVO 分页查询 23 * @param pageReqVO 分页查询
25 - * @return 粉丝消息分页 24 + * @return 公众号消息分页
26 */ 25 */
27 - PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO); 26 + PageResult<MpMessageDO> getMessagePage(MpMessagePageReqVO pageReqVO);
28 27
29 /** 28 /**
30 - * 从公众号,接收到用户消息 29 + * 从公众号,接收到粉丝消息
31 * 30 *
32 * @param appId 微信公众号 appId 31 * @param appId 微信公众号 appId
33 * @param wxMessage 消息 32 * @param wxMessage 消息
@@ -35,7 +34,7 @@ public interface MpMessageService { @@ -35,7 +34,7 @@ public interface MpMessageService {
35 void receiveMessage(String appId, WxMpXmlMessage wxMessage); 34 void receiveMessage(String appId, WxMpXmlMessage wxMessage);
36 35
37 /** 36 /**
38 - * 使用公众号,给用户回复消息 37 + * 使用公众号,给粉丝回复消息
39 * 38 *
40 * 例如说:自动回复、客服消息、菜单回复消息等场景 39 * 例如说:自动回复、客服消息、菜单回复消息等场景
41 * 40 *
@@ -47,7 +46,7 @@ public interface MpMessageService { @@ -47,7 +46,7 @@ public interface MpMessageService {
47 WxMpXmlOutMessage sendOutMessage(@Valid MpMessageSendOutReqBO sendReqBO); 46 WxMpXmlOutMessage sendOutMessage(@Valid MpMessageSendOutReqBO sendReqBO);
48 47
49 /** 48 /**
50 - * 使用公众号,给用户发送【客服】消息 49 + * 使用公众号,给粉丝发送【客服】消息
51 * 50 *
52 * 注意,该方法会真实发送消息 51 * 注意,该方法会真实发送消息
53 * 52 *
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
@@ -63,7 +63,7 @@ public class MpMessageServiceImpl implements MpMessageService { @@ -63,7 +63,7 @@ public class MpMessageServiceImpl implements MpMessageService {
63 private Validator validator; 63 private Validator validator;
64 64
65 @Override 65 @Override
66 - public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) { 66 + public PageResult<MpMessageDO> getMessagePage(MpMessagePageReqVO pageReqVO) {
67 return mpMessageMapper.selectPage(pageReqVO); 67 return mpMessageMapper.selectPage(pageReqVO);
68 } 68 }
69 69
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/bo/MpMessageSendOutReqBO.java
@@ -14,7 +14,7 @@ import java.util.List; @@ -14,7 +14,7 @@ import java.util.List;
14 /** 14 /**
15 * 公众号消息发送 Request BO 15 * 公众号消息发送 Request BO
16 * 16 *
17 - * 为什么要有该 BO 呢?在自动回复、客服消息、菜单回复消息等场景,都涉及到 MP 给用户发送消息,所以使用该 BO 统一承接 17 + * 为什么要有该 BO 呢?在自动回复、客服消息、菜单回复消息等场景,都涉及到 MP 给粉丝发送消息,所以使用该 BO 统一承接
18 * 18 *
19 * @author 芋道源码 19 * @author 芋道源码
20 */ 20 */
@@ -27,9 +27,9 @@ public class MpMessageSendOutReqBO { @@ -27,9 +27,9 @@ public class MpMessageSendOutReqBO {
27 @NotEmpty(message = "公众号 appId 不能为空") 27 @NotEmpty(message = "公众号 appId 不能为空")
28 private String appId; 28 private String appId;
29 /** 29 /**
30 - * 公众号用户 openid 30 + * 公众号粉丝 openid
31 */ 31 */
32 - @NotEmpty(message = "公众号用户 openid 不能为空") 32 + @NotEmpty(message = "公众号粉丝 openid 不能为空")
33 private String openid; 33 private String openid;
34 34
35 // ========== 消息内容 ========== 35 // ========== 消息内容 ==========
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/statistics/MpStatisticsService.java
@@ -16,20 +16,20 @@ import java.util.List; @@ -16,20 +16,20 @@ import java.util.List;
16 public interface MpStatisticsService { 16 public interface MpStatisticsService {
17 17
18 /** 18 /**
19 - * 获取用户增减数据 19 + * 获取粉丝增减数据
20 * 20 *
21 * @param accountId 公众号账号编号 21 * @param accountId 公众号账号编号
22 * @param date 时间区间 22 * @param date 时间区间
23 - * @return 用户增减数据 23 + * @return 粉丝增减数据
24 */ 24 */
25 List<WxDataCubeUserSummary> getUserSummary(Long accountId, LocalDateTime[] date); 25 List<WxDataCubeUserSummary> getUserSummary(Long accountId, LocalDateTime[] date);
26 26
27 /** 27 /**
28 - * 获取用户累计数据 28 + * 获取粉丝累计数据
29 * 29 *
30 * @param accountId 公众号账号编号 30 * @param accountId 公众号账号编号
31 * @param date 时间区间 31 * @param date 时间区间
32 - * @return 用户累计数据 32 + * @return 粉丝累计数据
33 */ 33 */
34 List<WxDataCubeUserCumulate> getUserCumulate(Long accountId, LocalDateTime[] date); 34 List<WxDataCubeUserCumulate> getUserCumulate(Long accountId, LocalDateTime[] date);
35 35
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserServiceImpl.java
@@ -99,11 +99,11 @@ public class MpUserServiceImpl implements MpUserService { @@ -99,11 +99,11 @@ public class MpUserServiceImpl implements MpUserService {
99 // for 循环,避免递归出意外问题,导致死循环 99 // for 循环,避免递归出意外问题,导致死循环
100 String nextOpenid = null; 100 String nextOpenid = null;
101 for (int i = 0; i < Short.MAX_VALUE; i++) { 101 for (int i = 0; i < Short.MAX_VALUE; i++) {
102 - log.info("[syncUser][第({}) 次加载公众号用户列表,nextOpenid({})]", i, nextOpenid); 102 + log.info("[syncUser][第({}) 次加载公众号粉丝列表,nextOpenid({})]", i, nextOpenid);
103 try { 103 try {
104 nextOpenid = syncUser0(account, nextOpenid); 104 nextOpenid = syncUser0(account, nextOpenid);
105 } catch (WxErrorException e) { 105 } catch (WxErrorException e) {
106 - log.error("[syncUser][第({}) 次同步用户异常]", i, e); 106 + log.error("[syncUser][第({}) 次同步粉丝异常]", i, e);
107 break; 107 break;
108 } 108 }
109 // 如果 nextOpenid 为空,表示已经同步完毕 109 // 如果 nextOpenid 为空,表示已经同步完毕
@@ -114,17 +114,17 @@ public class MpUserServiceImpl implements MpUserService { @@ -114,17 +114,17 @@ public class MpUserServiceImpl implements MpUserService {
114 } 114 }
115 115
116 private String syncUser0(MpAccountDO account, String nextOpenid) throws WxErrorException { 116 private String syncUser0(MpAccountDO account, String nextOpenid) throws WxErrorException {
117 - // 第一步,从公众号流式加载用户 117 + // 第一步,从公众号流式加载粉丝
118 WxMpService mpService = mpServiceFactory.getRequiredMpService(account.getId()); 118 WxMpService mpService = mpServiceFactory.getRequiredMpService(account.getId());
119 WxMpUserList wxUserList = mpService.getUserService().userList(nextOpenid); 119 WxMpUserList wxUserList = mpService.getUserService().userList(nextOpenid);
120 if (CollUtil.isEmpty(wxUserList.getOpenids())) { 120 if (CollUtil.isEmpty(wxUserList.getOpenids())) {
121 return null; 121 return null;
122 } 122 }
123 123
124 - // 第二步,分批加载用户信息 124 + // 第二步,分批加载粉丝信息
125 List<List<String>> openidsList = CollUtil.split(wxUserList.getOpenids(), 100); 125 List<List<String>> openidsList = CollUtil.split(wxUserList.getOpenids(), 100);
126 for (List<String> openids : openidsList) { 126 for (List<String> openids : openidsList) {
127 - log.info("[syncUser][批量加载用户信息,openids({})]", openids); 127 + log.info("[syncUser][批量加载粉丝信息,openids({})]", openids);
128 List<WxMpUser> wxUsers = mpService.getUserService().userInfoList(openids); 128 List<WxMpUser> wxUsers = mpService.getUserService().userInfoList(openids);
129 batchSaveUser(account, wxUsers); 129 batchSaveUser(account, wxUsers);
130 } 130 }
@@ -137,7 +137,7 @@ public class MpUserServiceImpl implements MpUserService { @@ -137,7 +137,7 @@ public class MpUserServiceImpl implements MpUserService {
137 if (CollUtil.isEmpty(wxUsers)) { 137 if (CollUtil.isEmpty(wxUsers)) {
138 return; 138 return;
139 } 139 }
140 - // 1. 获得数据库已保存的用户列表 140 + // 1. 获得数据库已保存的粉丝列表
141 List<MpUserDO> dbUsers = mpUserMapper.selectListByAppIdAndOpenid(account.getAppId(), 141 List<MpUserDO> dbUsers = mpUserMapper.selectListByAppIdAndOpenid(account.getAppId(),
142 CollectionUtils.convertList(wxUsers, WxMpUser::getOpenId)); 142 CollectionUtils.convertList(wxUsers, WxMpUser::getOpenId));
143 Map<String, MpUserDO> openId2Users = CollectionUtils.convertMap(dbUsers, MpUserDO::getOpenid); 143 Map<String, MpUserDO> openId2Users = CollectionUtils.convertMap(dbUsers, MpUserDO::getOpenid);
yudao-ui-admin/src/api/mp/draft.js
@@ -9,7 +9,7 @@ export function getDraftPage(query) { @@ -9,7 +9,7 @@ export function getDraftPage(query) {
9 }) 9 })
10 } 10 }
11 11
12 -// 创建草稿 12 +// 创建公众号草稿
13 export function createDraft(accountId, articles) { 13 export function createDraft(accountId, articles) {
14 return request({ 14 return request({
15 url: '/mp/draft/create?accountId=' + accountId, 15 url: '/mp/draft/create?accountId=' + accountId,
@@ -20,7 +20,7 @@ export function createDraft(accountId, articles) { @@ -20,7 +20,7 @@ export function createDraft(accountId, articles) {
20 }) 20 })
21 } 21 }
22 22
23 -// 更新草稿 23 +// 更新公众号草稿
24 export function updateDraft(accountId, mediaId, articles) { 24 export function updateDraft(accountId, mediaId, articles) {
25 return request({ 25 return request({
26 url: '/mp/draft/update?accountId=' + accountId + '&mediaId=' + mediaId, 26 url: '/mp/draft/update?accountId=' + accountId + '&mediaId=' + mediaId,
@@ -29,7 +29,7 @@ export function updateDraft(accountId, mediaId, articles) { @@ -29,7 +29,7 @@ export function updateDraft(accountId, mediaId, articles) {
29 }) 29 })
30 } 30 }
31 31
32 -// 删除草稿 32 +// 删除公众号草稿
33 export function deleteDraft(accountId, mediaId) { 33 export function deleteDraft(accountId, mediaId) {
34 return request({ 34 return request({
35 url: '/mp/draft/delete?accountId=' + accountId + '&mediaId=' + mediaId, 35 url: '/mp/draft/delete?accountId=' + accountId + '&mediaId=' + mediaId,
yudao-ui-admin/src/api/mp/message.js
1 import request from '@/utils/request' 1 import request from '@/utils/request'
2 2
3 -// 获得粉丝消息分页 3 +// 获得公众号消息分页
4 export function getMessagePage(query) { 4 export function getMessagePage(query) {
5 return request({ 5 return request({
6 url: '/mp/message/page', 6 url: '/mp/message/page',
yudao-ui-admin/src/utils/dict.js
@@ -60,6 +60,7 @@ export const DICT_TYPE = { @@ -60,6 +60,7 @@ export const DICT_TYPE = {
60 60
61 // ========== MP 模块 ========== 61 // ========== MP 模块 ==========
62 MP_AUTO_REPLY_REQUEST_MATCH: 'mp_auto_reply_request_match', // 自动回复请求匹配类型 62 MP_AUTO_REPLY_REQUEST_MATCH: 'mp_auto_reply_request_match', // 自动回复请求匹配类型
  63 + MP_MESSAGE_TYPE: 'mp_message_type', // 消息类型
63 64
64 // ========== MALL - PRODUCT 模块 ========== 65 // ========== MALL - PRODUCT 模块 ==========
65 PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态 66 PRODUCT_SPU_STATUS: 'product_spu_status', // 商品 SPU 状态
yudao-ui-admin/src/views/mp/autoReply/index.vue
@@ -50,49 +50,12 @@ SOFTWARE. @@ -50,49 +50,12 @@ SOFTWARE.
50 </el-col> 50 </el-col>
51 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" /> 51 <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
52 </el-row> 52 </el-row>
53 - <!-- 列表 --> 53 + <!-- tab 项 -->
54 <el-tab-pane name="1"> 54 <el-tab-pane name="1">
55 <span slot="label"><i class="el-icon-star-off"></i> 关注时回复</span> 55 <span slot="label"><i class="el-icon-star-off"></i> 关注时回复</span>
56 - <el-table v-loading="loading" :data="list">  
57 - <el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>  
58 - <el-table-column label="创建时间" align="center" prop="createTime" width="180">  
59 - <template slot-scope="scope">  
60 - <span>{{ parseTime(scope.row.createTime) }}</span>  
61 - </template>  
62 - </el-table-column>  
63 - <el-table-column label="操作" align="center" class-name="small-padding fixed-width">  
64 - <template slot-scope="scope">  
65 - <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"  
66 - v-hasPermi="['mp:auto-reply:update']">修改  
67 - </el-button>  
68 - <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"  
69 - v-hasPermi="['mp:auto-reply:delete']">删除  
70 - </el-button>  
71 - </template>  
72 - </el-table-column>  
73 - </el-table>  
74 </el-tab-pane> 56 </el-tab-pane>
75 <el-tab-pane name="2"> 57 <el-tab-pane name="2">
76 <span slot="label"><i class="el-icon-chat-line-round"></i> 消息回复</span> 58 <span slot="label"><i class="el-icon-chat-line-round"></i> 消息回复</span>
77 - <el-table v-loading="loading" :data="list">  
78 - <el-table-column label="请求消息类型" align="center" prop="requestMessageType"/>  
79 - <el-table-column label="回复消息类型" align="center" prop="responseMessageType"/>  
80 - <el-table-column label="创建时间" align="center" prop="createTime" width="180">  
81 - <template slot-scope="scope">  
82 - <span>{{ parseTime(scope.row.createTime) }}</span>  
83 - </template>  
84 - </el-table-column>  
85 - <el-table-column label="操作" align="center" class-name="small-padding fixed-width">  
86 - <template slot-scope="scope">  
87 - <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"  
88 - v-hasPermi="['mp:auto-reply:update']">修改  
89 - </el-button>  
90 - <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"  
91 - v-hasPermi="['mp:auto-reply:delete']">删除  
92 - </el-button>  
93 - </template>  
94 - </el-table-column>  
95 - </el-table>  
96 </el-tab-pane> 59 </el-tab-pane>
97 <el-tab-pane name="3"> 60 <el-tab-pane name="3">
98 <span slot="label"><i class="el-icon-news"></i> 关键词回复</span> 61 <span slot="label"><i class="el-icon-news"></i> 关键词回复</span>
@@ -101,13 +64,42 @@ SOFTWARE. @@ -101,13 +64,42 @@ SOFTWARE.
101 64
102 <!-- 列表 --> 65 <!-- 列表 -->
103 <el-table v-loading="loading" :data="list"> 66 <el-table v-loading="loading" :data="list">
104 - <el-table-column label="关键词" align="center" prop="requestKeyword"/>  
105 - <el-table-column label="匹配类型" align="center" prop="requestMatch"> 67 + <el-table-column label="请求消息类型" align="center" prop="requestMessageType" v-if="type === '2'" />
  68 + <el-table-column label="关键词" align="center" prop="requestKeyword" v-if="type === '3'" />
  69 + <el-table-column label="匹配类型" align="center" prop="requestMatch" v-if="type === '3'">
106 <template v-slot="scope"> 70 <template v-slot="scope">
107 <dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/> 71 <dict-tag :type="DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH" :value="scope.row.requestMatch"/>
108 </template> 72 </template>
109 </el-table-column> 73 </el-table-column>
110 - <el-table-column label="回复消息类型" align="center" prop="responseMessageType"/> 74 + <el-table-column label="回复消息类型" align="center">
  75 + <template v-slot="scope">
  76 + <dict-tag :type="DICT_TYPE.MP_MESSAGE_TYPE" :value="scope.row.responseMessageType"/>
  77 + </template>
  78 + </el-table-column>
  79 + <el-table-column label="回复内容" align="center">
  80 + <template slot-scope="scope">
  81 + <div v-if="scope.row.responseMessageType === 'text'">{{ scope.row.responseContent }}</div>
  82 + <div v-else-if="scope.row.responseMessageType === 'voice'">
  83 + <wx-voice-player :url="scope.row.responseMediaUrl" />
  84 + </div>
  85 + <div v-else-if="scope.row.responseMessageType === 'image'">
  86 + <a target="_blank" :href="scope.row.responseMediaUrl">
  87 + <img :src="scope.row.responseMediaUrl" style="width: 100px">
  88 + </a>
  89 + </div>
  90 + <div v-else-if="scope.row.responseMessageType === 'video' || scope.row.responseMessageType === 'shortvideo'">
  91 + <wx-video-player :url="scope.row.responseMediaUrl" style="margin-top: 10px" />
  92 + </div>
  93 + <div v-else-if="scope.row.responseMessageType === 'news'">
  94 + <wx-news :articles="scope.row.responseArticles" />
  95 + </div>
  96 + <div v-else-if="scope.row.responseMessageType === 'music'">
  97 + <wx-music :title="scope.row.responseTitle" :description="scope.row.responseDescription"
  98 + :thumb-media-url="scope.row.responseThumbMediaUrl"
  99 + :music-url="scope.row.responseMusicUrl" :hq-music-url="scope.row.responseHqMusicUrl" />
  100 + </div>
  101 + </template>
  102 + </el-table-column>
111 <el-table-column label="创建时间" align="center" prop="createTime" width="180"> 103 <el-table-column label="创建时间" align="center" prop="createTime" width="180">
112 <template slot-scope="scope"> 104 <template slot-scope="scope">
113 <span>{{ parseTime(scope.row.createTime) }}</span> 105 <span>{{ parseTime(scope.row.createTime) }}</span>
@@ -130,9 +122,9 @@ SOFTWARE. @@ -130,9 +122,9 @@ SOFTWARE.
130 <el-form ref="form" :model="form" :rules="rules" label-width="80px"> 122 <el-form ref="form" :model="form" :rules="rules" label-width="80px">
131 <el-form-item label="消息类型" prop="requestMessageType" v-if="type === '2'"> 123 <el-form-item label="消息类型" prop="requestMessageType" v-if="type === '2'">
132 <el-select v-model="form.requestMessageType" placeholder="请选择"> 124 <el-select v-model="form.requestMessageType" placeholder="请选择">
133 - <el-option v-for="item in dictData.get('wx_req_type')" :key="item.value" :label="item.label"  
134 - :value="item.value" :disabled="item.disabled" v-if="item.value !== 'event'">  
135 - </el-option> 125 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
  126 + :key="dict.value" :label="dict.label" :value="dict.value"
  127 + v-if="requestMessageTypes.includes(dict.value)"/>
136 </el-select> 128 </el-select>
137 </el-form-item> 129 </el-form-item>
138 <el-form-item label="匹配类型" prop="requestMatch" v-if="type === '3'"> 130 <el-form-item label="匹配类型" prop="requestMatch" v-if="type === '3'">
@@ -157,6 +149,12 @@ SOFTWARE. @@ -157,6 +149,12 @@ SOFTWARE.
157 </template> 149 </template>
158 150
159 <script> 151 <script>
  152 +import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
  153 +import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
  154 +import WxMsg from '@/views/mp/components/wx-msg/main.vue';
  155 +import WxLocation from '@/views/mp/components/wx-location/main.vue';
  156 +import WxMusic from '@/views/mp/components/wx-music/main.vue';
  157 +import WxNews from '@/views/mp/components/wx-news/main.vue';
160 import WxReplySelect from '@/views/mp/components/wx-reply/main.vue' 158 import WxReplySelect from '@/views/mp/components/wx-reply/main.vue'
161 import { getSimpleAccounts } from "@/api/mp/account"; 159 import { getSimpleAccounts } from "@/api/mp/account";
162 import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updateAutoReply } from "@/api/mp/autoReply"; 160 import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updateAutoReply } from "@/api/mp/autoReply";
@@ -164,12 +162,20 @@ import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updat @@ -164,12 +162,20 @@ import { createAutoReply, deleteAutoReply, getAutoReply, getAutoReplyPage, updat
164 export default { 162 export default {
165 name: 'mpAutoReply', 163 name: 'mpAutoReply',
166 components: { 164 components: {
  165 + WxVideoPlayer,
  166 + WxVoicePlayer,
  167 + WxMsg,
  168 + WxLocation,
  169 + WxMusic,
  170 + WxNews,
167 WxReplySelect 171 WxReplySelect
168 }, 172 },
169 data() { 173 data() {
170 return { 174 return {
171 // tab 类型(1、关注时回复;2、消息回复;3、关键词回复) 175 // tab 类型(1、关注时回复;2、消息回复;3、关键词回复)
172 type: '3', 176 type: '3',
  177 + // 允许选择的请求消息类型
  178 + requestMessageTypes: ['text', 'image', 'voice', 'video', 'shortvideo', 'location', 'link'],
173 // 遮罩层 179 // 遮罩层
174 loading: true, 180 loading: true,
175 // 显示搜索条件 181 // 显示搜索条件
@@ -202,8 +208,6 @@ export default { @@ -202,8 +208,6 @@ export default {
202 }, 208 },
203 hackResetWxReplySelect: false, // 重置 WxReplySelect 组件,解决无法清除的问题 209 hackResetWxReplySelect: false, // 重置 WxReplySelect 组件,解决无法清除的问题
204 210
205 - dictData: new Map(),  
206 -  
207 // 公众号账号列表 211 // 公众号账号列表
208 accounts: [] 212 accounts: []
209 } 213 }
@@ -218,33 +222,6 @@ export default { @@ -218,33 +222,6 @@ export default {
218 // 加载数据 222 // 加载数据
219 this.getList(); 223 this.getList();
220 }) 224 })
221 -  
222 - // TODO 芋艿:字典数据,一起搞  
223 - this.dictData.set('wx_req_type',[{  
224 - value: 'text',  
225 - label: '文本'  
226 - },{  
227 - value: 'image',  
228 - label: '图片'  
229 - },{  
230 - value: 'voice',  
231 - label: '语音'  
232 - },{  
233 - value: 'video',  
234 - label: '视频'  
235 - },{  
236 - value: 'shortvideo',  
237 - label: '小视频'  
238 - },{  
239 - value: 'location',  
240 - label: '地理位置'  
241 - },{  
242 - value: 'link',  
243 - label: '链接消息'  
244 - },{  
245 - value: 'event',  
246 - label: '事件推送'  
247 - }])  
248 }, 225 },
249 methods: { 226 methods: {
250 /** 查询列表 */ 227 /** 查询列表 */
@@ -295,7 +272,8 @@ export default { @@ -295,7 +272,8 @@ export default {
295 this.open = true 272 this.open = true
296 this.title = '新增自动回复'; 273 this.title = '新增自动回复';
297 this.objData = { 274 this.objData = {
298 - type : 'text' 275 + type : 'text',
  276 + accountId: this.queryParams.accountId,
299 } 277 }
300 }, 278 },
301 /** 修改按钮操作 */ 279 /** 修改按钮操作 */
yudao-ui-admin/src/views/mp/components/wx-msg/main.vue
@@ -9,13 +9,13 @@ @@ -9,13 +9,13 @@
9 <div class="msg-main"> 9 <div class="msg-main">
10 <div class="msg-div" :id="'msg-div' + nowStr"> 10 <div class="msg-div" :id="'msg-div' + nowStr">
11 <!-- 加载更多 --> 11 <!-- 加载更多 -->
12 - <div v-loading="tableLoading"></div>  
13 - <div v-if="!tableLoading"> 12 + <div v-loading="loading"></div>
  13 + <div v-if="!loading">
14 <div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div> 14 <div class="el-table__empty-block" v-if="loadMore" @click="loadingMore"><span class="el-table__empty-text">点击加载更多</span></div>
15 <div class="el-table__empty-block" v-if="!loadMore"><span class="el-table__empty-text">没有更多了</span></div> 15 <div class="el-table__empty-block" v-if="!loadMore"><span class="el-table__empty-text">没有更多了</span></div>
16 </div> 16 </div>
17 <!-- 消息列表 --> 17 <!-- 消息列表 -->
18 - <div class="execution" v-for="item in tableData" :key='item.id'> 18 + <div class="execution" v-for="item in list" :key='item.id'>
19 <div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''"> 19 <div class="avue-comment" :class="item.sendFrom === 2 ? 'avue-comment--reverse' : ''">
20 <div class="avatar-div"> 20 <div class="avatar-div">
21 <img :src="item.sendFrom === 1 ? user.avatar : mp.avatar" class="avue-comment__avatar"> 21 <img :src="item.sendFrom === 1 ? user.avatar : mp.avatar" class="avue-comment__avatar">
@@ -124,20 +124,20 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -124,20 +124,20 @@ import { getUser } from &quot;@/api/mp/user&quot;;
124 }, 124 },
125 props: { 125 props: {
126 userId: { 126 userId: {
127 - type: String, 127 + type: Number,
128 required: true 128 required: true
129 }, 129 },
130 }, 130 },
131 data() { 131 data() {
132 return { 132 return {
133 nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处 133 nowStr: new Date().getTime(), // 当前的时间戳,用于每次消息加载后,回到原位置;具体见 :id="'msg-div' + nowStr" 处
134 - sendLoading: false, // 发送消息是否加载中  
135 - tableLoading: false, // 消息列表是否正在加载中 134 + loading: false, // 消息列表是否正在加载中
136 loadMore: true, // 是否可以加载更多 135 loadMore: true, // 是否可以加载更多
137 - tableData: [], // 消息列表  
138 - page: { 136 + list: [], // 消息列表
  137 + queryParams: {
139 pageNo: 1, // 当前页数 138 pageNo: 1, // 当前页数
140 pageSize: 14, // 每页显示多少条 139 pageSize: 14, // 每页显示多少条
  140 + accountId: undefined,
141 }, 141 },
142 user: { // 由于微信不再提供昵称,直接使用“用户”展示 142 user: { // 由于微信不再提供昵称,直接使用“用户”展示
143 nickname: '用户', 143 nickname: '用户',
@@ -148,6 +148,9 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -148,6 +148,9 @@ import { getUser } from &quot;@/api/mp/user&quot;;
148 nickname: '公众号', 148 nickname: '公众号',
149 avatar: require("@/assets/images/wechat.png"), 149 avatar: require("@/assets/images/wechat.png"),
150 }, 150 },
  151 +
  152 + // ========= 消息发送 =========
  153 + sendLoading: false, // 发送消息是否加载中
151 objData: { // 微信发送消息 154 objData: { // 微信发送消息
152 type: 'text', 155 type: 'text',
153 }, 156 },
@@ -156,14 +159,19 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -156,14 +159,19 @@ import { getUser } from &quot;@/api/mp/user&quot;;
156 created() { 159 created() {
157 // 获得用户信息 160 // 获得用户信息
158 getUser(this.userId).then(response => { 161 getUser(this.userId).then(response => {
159 - this.user.nickname = response.data.nickname | this.user.nickname;  
160 - this.user.avatar = response.data.avatar | this.user.avatar; 162 + this.user.nickname = response.data.nickname && response.data.nickname.length > 0 ?
  163 + response.data.nickname : this.user.nickname;
  164 + this.user.avatar = response.data.avatar && this.user.avatar.length > 0 ?
  165 + response.data.avatar : this.user.avatar;
  166 + this.user.accountId = response.data.accountId;
161 // 设置公众号账号编号 167 // 设置公众号账号编号
  168 + this.queryParams.accountId = response.data.accountId;
162 this.objData.accountId = response.data.accountId; 169 this.objData.accountId = response.data.accountId;
163 - })  
164 170
165 - // 加载消息  
166 - this.refreshChange() 171 + // 加载消息
  172 + console.log(this.queryParams)
  173 + this.refreshChange()
  174 + })
167 }, 175 },
168 methods:{ 176 methods:{
169 sendMsg(){ 177 sendMsg(){
@@ -190,7 +198,7 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -190,7 +198,7 @@ import { getUser } from &quot;@/api/mp/user&quot;;
190 })).then(response => { 198 })).then(response => {
191 this.sendLoading = false 199 this.sendLoading = false
192 // 添加到消息列表,并滚动 200 // 添加到消息列表,并滚动
193 - this.tableData = [...this.tableData , ...[response.data] ] 201 + this.list = [...this.list , ...[response.data] ]
194 this.scrollToBottom() 202 this.scrollToBottom()
195 // 重置 objData 状态 203 // 重置 objData 状态
196 this.$refs['replySelect'].deleteObj(); // 重置,避免 tab 的数据未清理 204 this.$refs['replySelect'].deleteObj(); // 重置,避免 tab 的数据未清理
@@ -199,15 +207,16 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -199,15 +207,16 @@ import { getUser } from &quot;@/api/mp/user&quot;;
199 }) 207 })
200 }, 208 },
201 loadingMore() { 209 loadingMore() {
202 - this.page.pageNo++  
203 - this.getPage(this.page) 210 + this.queryParams.pageNo++
  211 + this.getPage(this.queryParams)
204 }, 212 },
205 getPage(page, params) { 213 getPage(page, params) {
206 - this.tableLoading = true 214 + this.loading = true
207 getMessagePage(Object.assign({ 215 getMessagePage(Object.assign({
208 pageNo: page.pageNo, 216 pageNo: page.pageNo,
209 pageSize: page.pageSize, 217 pageSize: page.pageSize,
210 - userId: this.userId 218 + userId: this.userId,
  219 + accountId: page.accountId,
211 }, params)).then(response => { 220 }, params)).then(response => {
212 // 计算当前的滚动高度 221 // 计算当前的滚动高度
213 const msgDiv = document.getElementById('msg-div' + this.nowStr); 222 const msgDiv = document.getElementById('msg-div' + this.nowStr);
@@ -218,16 +227,16 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -218,16 +227,16 @@ import { getUser } from &quot;@/api/mp/user&quot;;
218 227
219 // 处理数据 228 // 处理数据
220 const data = response.data.list.reverse(); 229 const data = response.data.list.reverse();
221 - this.tableData = [...data, ...this.tableData]  
222 - this.tableLoading = false  
223 - if (data.length < this.page.pageSize || data.length === 0){ 230 + this.list = [...data, ...this.list]
  231 + this.loading = false
  232 + if (data.length < this.queryParams.pageSize || data.length === 0){
224 this.loadMore = false 233 this.loadMore = false
225 } 234 }
226 - this.page.pageNo = page.pageNo  
227 - this.page.pageSize = page.pageSize 235 + this.queryParams.pageNo = page.pageNo
  236 + this.queryParams.pageSize = page.pageSize
228 237
229 // 滚动到原来的位置 238 // 滚动到原来的位置
230 - if(this.page.pageNo === 1) { // 定位到消息底部 239 + if(this.queryParams.pageNo === 1) { // 定位到消息底部
231 this.scrollToBottom() 240 this.scrollToBottom()
232 } else if (data.length !== 0) { // 定位滚动条 241 } else if (data.length !== 0) { // 定位滚动条
233 this.$nextTick(() => { 242 this.$nextTick(() => {
@@ -242,7 +251,7 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -242,7 +251,7 @@ import { getUser } from &quot;@/api/mp/user&quot;;
242 * 刷新回调 251 * 刷新回调
243 */ 252 */
244 refreshChange() { 253 refreshChange() {
245 - this.getPage(this.page) 254 + this.getPage(this.queryParams)
246 }, 255 },
247 /** 定位到消息底部 */ 256 /** 定位到消息底部 */
248 scrollToBottom: function () { 257 scrollToBottom: function () {
@@ -267,6 +276,8 @@ import { getUser } from &quot;@/api/mp/user&quot;; @@ -267,6 +276,8 @@ import { getUser } from &quot;@/api/mp/user&quot;;
267 height: 50vh; 276 height: 50vh;
268 overflow: auto; 277 overflow: auto;
269 background-color: #eaeaea; 278 background-color: #eaeaea;
  279 + margin-left: 10px;
  280 + margin-right: 10px;
270 } 281 }
271 .msg-send { 282 .msg-send {
272 padding: 10px; 283 padding: 10px;
yudao-ui-admin/src/views/mp/menu/index.vue
@@ -66,8 +66,8 @@ SOFTWARE. @@ -66,8 +66,8 @@ SOFTWARE.
66 <div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div> 66 <div class="menu_bottom menu_addicon" v-if="this.menuList.length < 3" @click="addMenu"><i class="el-icon-plus"></i></div>
67 </div> 67 </div>
68 <div class="save_div"> 68 <div class="save_div">
69 - <el-button class="save_btn" type="success" size="small" @click="handleSave">保存并发布菜单</el-button>  
70 - <el-button class="save_btn" type="danger" size="small" @click="handleDelete">清空菜单</el-button> 69 + <el-button class="save_btn" type="success" size="small" @click="handleSave" v-hasPermi="['mp:menu:save']">保存并发布菜单</el-button>
  70 + <el-button class="save_btn" type="danger" size="small" @click="handleDelete" v-hasPermi="['mp:menu:delete']">清空菜单</el-button>
71 </div> 71 </div>
72 </div> 72 </div>
73 <!--右边配置--> 73 <!--右边配置-->
yudao-ui-admin/src/views/mp/message/index.vue
@@ -8,18 +8,19 @@ @@ -8,18 +8,19 @@
8 <el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" /> 8 <el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
9 </el-select> 9 </el-select>
10 </el-form-item> 10 </el-form-item>
11 - <!-- 等待处理 -->  
12 - <el-form-item label="消息类型" prop="msgType">  
13 - <el-select v-model="queryParams.msgType" placeholder="请选择消息类型" clearable size="small">  
14 - <el-option label="请选择字典生成" value=""/> 11 + <!-- TODO 等待处理 -->
  12 + <el-form-item label="消息类型" prop="type">
  13 + <el-select v-model="queryParams.type" placeholder="请选择消息类型" clearable size="small">
  14 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.MP_MESSAGE_TYPE)"
  15 + :key="dict.value" :label="dict.label" :value="dict.value"/>
15 </el-select> 16 </el-select>
16 </el-form-item> 17 </el-form-item>
17 <el-form-item label="用户标识" prop="openid"> 18 <el-form-item label="用户标识" prop="openid">
18 <el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/> 19 <el-input v-model="queryParams.openid" placeholder="请输入用户标识" clearable @keyup.enter.native="handleQuery"/>
19 </el-form-item> 20 </el-form-item>
20 - <el-form-item label="创建时间">  
21 - <el-date-picker v-model="dateRangeCreateTime" style="width: 240px" value-format="yyyy-MM-dd"  
22 - type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/> 21 + <el-form-item label="创建时间" prop="createTime">
  22 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  23 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
23 </el-form-item> 24 </el-form-item>
24 <el-form-item> 25 <el-form-item>
25 <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> 26 <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -42,12 +43,11 @@ @@ -42,12 +43,11 @@
42 <el-table-column label="消息类型" align="center" prop="type" width="80"/> 43 <el-table-column label="消息类型" align="center" prop="type" width="80"/>
43 <el-table-column label="发送方" align="center" prop="sendFrom" width="80"> 44 <el-table-column label="发送方" align="center" prop="sendFrom" width="80">
44 <template slot-scope="scope"> 45 <template slot-scope="scope">
45 - <el-tag v-if="scope.row.sendFrom === 1" type="success">用户</el-tag> 46 + <el-tag v-if="scope.row.sendFrom === 1" type="success">粉丝</el-tag>
46 <el-tag v-else type="info">公众号</el-tag> 47 <el-tag v-else type="info">公众号</el-tag>
47 </template> 48 </template>
48 </el-table-column> 49 </el-table-column>
49 <el-table-column label="用户标识" align="center" prop="openid" width="300" /> 50 <el-table-column label="用户标识" align="center" prop="openid" width="300" />
50 - <!-- TODO 芋艿:发送/接收 -->  
51 <el-table-column label="内容" prop="content"> 51 <el-table-column label="内容" prop="content">
52 <template slot-scope="scope"> 52 <template slot-scope="scope">
53 <!-- 【事件】区域 --> 53 <!-- 【事件】区域 -->
@@ -118,10 +118,9 @@ @@ -118,10 +118,9 @@
118 </el-table-column> 118 </el-table-column>
119 <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> 119 <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
120 <template slot-scope="scope"> 120 <template slot-scope="scope">
121 - <!-- TODO 芋艿:增加消息按钮 -->  
122 -<!-- <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"-->  
123 -<!-- v-hasPermi="['mp:message:update']">修改-->  
124 -<!-- </el-button>--> 121 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleSend(scope.row)"
  122 + v-hasPermi="['mp:message:send']">消息
  123 + </el-button>
125 </template> 124 </template>
126 </el-table-column> 125 </el-table-column>
127 </el-table> 126 </el-table>
@@ -129,24 +128,26 @@ @@ -129,24 +128,26 @@
129 <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" 128 <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
130 @pagination="getList"/> 129 @pagination="getList"/>
131 130
132 - <el-dialog title="用户消息" :visible.sync="open" width="40%">  
133 - <wx-msg user-id="3" v-if="true" /> 131 + <!-- 发送消息的弹窗 -->
  132 + <el-dialog title="粉丝消息列表" :visible.sync="open" width="50%">
  133 + <wx-msg :user-id="userId" v-if="open" />
134 </el-dialog> 134 </el-dialog>
135 135
136 </div> 136 </div>
137 </template> 137 </template>
138 138
139 <script> 139 <script>
140 -import { getMessagePage } from "@/api/mp/message";  
141 import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'; 140 import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue';
142 import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'; 141 import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue';
143 import WxMsg from '@/views/mp/components/wx-msg/main.vue'; 142 import WxMsg from '@/views/mp/components/wx-msg/main.vue';
144 import WxLocation from '@/views/mp/components/wx-location/main.vue'; 143 import WxLocation from '@/views/mp/components/wx-location/main.vue';
145 import WxMusic from '@/views/mp/components/wx-music/main.vue'; 144 import WxMusic from '@/views/mp/components/wx-music/main.vue';
146 import WxNews from '@/views/mp/components/wx-news/main.vue'; 145 import WxNews from '@/views/mp/components/wx-news/main.vue';
  146 +import { getMessagePage } from "@/api/mp/message";
  147 +import { getSimpleAccounts } from "@/api/mp/account";
147 148
148 export default { 149 export default {
149 - name: "WxFansMsg", 150 + name: "MpMessage",
150 components: { 151 components: {
151 WxVideoPlayer, 152 WxVideoPlayer,
152 WxVoicePlayer, 153 WxVoicePlayer,
@@ -167,76 +168,52 @@ export default { @@ -167,76 +168,52 @@ export default {
167 total: 0, 168 total: 0,
168 // 粉丝消息列表 169 // 粉丝消息列表
169 list: [], 170 list: [],
170 - // 弹出层标题  
171 - title: "",  
172 // 是否显示弹出层 171 // 是否显示弹出层
173 - open: true, 172 + open: false,
174 // 查询参数 173 // 查询参数
175 - dateRangeCreateTime: [],  
176 queryParams: { 174 queryParams: {
177 pageNo: 1, 175 pageNo: 1,
178 pageSize: 10, 176 pageSize: 10,
179 openid: null, 177 openid: null,
180 - nickname: null,  
181 - headimgUrl: null,  
182 - wxAccountId: null,  
183 - msgType: null,  
184 - content: null,  
185 - resContent: null,  
186 - isRes: null,  
187 - mediaId: null,  
188 - picUrl: null,  
189 - picPath: null, 178 + accountId: null,
  179 + type: null,
  180 + createTime: []
190 }, 181 },
191 - // 表单参数  
192 - form: {},  
193 - // 表单校验  
194 - rules: {}, 182 + // 操作的用户编号
  183 + userId: 0,
195 184
196 // 公众号账号列表 185 // 公众号账号列表
197 accounts: [] 186 accounts: []
198 }; 187 };
199 }, 188 },
200 created() { 189 created() {
201 - this.getList(); 190 + getSimpleAccounts().then(response => {
  191 + this.accounts = response.data;
  192 + // 默认选中第一个
  193 + if (this.accounts.length > 0) {
  194 + this.queryParams.accountId = this.accounts[0].id;
  195 + }
  196 + // 加载数据
  197 + this.getList();
  198 + })
202 }, 199 },
203 methods: { 200 methods: {
204 /** 查询列表 */ 201 /** 查询列表 */
205 getList() { 202 getList() {
  203 + // 如果没有选中公众号账号,则进行提示。
  204 + if (!this.queryParams.accountId) {
  205 + this.$message.error('未选中公众号,无法查询消息')
  206 + return false
  207 + }
  208 +
206 this.loading = true; 209 this.loading = true;
207 - // 处理查询参数  
208 - let params = {...this.queryParams};  
209 - this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');  
210 // 执行查询 210 // 执行查询
211 - getMessagePage(params).then(response => { 211 + getMessagePage(this.queryParams).then(response => {
212 this.list = response.data.list; 212 this.list = response.data.list;
213 this.total = response.data.total; 213 this.total = response.data.total;
214 this.loading = false; 214 this.loading = false;
215 }); 215 });
216 }, 216 },
217 - /** 取消按钮 */  
218 - cancel() {  
219 - this.open = false;  
220 - this.reset();  
221 - },  
222 - /** 表单重置 */  
223 - reset() {  
224 - this.form = {  
225 - id: undefined,  
226 - openid: undefined,  
227 - nickname: undefined,  
228 - headimgUrl: undefined,  
229 - wxAccountId: undefined,  
230 - msgType: undefined,  
231 - content: undefined,  
232 - resContent: undefined,  
233 - isRes: undefined,  
234 - mediaId: undefined,  
235 - picUrl: undefined,  
236 - picPath: undefined,  
237 - };  
238 - this.resetForm("form");  
239 - },  
240 /** 搜索按钮操作 */ 217 /** 搜索按钮操作 */
241 handleQuery() { 218 handleQuery() {
242 this.queryParams.pageNo = 1; 219 this.queryParams.pageNo = 1;
@@ -244,10 +221,17 @@ export default { @@ -244,10 +221,17 @@ export default {
244 }, 221 },
245 /** 重置按钮操作 */ 222 /** 重置按钮操作 */
246 resetQuery() { 223 resetQuery() {
247 - this.dateRangeCreateTime = [];  
248 this.resetForm("queryForm"); 224 this.resetForm("queryForm");
  225 + // 默认选中第一个
  226 + if (this.accounts.length > 0) {
  227 + this.queryParams.accountId = this.accounts[0].id;
  228 + }
249 this.handleQuery(); 229 this.handleQuery();
250 }, 230 },
  231 + handleSend(row) {
  232 + this.userId = row.userId;
  233 + this.open = true;
  234 + },
251 } 235 }
252 }; 236 };
253 </script> 237 </script>
yudao-ui-admin/src/views/mp/statistics/index.vue
@@ -77,7 +77,7 @@ require(&#39;echarts/lib/component/legend&#39;) @@ -77,7 +77,7 @@ require(&#39;echarts/lib/component/legend&#39;)
77 import { getInterfaceSummary, getUserSummary, getUserCumulate, getUpstreamMessage} from '@/api/mp/statistics' 77 import { getInterfaceSummary, getUserSummary, getUserCumulate, getUpstreamMessage} from '@/api/mp/statistics'
78 import { datePickerOptions } from "@/utils/constants"; 78 import { datePickerOptions } from "@/utils/constants";
79 import {addTime, beginOfDay, betweenDay, endOfDay, formatDate} from "@/utils/dateUtils"; 79 import {addTime, beginOfDay, betweenDay, endOfDay, formatDate} from "@/utils/dateUtils";
80 -import {getSimpleAccounts} from "@/api/mp/account"; 80 +import { getSimpleAccounts } from "@/api/mp/account";
81 81
82 export default { 82 export default {
83 name: 'mpStatistics', 83 name: 'mpStatistics',