Commit c141ebef3f04e99b17df6b4e8cb80a01bac5947d
1 parent
fefad029
mp:实现消息的存储
Showing
13 changed files
with
292 additions
and
183 deletions
yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/message/MpMessageSendFrom.java renamed to yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/message/MpMessageSendFromEnum.java
| @@ -10,7 +10,7 @@ import lombok.Getter; | @@ -10,7 +10,7 @@ import lombok.Getter; | ||
| 10 | */ | 10 | */ |
| 11 | @Getter | 11 | @Getter |
| 12 | @AllArgsConstructor | 12 | @AllArgsConstructor |
| 13 | -public enum MpMessageSendFrom { | 13 | +public enum MpMessageSendFromEnum { |
| 14 | 14 | ||
| 15 | USER_TO_MP(1, "用户发送给公众号"), | 15 | USER_TO_MP(1, "用户发送给公众号"), |
| 16 | MP_TO_USER(2, "公众号发给用户"), | 16 | MP_TO_USER(2, "公众号发给用户"), |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/message/MpMessageConvert.java
| @@ -4,8 +4,13 @@ import java.util.*; | @@ -4,8 +4,13 @@ import java.util.*; | ||
| 4 | 4 | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 6 | ||
| 7 | +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||
| 7 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; | 8 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; |
| 9 | +import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; | ||
| 10 | +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | ||
| 8 | import org.mapstruct.Mapper; | 11 | import org.mapstruct.Mapper; |
| 12 | +import org.mapstruct.Mapping; | ||
| 13 | +import org.mapstruct.Mappings; | ||
| 9 | import org.mapstruct.factory.Mappers; | 14 | import org.mapstruct.factory.Mappers; |
| 10 | import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*; | 15 | import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*; |
| 11 | 16 | ||
| @@ -20,4 +25,19 @@ public interface MpMessageConvert { | @@ -20,4 +25,19 @@ public interface MpMessageConvert { | ||
| 20 | 25 | ||
| 21 | PageResult<MpMessageRespVO> convertPage(PageResult<MpMessageDO> page); | 26 | PageResult<MpMessageRespVO> convertPage(PageResult<MpMessageDO> page); |
| 22 | 27 | ||
| 28 | + @Mappings(value = { | ||
| 29 | + @Mapping(source = "msgType", target = "type"), | ||
| 30 | + }) | ||
| 31 | + MpMessageDO convert(WxMpXmlMessage wxMessage); | ||
| 32 | + | ||
| 33 | + default MpMessageDO convert(WxMpXmlMessage wxMessage, MpAccountDO account, MpUserDO user) { | ||
| 34 | + MpMessageDO message = convert(wxMessage); | ||
| 35 | + if (account != null) { | ||
| 36 | + message.setAccountId(account.getId()).setAppId(account.getAppId()); | ||
| 37 | + } | ||
| 38 | + if (user != null) { | ||
| 39 | + message.setUserId(user.getId()).setOpenid(user.getOpenid()); | ||
| 40 | + } | ||
| 41 | + return message; | ||
| 42 | + } | ||
| 23 | } | 43 | } |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java
| 1 | package cn.iocoder.yudao.module.mp.dal.dataobject.message; | 1 | package cn.iocoder.yudao.module.mp.dal.dataobject.message; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 3 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | 4 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |
| 5 | import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; | 5 | import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; |
| 6 | -import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFrom; | ||
| 7 | -import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; | 6 | +import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum; |
| 7 | +import com.baomidou.mybatisplus.annotation.KeySequence; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 9 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 8 | import lombok.*; | 10 | import lombok.*; |
| 9 | - | ||
| 10 | -import com.baomidou.mybatisplus.annotation.*; | ||
| 11 | -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 12 | import me.chanjar.weixin.common.api.WxConsts; | 11 | import me.chanjar.weixin.common.api.WxConsts; |
| 13 | 12 | ||
| 14 | -import java.io.Serializable; | ||
| 15 | -import java.time.LocalDateTime; | ||
| 16 | -import java.util.List; | ||
| 17 | - | ||
| 18 | /** | 13 | /** |
| 19 | * 微信消息 DO | 14 | * 微信消息 DO |
| 20 | * | 15 | * |
| @@ -36,6 +31,10 @@ public class MpMessageDO extends BaseDO { | @@ -36,6 +31,10 @@ public class MpMessageDO extends BaseDO { | ||
| 36 | @TableId | 31 | @TableId |
| 37 | private Long id; | 32 | private Long id; |
| 38 | /** | 33 | /** |
| 34 | + * 微信公众号消息 id | ||
| 35 | + */ | ||
| 36 | + private Long msgId; | ||
| 37 | + /** | ||
| 39 | * 微信公众号 ID | 38 | * 微信公众号 ID |
| 40 | * | 39 | * |
| 41 | * 关联 {@link MpAccountDO#getId()} | 40 | * 关联 {@link MpAccountDO#getId()} |
| @@ -56,17 +55,9 @@ public class MpMessageDO extends BaseDO { | @@ -56,17 +55,9 @@ public class MpMessageDO extends BaseDO { | ||
| 56 | /** | 55 | /** |
| 57 | * 用户标识 | 56 | * 用户标识 |
| 58 | * | 57 | * |
| 59 | - * 冗余 {@link MpUserDO#getId()} | ||
| 60 | - */ | ||
| 61 | - private String openId; | ||
| 62 | -// /** | ||
| 63 | -// * 昵称 | ||
| 64 | -// */ | ||
| 65 | -// private String nickname; | ||
| 66 | -// /** | ||
| 67 | -// * 头像 | ||
| 68 | -// */ | ||
| 69 | -// private String headImageUrl; | 58 | + * 冗余 {@link MpUserDO#getOpenid()} |
| 59 | + */ | ||
| 60 | + private String openid; | ||
| 70 | 61 | ||
| 71 | /** | 62 | /** |
| 72 | * 消息类型 | 63 | * 消息类型 |
| @@ -77,11 +68,11 @@ public class MpMessageDO extends BaseDO { | @@ -77,11 +68,11 @@ public class MpMessageDO extends BaseDO { | ||
| 77 | /** | 68 | /** |
| 78 | * 消息来源 | 69 | * 消息来源 |
| 79 | * | 70 | * |
| 80 | - * 枚举 {@link MpMessageSendFrom} | 71 | + * 枚举 {@link MpMessageSendFromEnum} |
| 81 | */ | 72 | */ |
| 82 | private Integer sendFrom; | 73 | private Integer sendFrom; |
| 83 | 74 | ||
| 84 | - // ========= 消息内容 ========== | 75 | + // ========= 普通消息内容 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html |
| 85 | 76 | ||
| 86 | /** | 77 | /** |
| 87 | * 消息内容 | 78 | * 消息内容 |
| @@ -97,50 +88,53 @@ public class MpMessageDO extends BaseDO { | @@ -97,50 +88,53 @@ public class MpMessageDO extends BaseDO { | ||
| 97 | */ | 88 | */ |
| 98 | private String mediaId; | 89 | private String mediaId; |
| 99 | /** | 90 | /** |
| 100 | - * 标题 | 91 | + * 媒体文件的 URL |
| 92 | + */ | ||
| 93 | + private String mediaUrl; | ||
| 94 | + /** | ||
| 95 | + * 语音识别后文本 | ||
| 101 | * | 96 | * |
| 102 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC | 97 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VOICE |
| 103 | */ | 98 | */ |
| 104 | - private String title; | 99 | + private String recognition; |
| 105 | /** | 100 | /** |
| 106 | - * 描述 | 101 | + * 语音格式,如 amr,speex 等 |
| 107 | * | 102 | * |
| 108 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC | 103 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VOICE |
| 109 | */ | 104 | */ |
| 110 | - private String description; | ||
| 111 | - | 105 | + private String format; |
| 112 | /** | 106 | /** |
| 113 | - * 音乐链接 | 107 | + * 标题 |
| 114 | * | 108 | * |
| 115 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC | 109 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC、LINK |
| 116 | */ | 110 | */ |
| 117 | - private String musicURL; | 111 | + private String title; |
| 118 | /** | 112 | /** |
| 119 | - * 高质量音乐链接,WIFI 环境优先使用该链接播放音乐 | 113 | + * 描述 |
| 120 | * | 114 | * |
| 121 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC | 115 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC |
| 122 | */ | 116 | */ |
| 123 | - private String hqMusicUrl; | 117 | + private String description; |
| 118 | + | ||
| 124 | /** | 119 | /** |
| 125 | * 缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id | 120 | * 缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id |
| 126 | * | 121 | * |
| 127 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC | 122 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO |
| 128 | */ | 123 | */ |
| 129 | private String thumbMediaId; | 124 | private String thumbMediaId; |
| 130 | - | ||
| 131 | /** | 125 | /** |
| 132 | - * 图文消息个数,限制为 10 条以内 | 126 | + * 缩略图的媒体 URL |
| 133 | * | 127 | * |
| 134 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS | 128 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO |
| 135 | */ | 129 | */ |
| 136 | - private Integer articleCount; | 130 | + private String thumbMediaUrl; |
| 131 | + | ||
| 137 | /** | 132 | /** |
| 138 | - * 图文消息数组 | 133 | + * 点击图文消息跳转链接 |
| 139 | * | 134 | * |
| 140 | - * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS | 135 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 LINK |
| 141 | */ | 136 | */ |
| 142 | - @TableField(typeHandler = ArticleTypeHandler.class) | ||
| 143 | - private List<Article> articles; | 137 | + private String url; |
| 144 | 138 | ||
| 145 | /** | 139 | /** |
| 146 | * 地理位置维度 | 140 | * 地理位置维度 |
| @@ -160,47 +154,29 @@ public class MpMessageDO extends BaseDO { | @@ -160,47 +154,29 @@ public class MpMessageDO extends BaseDO { | ||
| 160 | * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION | 154 | * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION |
| 161 | */ | 155 | */ |
| 162 | private Double scale; | 156 | private Double scale; |
| 163 | - | ||
| 164 | /** | 157 | /** |
| 165 | - * 文章 | 158 | + * 详细地址 |
| 159 | + * | ||
| 160 | + * 消息类型为 {@link WxConsts.XmlMsgType} 的 LOCATION | ||
| 161 | + * | ||
| 162 | + * 例如说杨浦区黄兴路 221-4 号临 | ||
| 166 | */ | 163 | */ |
| 167 | - @Data | ||
| 168 | - public static class Article implements Serializable { | ||
| 169 | - | ||
| 170 | - /** | ||
| 171 | - * 图文消息标题 | ||
| 172 | - */ | ||
| 173 | - private String title; | ||
| 174 | - /** | ||
| 175 | - * 图文消息描述 | ||
| 176 | - */ | ||
| 177 | - private String description; | ||
| 178 | - /** | ||
| 179 | - * 图片链接 | ||
| 180 | - * | ||
| 181 | - * 支持JPG、PNG格式,较好的效果为大图 360*200,小图 200*200 | ||
| 182 | - */ | ||
| 183 | - private String picUrl; | ||
| 184 | - /** | ||
| 185 | - * 点击图文消息跳转链接 | ||
| 186 | - */ | ||
| 187 | - private String url; | 164 | + private String label; |
| 188 | 165 | ||
| 189 | - } | 166 | + // ========= 事件推送 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html |
| 190 | 167 | ||
| 191 | - // TODO @芋艿:可以找一些新的思路 | ||
| 192 | - public static class ArticleTypeHandler extends AbstractJsonTypeHandler<List<Article>> { | ||
| 193 | - | ||
| 194 | - @Override | ||
| 195 | - protected List<Article> parse(String json) { | ||
| 196 | - return JsonUtils.parseArray(json, Article.class); | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - @Override | ||
| 200 | - protected String toJson(List<Article> obj) { | ||
| 201 | - return JsonUtils.toJsonString(obj); | ||
| 202 | - } | ||
| 203 | - | ||
| 204 | - } | 168 | + /** |
| 169 | + * 事件类型 | ||
| 170 | + * | ||
| 171 | + * 枚举 {@link WxConsts.EventType} | ||
| 172 | + */ | ||
| 173 | + private String event; | ||
| 174 | + /** | ||
| 175 | + * 事件 Key | ||
| 176 | + * | ||
| 177 | + * 1. {@link WxConsts.EventType} 的 SCAN:qrscene_ 为前缀,后面为二维码的参数值 | ||
| 178 | + * 2. {@link WxConsts.EventType} 的 CLICK:与自定义菜单接口中 KEY 值对应 | ||
| 179 | + */ | ||
| 180 | + private String eventKey; | ||
| 205 | 181 | ||
| 206 | } | 182 | } |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/fansmsg/MpMessageMapper.java renamed to yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/message/MpMessageMapper.java
| 1 | -package cn.iocoder.yudao.module.mp.dal.mysql.fansmsg; | 1 | +package cn.iocoder.yudao.module.mp.dal.mysql.message; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| @@ -13,7 +13,7 @@ public interface MpMessageMapper extends BaseMapperX<MpMessageDO> { | @@ -13,7 +13,7 @@ public interface MpMessageMapper extends BaseMapperX<MpMessageDO> { | ||
| 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()) | 16 | + .eqIfPresent(MpMessageDO::getOpenid, reqVO.getOpenId()) |
| 17 | // .likeIfPresent(MpMessageDO::getNickname, reqVO.getNickname()) | 17 | // .likeIfPresent(MpMessageDO::getNickname, reqVO.getNickname()) |
| 18 | .eqIfPresent(MpMessageDO::getType, reqVO.getType()) | 18 | .eqIfPresent(MpMessageDO::getType, reqVO.getType()) |
| 19 | .orderByDesc(MpMessageDO::getId)); | 19 | .orderByDesc(MpMessageDO::getId)); |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/accountfans/MpUserMapper.java renamed to yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/user/MpUserMapper.java
| 1 | -package cn.iocoder.yudao.module.mp.dal.mysql.accountfans; | 1 | +package cn.iocoder.yudao.module.mp.dal.mysql.user; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/config/MpConfiguration.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.mp.framework.mp.config; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.mp.framework.mp.config; | ||
| 3 | import cn.iocoder.yudao.module.mp.framework.mp.core.DefaultMpServiceFactory; | 3 | import cn.iocoder.yudao.module.mp.framework.mp.core.DefaultMpServiceFactory; |
| 4 | import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; | 4 | import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; |
| 5 | import cn.iocoder.yudao.module.mp.service.handler.menu.MenuHandler; | 5 | import cn.iocoder.yudao.module.mp.service.handler.menu.MenuHandler; |
| 6 | -import cn.iocoder.yudao.module.mp.service.handler.message.MessageLogHandler; | 6 | +import cn.iocoder.yudao.module.mp.service.handler.message.MessageReceiveHandler; |
| 7 | import cn.iocoder.yudao.module.mp.service.handler.message.MessageAutoReplyHandler; | 7 | import cn.iocoder.yudao.module.mp.service.handler.message.MessageAutoReplyHandler; |
| 8 | import cn.iocoder.yudao.module.mp.service.handler.other.KfSessionHandler; | 8 | import cn.iocoder.yudao.module.mp.service.handler.other.KfSessionHandler; |
| 9 | import cn.iocoder.yudao.module.mp.service.handler.other.NullHandler; | 9 | import cn.iocoder.yudao.module.mp.service.handler.other.NullHandler; |
| @@ -36,7 +36,7 @@ public class MpConfiguration { | @@ -36,7 +36,7 @@ public class MpConfiguration { | ||
| 36 | @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") | 36 | @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") |
| 37 | public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps, | 37 | public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps, |
| 38 | WxMpProperties wxMpProperties, | 38 | WxMpProperties wxMpProperties, |
| 39 | - MessageLogHandler logHandler, | 39 | + MessageReceiveHandler logHandler, |
| 40 | KfSessionHandler kfSessionHandler, | 40 | KfSessionHandler kfSessionHandler, |
| 41 | StoreCheckNotifyHandler storeCheckNotifyHandler, | 41 | StoreCheckNotifyHandler storeCheckNotifyHandler, |
| 42 | MenuHandler menuHandler, | 42 | MenuHandler menuHandler, |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/DefaultMpServiceFactory.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.mp.framework.mp.core; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.mp.framework.mp.core; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | 3 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |
| 4 | import cn.iocoder.yudao.module.mp.service.handler.menu.MenuHandler; | 4 | import cn.iocoder.yudao.module.mp.service.handler.menu.MenuHandler; |
| 5 | -import cn.iocoder.yudao.module.mp.service.handler.message.MessageLogHandler; | 5 | +import cn.iocoder.yudao.module.mp.service.handler.message.MessageReceiveHandler; |
| 6 | import cn.iocoder.yudao.module.mp.service.handler.message.MessageAutoReplyHandler; | 6 | import cn.iocoder.yudao.module.mp.service.handler.message.MessageAutoReplyHandler; |
| 7 | import cn.iocoder.yudao.module.mp.service.handler.other.KfSessionHandler; | 7 | import cn.iocoder.yudao.module.mp.service.handler.other.KfSessionHandler; |
| 8 | import cn.iocoder.yudao.module.mp.service.handler.other.NullHandler; | 8 | import cn.iocoder.yudao.module.mp.service.handler.other.NullHandler; |
| @@ -49,7 +49,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { | @@ -49,7 +49,7 @@ public class DefaultMpServiceFactory implements MpServiceFactory { | ||
| 49 | 49 | ||
| 50 | // ========== 各种 Handler ========== | 50 | // ========== 各种 Handler ========== |
| 51 | 51 | ||
| 52 | - private final MessageLogHandler logHandler; | 52 | + private final MessageReceiveHandler logHandler; |
| 53 | private final KfSessionHandler kfSessionHandler; | 53 | private final KfSessionHandler kfSessionHandler; |
| 54 | private final StoreCheckNotifyHandler storeCheckNotifyHandler; | 54 | private final StoreCheckNotifyHandler storeCheckNotifyHandler; |
| 55 | private final MenuHandler menuHandler; | 55 | private final MenuHandler menuHandler; |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageAutoReplyHandler.java
| @@ -52,91 +52,91 @@ public class MessageAutoReplyHandler implements WxMpMessageHandler { | @@ -52,91 +52,91 @@ public class MessageAutoReplyHandler implements WxMpMessageHandler { | ||
| 52 | log.info("收到信息内容:{}", JsonUtils.toJsonString(wxMessage)); | 52 | log.info("收到信息内容:{}", JsonUtils.toJsonString(wxMessage)); |
| 53 | log.info("关键字:{}", wxMessage.getContent()); | 53 | log.info("关键字:{}", wxMessage.getContent()); |
| 54 | 54 | ||
| 55 | - if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) { | ||
| 56 | - //可以选择将消息保存到本地 | ||
| 57 | - | ||
| 58 | - // 获取微信用户基本信息 | ||
| 59 | - try { | ||
| 60 | - WxMpUser wxmpUser = weixinService.getUserService() | ||
| 61 | - .userInfo(wxMessage.getFromUser(), null); | ||
| 62 | - if (wxmpUser != null) { | ||
| 63 | - MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); | ||
| 64 | - if (wxAccount != null) { | ||
| 65 | - | ||
| 66 | - if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) { | ||
| 67 | - WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO(); | ||
| 68 | - wxFansMsg.setOpenid(wxmpUser.getOpenId()); | ||
| 69 | - try { | ||
| 70 | - wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8")); | ||
| 71 | - } catch (UnsupportedEncodingException e) { | ||
| 72 | - e.printStackTrace(); | ||
| 73 | - } | ||
| 74 | - wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl()); | ||
| 75 | - wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId())); | ||
| 76 | - wxFansMsg.setMsgType(wxMessage.getMsgType()); | ||
| 77 | - wxFansMsg.setContent(wxMessage.getContent()); | ||
| 78 | - wxFansMsg.setIsRes("1"); | ||
| 79 | - | ||
| 80 | - //组装回复消息 | ||
| 81 | - String content = processContent(wxMessage); | ||
| 82 | - content = HtmlUtil.escape(content); | ||
| 83 | - wxFansMsg.setResContent(content); | ||
| 84 | - | ||
| 85 | - mpMessageService.createWxFansMsg(wxFansMsg); | ||
| 86 | - return new TextBuilder().build(content, wxMessage, weixinService); | ||
| 87 | - | ||
| 88 | - } | ||
| 89 | - if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.IMAGE)) { | ||
| 90 | - WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO(); | ||
| 91 | - wxFansMsg.setOpenid(wxmpUser.getOpenId()); | ||
| 92 | - try { | ||
| 93 | - wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8")); | ||
| 94 | - } catch (UnsupportedEncodingException e) { | ||
| 95 | - e.printStackTrace(); | ||
| 96 | - } | ||
| 97 | - wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl()); | ||
| 98 | - wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId())); | ||
| 99 | - wxFansMsg.setMsgType(wxMessage.getMsgType()); | ||
| 100 | - wxFansMsg.setMediaId(wxMessage.getMediaId()); | ||
| 101 | - wxFansMsg.setPicUrl(wxMessage.getPicUrl()); | ||
| 102 | - String downloadDirStr = fileApi.createFile(HttpUtil.downloadBytes(wxMessage.getPicUrl())); | ||
| 103 | - File downloadDir = new File(downloadDirStr); | ||
| 104 | - if (!downloadDir.exists()) { | ||
| 105 | - downloadDir.mkdirs(); | ||
| 106 | - } | ||
| 107 | - String filepath = downloadDirStr + System.currentTimeMillis() + ".png"; | ||
| 108 | - //微信pic url下载到本地,防止失效 | ||
| 109 | - long size = HttpUtil.downloadFile(wxMessage.getPicUrl(), FileUtil.file(filepath)); | ||
| 110 | - log.info("download pic size : {}", size); | ||
| 111 | - wxFansMsg.setPicPath(filepath); | ||
| 112 | - wxFansMsg.setIsRes("0"); | ||
| 113 | - mpMessageService.createWxFansMsg(wxFansMsg); | ||
| 114 | - } | ||
| 115 | - | ||
| 116 | - } | ||
| 117 | - } | ||
| 118 | - } catch (WxErrorException e) { | ||
| 119 | - if (e.getError().getErrorCode() == 48001) { | ||
| 120 | - log.info("该公众号没有获取用户信息权限!"); | ||
| 121 | - } | ||
| 122 | - } catch (Exception e) { | ||
| 123 | - e.printStackTrace(); | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | - //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服 | ||
| 129 | - try { | ||
| 130 | - if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服") | ||
| 131 | - && weixinService.getKefuService().kfOnlineList() | ||
| 132 | - .getKfOnlineList().size() > 0) { | ||
| 133 | - return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE() | ||
| 134 | - .fromUser(wxMessage.getToUser()) | ||
| 135 | - .toUser(wxMessage.getFromUser()).build(); | ||
| 136 | - } | ||
| 137 | - } catch (WxErrorException e) { | ||
| 138 | - e.printStackTrace(); | ||
| 139 | - } | 55 | +// if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) { |
| 56 | +// //可以选择将消息保存到本地 | ||
| 57 | +// | ||
| 58 | +// // 获取微信用户基本信息 | ||
| 59 | +// try { | ||
| 60 | +// WxMpUser wxmpUser = weixinService.getUserService() | ||
| 61 | +// .userInfo(wxMessage.getFromUser(), null); | ||
| 62 | +// if (wxmpUser != null) { | ||
| 63 | +// MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); | ||
| 64 | +// if (wxAccount != null) { | ||
| 65 | +// | ||
| 66 | +// if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) { | ||
| 67 | +// WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO(); | ||
| 68 | +// wxFansMsg.setOpenid(wxmpUser.getOpenId()); | ||
| 69 | +// try { | ||
| 70 | +// wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8")); | ||
| 71 | +// } catch (UnsupportedEncodingException e) { | ||
| 72 | +// e.printStackTrace(); | ||
| 73 | +// } | ||
| 74 | +// wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl()); | ||
| 75 | +// wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId())); | ||
| 76 | +// wxFansMsg.setMsgType(wxMessage.getMsgType()); | ||
| 77 | +// wxFansMsg.setContent(wxMessage.getContent()); | ||
| 78 | +// wxFansMsg.setIsRes("1"); | ||
| 79 | +// | ||
| 80 | +// //组装回复消息 | ||
| 81 | +// String content = processContent(wxMessage); | ||
| 82 | +// content = HtmlUtil.escape(content); | ||
| 83 | +// wxFansMsg.setResContent(content); | ||
| 84 | +// | ||
| 85 | +// mpMessageService.createWxFansMsg(wxFansMsg); | ||
| 86 | +// return new TextBuilder().build(content, wxMessage, weixinService); | ||
| 87 | +// | ||
| 88 | +// } | ||
| 89 | +// if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.IMAGE)) { | ||
| 90 | +// WxFansMsgCreateReqVO wxFansMsg = new WxFansMsgCreateReqVO(); | ||
| 91 | +// wxFansMsg.setOpenid(wxmpUser.getOpenId()); | ||
| 92 | +// try { | ||
| 93 | +// wxFansMsg.setNickname(wxmpUser.getNickname().getBytes("UTF-8")); | ||
| 94 | +// } catch (UnsupportedEncodingException e) { | ||
| 95 | +// e.printStackTrace(); | ||
| 96 | +// } | ||
| 97 | +// wxFansMsg.setHeadimgUrl(wxmpUser.getHeadImgUrl()); | ||
| 98 | +// wxFansMsg.setWxAccountId(String.valueOf(wxAccount.getId())); | ||
| 99 | +// wxFansMsg.setMsgType(wxMessage.getMsgType()); | ||
| 100 | +// wxFansMsg.setMediaId(wxMessage.getMediaId()); | ||
| 101 | +// wxFansMsg.setPicUrl(wxMessage.getPicUrl()); | ||
| 102 | +// String downloadDirStr = fileApi.createFile(HttpUtil.downloadBytes(wxMessage.getPicUrl())); | ||
| 103 | +// File downloadDir = new File(downloadDirStr); | ||
| 104 | +// if (!downloadDir.exists()) { | ||
| 105 | +// downloadDir.mkdirs(); | ||
| 106 | +// } | ||
| 107 | +// String filepath = downloadDirStr + System.currentTimeMillis() + ".png"; | ||
| 108 | +// //微信pic url下载到本地,防止失效 | ||
| 109 | +// long size = HttpUtil.downloadFile(wxMessage.getPicUrl(), FileUtil.file(filepath)); | ||
| 110 | +// log.info("download pic size : {}", size); | ||
| 111 | +// wxFansMsg.setPicPath(filepath); | ||
| 112 | +// wxFansMsg.setIsRes("0"); | ||
| 113 | +// mpMessageService.createWxFansMsg(wxFansMsg); | ||
| 114 | +// } | ||
| 115 | +// | ||
| 116 | +// } | ||
| 117 | +// } | ||
| 118 | +// } catch (WxErrorException e) { | ||
| 119 | +// if (e.getError().getErrorCode() == 48001) { | ||
| 120 | +// log.info("该公众号没有获取用户信息权限!"); | ||
| 121 | +// } | ||
| 122 | +// } catch (Exception e) { | ||
| 123 | +// e.printStackTrace(); | ||
| 124 | +// } | ||
| 125 | +// | ||
| 126 | +// } | ||
| 127 | +// | ||
| 128 | +// //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服 | ||
| 129 | +// try { | ||
| 130 | +// if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服") | ||
| 131 | +// && weixinService.getKefuService().kfOnlineList() | ||
| 132 | +// .getKfOnlineList().size() > 0) { | ||
| 133 | +// return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE() | ||
| 134 | +// .fromUser(wxMessage.getToUser()) | ||
| 135 | +// .toUser(wxMessage.getFromUser()).build(); | ||
| 136 | +// } | ||
| 137 | +// } catch (WxErrorException e) { | ||
| 138 | +// e.printStackTrace(); | ||
| 139 | +// } | ||
| 140 | 140 | ||
| 141 | //组装默认回复消息 | 141 | //组装默认回复消息 |
| 142 | return new TextBuilder().build("测试", wxMessage, weixinService); | 142 | return new TextBuilder().build("测试", wxMessage, weixinService); |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageLogHandler.java renamed to yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageReceiveHandler.java
| 1 | package cn.iocoder.yudao.module.mp.service.handler.message; | 1 | package cn.iocoder.yudao.module.mp.service.handler.message; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 3 | +import cn.iocoder.yudao.module.mp.framework.mp.core.context.MpContextHolder; |
| 4 | +import cn.iocoder.yudao.module.mp.service.message.MpMessageService; | ||
| 4 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 5 | import me.chanjar.weixin.common.session.WxSessionManager; | 6 | import me.chanjar.weixin.common.session.WxSessionManager; |
| 6 | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | 7 | import me.chanjar.weixin.mp.api.WxMpMessageHandler; |
| @@ -9,21 +10,26 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | @@ -9,21 +10,26 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | ||
| 9 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | 10 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
| 10 | import org.springframework.stereotype.Component; | 11 | import org.springframework.stereotype.Component; |
| 11 | 12 | ||
| 13 | +import javax.annotation.Resource; | ||
| 12 | import java.util.Map; | 14 | import java.util.Map; |
| 13 | 15 | ||
| 14 | /** | 16 | /** |
| 15 | * 保存微信消息的事件处理器 | 17 | * 保存微信消息的事件处理器 |
| 16 | * | 18 | * |
| 17 | - * // TODO 芋艿:实现一下 | 19 | + * @author 芋道源码 |
| 18 | */ | 20 | */ |
| 19 | @Component | 21 | @Component |
| 20 | @Slf4j | 22 | @Slf4j |
| 21 | -public class MessageLogHandler implements WxMpMessageHandler { | 23 | +public class MessageReceiveHandler implements WxMpMessageHandler { |
| 24 | + | ||
| 25 | + @Resource | ||
| 26 | + private MpMessageService mpMessageService; | ||
| 22 | 27 | ||
| 23 | @Override | 28 | @Override |
| 24 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | 29 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, |
| 25 | WxMpService wxMpService, WxSessionManager sessionManager) { | 30 | WxMpService wxMpService, WxSessionManager sessionManager) { |
| 26 | - log.info("接收到请求消息,内容:{}", JsonUtils.toJsonString(wxMessage)); | 31 | + log.info("[handle][接收到请求消息,内容:{}]", wxMessage); |
| 32 | + mpMessageService.createFromUser(MpContextHolder.getAppId(), wxMessage); | ||
| 27 | return null; | 33 | return null; |
| 28 | } | 34 | } |
| 29 | 35 |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mp.service.message; | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.mp.service.message; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO; | 4 | import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO; |
| 5 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; | 5 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; |
| 6 | +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | ||
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| 8 | * 粉丝消息表 Service 接口 | 9 | * 粉丝消息表 Service 接口 |
| @@ -12,11 +13,18 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; | @@ -12,11 +13,18 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; | ||
| 12 | public interface MpMessageService { | 13 | public interface MpMessageService { |
| 13 | 14 | ||
| 14 | /** | 15 | /** |
| 15 | - * 获得粉丝消息表 分页 | 16 | + * 获得粉丝消息表分页 |
| 16 | * | 17 | * |
| 17 | * @param pageReqVO 分页查询 | 18 | * @param pageReqVO 分页查询 |
| 18 | * @return 粉丝消息表 分页 | 19 | * @return 粉丝消息表 分页 |
| 19 | */ | 20 | */ |
| 20 | PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO); | 21 | PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO); |
| 21 | 22 | ||
| 23 | + /** | ||
| 24 | + * 保存粉丝消息,来自用户发送 | ||
| 25 | + * | ||
| 26 | + * @param appId 微信公众号 appId | ||
| 27 | + * @param wxMessage 消息 | ||
| 28 | + */ | ||
| 29 | + void createFromUser(String appId, WxMpXmlMessage wxMessage); | ||
| 22 | } | 30 | } |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java
| 1 | package cn.iocoder.yudao.module.mp.service.message; | 1 | package cn.iocoder.yudao.module.mp.service.message; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.io.FileTypeUtil; | ||
| 4 | +import cn.hutool.core.io.FileUtil; | ||
| 5 | +import cn.hutool.core.io.IoUtil; | ||
| 6 | +import cn.hutool.core.lang.Assert; | ||
| 7 | +import cn.hutool.core.util.StrUtil; | ||
| 8 | +import cn.iocoder.yudao.framework.common.util.io.FileUtils; | ||
| 9 | +import cn.iocoder.yudao.module.infra.api.file.FileApi; | ||
| 10 | +import cn.iocoder.yudao.module.mp.convert.message.MpMessageConvert; | ||
| 11 | +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||
| 3 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; | 12 | import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; |
| 13 | +import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; | ||
| 14 | +import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum; | ||
| 15 | +import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; | ||
| 16 | +import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | ||
| 17 | +import cn.iocoder.yudao.module.mp.service.user.MpUserService; | ||
| 18 | +import lombok.SneakyThrows; | ||
| 19 | +import lombok.extern.slf4j.Slf4j; | ||
| 20 | +import me.chanjar.weixin.common.error.WxErrorException; | ||
| 21 | +import me.chanjar.weixin.mp.api.WxMpService; | ||
| 22 | +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | ||
| 23 | +import org.springframework.context.annotation.Lazy; | ||
| 4 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
| 5 | 25 | ||
| 6 | import javax.annotation.Resource; | 26 | import javax.annotation.Resource; |
| @@ -10,7 +30,9 @@ import org.springframework.validation.annotation.Validated; | @@ -10,7 +30,9 @@ import org.springframework.validation.annotation.Validated; | ||
| 10 | import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*; | 30 | import cn.iocoder.yudao.module.mp.controller.admin.message.vo.*; |
| 11 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 31 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 12 | 32 | ||
| 13 | -import cn.iocoder.yudao.module.mp.dal.mysql.fansmsg.MpMessageMapper; | 33 | +import cn.iocoder.yudao.module.mp.dal.mysql.message.MpMessageMapper; |
| 34 | + | ||
| 35 | +import java.io.File; | ||
| 14 | 36 | ||
| 15 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 37 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 16 | 38 | ||
| @@ -21,14 +43,77 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | @@ -21,14 +43,77 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | ||
| 21 | */ | 43 | */ |
| 22 | @Service | 44 | @Service |
| 23 | @Validated | 45 | @Validated |
| 46 | +@Slf4j | ||
| 24 | public class MpMessageServiceImpl implements MpMessageService { | 47 | public class MpMessageServiceImpl implements MpMessageService { |
| 25 | 48 | ||
| 26 | @Resource | 49 | @Resource |
| 50 | + @Lazy // 延迟加载,避免循环依赖 | ||
| 51 | + private MpAccountService mpAccountService; | ||
| 52 | + @Resource | ||
| 53 | + private MpUserService mpUserService; | ||
| 54 | + | ||
| 55 | + @Resource | ||
| 27 | private MpMessageMapper mpMessageMapper; | 56 | private MpMessageMapper mpMessageMapper; |
| 28 | 57 | ||
| 58 | + @Resource | ||
| 59 | + @Lazy // 延迟加载,解决循环依赖的问题 | ||
| 60 | + private MpServiceFactory mpServiceFactory; | ||
| 61 | + | ||
| 62 | + @Resource | ||
| 63 | + private FileApi fileApi; | ||
| 64 | + | ||
| 29 | @Override | 65 | @Override |
| 30 | public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) { | 66 | public PageResult<MpMessageDO> getWxFansMsgPage(MpMessagePageReqVO pageReqVO) { |
| 31 | return mpMessageMapper.selectPage(pageReqVO); | 67 | return mpMessageMapper.selectPage(pageReqVO); |
| 32 | } | 68 | } |
| 33 | 69 | ||
| 70 | + @Override | ||
| 71 | + public void createFromUser(String appId, WxMpXmlMessage wxMessage) { | ||
| 72 | + WxMpService mpService = mpServiceFactory.getRequiredMpService(appId); | ||
| 73 | + // 获得关联信息 | ||
| 74 | + MpAccountDO account = mpAccountService.getAccountFromCache(appId); | ||
| 75 | + Assert.notNull(account, "公众号账号({}) 不存在", appId); | ||
| 76 | + MpUserDO user = mpUserService.getUser(appId, wxMessage.getFromUser()); | ||
| 77 | + Assert.notNull(account, "公众号粉丝({}/{}) 不存在", appId, wxMessage.getFromUser()); | ||
| 78 | + | ||
| 79 | + // 记录消息 | ||
| 80 | + MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user); | ||
| 81 | + message.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom()); | ||
| 82 | + if (StrUtil.isNotEmpty(message.getMediaId())) { | ||
| 83 | + message.setMediaUrl(mediaDownload(mpService, message.getMediaId())); | ||
| 84 | + } | ||
| 85 | + if (StrUtil.isNotEmpty(message.getThumbMediaId())) { | ||
| 86 | + message.setThumbMediaUrl(mediaDownload(mpService, message.getThumbMediaId())); | ||
| 87 | + } | ||
| 88 | + mpMessageMapper.insert(message); | ||
| 89 | + | ||
| 90 | +// WxConsts.MenuButtonType.VIEW | ||
| 91 | +// wxMessage.getEventKey() | ||
| 92 | + | ||
| 93 | +// WxConsts.MenuButtonType.CLICK | ||
| 94 | +// wxMessage.getEventKey() | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * 下载微信媒体文件的内容,并上传到文件服务 | ||
| 99 | + * | ||
| 100 | + * 为什么要下载?媒体文件在微信后台保存时间为 3 天,即 3 天后 media_id 失效。 | ||
| 101 | + * | ||
| 102 | + * @param mpService 微信公众号 Service | ||
| 103 | + * @param mediaId 媒体文件编号 | ||
| 104 | + * @return 上传后的 URL | ||
| 105 | + */ | ||
| 106 | + private String mediaDownload(WxMpService mpService, String mediaId) { | ||
| 107 | + try { | ||
| 108 | + // 第一步,从公众号下载媒体文件 | ||
| 109 | + File file = mpService.getMaterialService().mediaDownload(mediaId); | ||
| 110 | + // 第二步,上传到文件服务 | ||
| 111 | + String path = mediaId + "." + FileTypeUtil.getType(file); | ||
| 112 | + return fileApi.createFile(path, FileUtil.readBytes(file)); | ||
| 113 | + } catch (WxErrorException e) { | ||
| 114 | + log.error("[mediaDownload][media({}) 下载失败]", mediaId); | ||
| 115 | + } | ||
| 116 | + return null; | ||
| 117 | + } | ||
| 118 | + | ||
| 34 | } | 119 | } |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserService.java
| @@ -24,6 +24,15 @@ public interface MpUserService { | @@ -24,6 +24,15 @@ public interface MpUserService { | ||
| 24 | MpUserDO getUser(Long id); | 24 | MpUserDO getUser(Long id); |
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 27 | + * 使用 appId + openId,获得微信公众号粉丝 | ||
| 28 | + * | ||
| 29 | + * @param appId 微信公众号 appId | ||
| 30 | + * @param openId 微信公众号 openId | ||
| 31 | + * @return 微信公众号粉丝 | ||
| 32 | + */ | ||
| 33 | + MpUserDO getUser(String appId, String openId); | ||
| 34 | + | ||
| 35 | + /** | ||
| 27 | * 获得微信公众号粉丝列表 | 36 | * 获得微信公众号粉丝列表 |
| 28 | * | 37 | * |
| 29 | * @param ids 编号 | 38 | * @param ids 编号 |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserServiceImpl.java
| @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.user.vo.MpUserPageReqVO; | @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.mp.controller.admin.user.vo.MpUserPageReqVO; | ||
| 6 | import cn.iocoder.yudao.module.mp.convert.user.MpUserConvert; | 6 | import cn.iocoder.yudao.module.mp.convert.user.MpUserConvert; |
| 7 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | 7 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |
| 8 | import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; | 8 | import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; |
| 9 | -import cn.iocoder.yudao.module.mp.dal.mysql.accountfans.MpUserMapper; | 9 | +import cn.iocoder.yudao.module.mp.dal.mysql.user.MpUserMapper; |
| 10 | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | 10 | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; |
| 11 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
| 12 | import me.chanjar.weixin.mp.bean.result.WxMpUser; | 12 | import me.chanjar.weixin.mp.bean.result.WxMpUser; |
| @@ -42,6 +42,11 @@ public class MpUserServiceImpl implements MpUserService { | @@ -42,6 +42,11 @@ public class MpUserServiceImpl implements MpUserService { | ||
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | @Override | 44 | @Override |
| 45 | + public MpUserDO getUser(String appId, String openId) { | ||
| 46 | + return mpUserMapper.selectByAppIdAndOpenid(appId, openId); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Override | ||
| 45 | public List<MpUserDO> getUserList(Collection<Long> ids) { | 50 | public List<MpUserDO> getUserList(Collection<Long> ids) { |
| 46 | return mpUserMapper.selectBatchIds(ids); | 51 | return mpUserMapper.selectBatchIds(ids); |
| 47 | } | 52 | } |