diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http index 6979d593a..b9f9721c9 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http @@ -3,3 +3,31 @@ GET {{baseUrl}}/mp/message/page?accountId=1&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} + +### 请求 /mp/message/send 接口 => 成功(文本) +POST {{baseUrl}}/mp/message/send +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "userId": 3, + "type": "text", + "content": "测试消息" +} + +### 请求 /mp/message/send 接口 => 成功(音乐) +POST {{baseUrl}}/mp/message/send +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +{ + "userId": 3, + "type": "music", + "title": "测试音乐标题", + "description": "测试音乐内容", + "musicUrl": "https://www.iocoder.cn/xx.mp3", + "hqMusicUrl": "https://www.iocoder.cn/xx_high.mp3", + "thumbMediaId": "s98Iveeg9vDVFwa9q0u8-zSfdKe3xIzAm7wCrFE4WKGPIo4d9qAhtC-n6qvnyWyH" +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.java index 443891b24..9c83172aa 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO; import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageRespVO; +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageSendReqVO; import cn.iocoder.yudao.module.mp.convert.message.MpMessageConvert; import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; import cn.iocoder.yudao.module.mp.service.message.MpMessageService; @@ -11,9 +12,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.Valid; @@ -37,4 +36,11 @@ public class MpMessageController { return success(MpMessageConvert.INSTANCE.convertPage(pageResult)); } + @PostMapping("/send") +// @ApiOperation("获得粉丝消息表分页") +// @PreAuthorize("@ss.hasPermission('mp:message:query')") + public CommonResult sendMessage(@Valid @RequestBody MpMessageSendReqVO reqVO) { + return success(mpMessageService.sendKefuMessage(reqVO)); + } + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/MpMessageSendReqVO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/MpMessageSendReqVO.java new file mode 100644 index 000000000..cb673b2f6 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/MpMessageSendReqVO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.mp.controller.admin.message.vo; + +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; +import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel("管理后台 - 公众号消息发送 Request VO") +@Data +public class MpMessageSendReqVO { + + @ApiModelProperty(value = "公众号粉丝的编号", required = true, example = "1024") + @NotNull(message = "公众号粉丝的编号不能为空") + private Long userId; + + // ========== 消息内容 ========== + + @ApiModelProperty(value = "消息类型", required = true, example = "text", notes = "TEXT/IMAGE/VOICE/VIDEO/NEWS") + @NotEmpty(message = "消息类型不能为空") + public String type; + + @ApiModelProperty(value = "消息内容", required = true, example = "你好呀") + @NotEmpty(message = "消息内容不能为空", groups = TextGroup.class) + private String content; + + @ApiModelProperty(value = "媒体 ID", required = true, example = "qqc_2Fot30Jse-HDoZmo5RrUDijz2nGUkP") + @NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class}) + private String mediaId; + + @ApiModelProperty(value = "标题", required = true, example = "没有标题") + @NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class) + private String title; + + @ApiModelProperty(value = "描述", required = true, example = "你猜") + @NotEmpty(message = "消息内容不能为空", groups = VideoGroup.class) + private String description; + + @ApiModelProperty(value = "缩略图的媒体 id", required = true, example = "qqc_2Fot30Jse-HDoZmo5RrUDijz2nGUkP") + @NotEmpty(message = "缩略图的媒体 id 不能为空", groups = MusicGroup.class) + private String thumbMediaId; + + @ApiModelProperty(value = "图文消息", required = true) + @Valid + @NotNull(message = "图文消息不能为空", groups = NewsGroup.class) + private List articles; + + @ApiModelProperty(value = "音乐链接", example = "https://www.iocoder.cn/music.mp3", notes = "消息类型为 MUSIC 时") + private String musicUrl; + + @ApiModelProperty(value = "高质量音乐链接", example = "https://www.iocoder.cn/music.mp3", notes = "消息类型为 MUSIC 时") + private String hqMusicUrl; + +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/message/MpMessageConvert.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/message/MpMessageConvert.java index cbd2a7118..670b6c807 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/message/MpMessageConvert.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/message/MpMessageConvert.java @@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.mp.convert.message; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageRespVO; +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageSendReqVO; import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO; import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; @@ -16,7 +18,6 @@ import org.mapstruct.Mapping; import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; -import java.util.Collections; import java.util.List; @Mapper @@ -30,12 +31,6 @@ public interface MpMessageConvert { PageResult convertPage(PageResult page); - @Mappings(value = { - @Mapping(source = "msgType", target = "type"), - @Mapping(target = "createTime", ignore = true), - }) - MpMessageDO convert(WxMpXmlMessage wxMessage); - default MpMessageDO convert(WxMpXmlMessage wxMessage, MpAccountDO account, MpUserDO user) { MpMessageDO message = convert(wxMessage); if (account != null) { @@ -46,6 +41,11 @@ public interface MpMessageConvert { } return message; } + @Mappings(value = { + @Mapping(source = "msgType", target = "type"), + @Mapping(target = "createTime", ignore = true), + }) + MpMessageDO convert(WxMpXmlMessage bean); default MpMessageDO convert(MpMessageSendOutReqBO sendReqBO, MpAccountDO account, MpUserDO user) { // 构建消息 @@ -65,6 +65,11 @@ public interface MpMessageConvert { break; case WxConsts.XmlMsgType.NEWS: // 5. 图文 message.setArticles(sendReqBO.getArticles()); + case WxConsts.XmlMsgType.MUSIC: // 6. 音乐 + message.setTitle(sendReqBO.getTitle()).setDescription(sendReqBO.getDescription()) + .setMusicUrl(sendReqBO.getMusicUrl()).setHqMusicUrl(sendReqBO.getHqMusicUrl()) + .setThumbMediaId(sendReqBO.getThumbMediaId()); +// .setThumbMediaUrl(sendReqBO.getThumbMediaUrl()); TODO 芋艿:url 待确定 break; default: throw new IllegalArgumentException("不支持的消息类型:" + message.getType()); @@ -81,33 +86,88 @@ public interface MpMessageConvert { } default WxMpXmlOutMessage convert02(MpMessageDO message, MpAccountDO account) { - BaseBuilder messageBuilder; + BaseBuilder builder; // 个性化字段 switch (message.getType()) { case WxConsts.XmlMsgType.TEXT: - messageBuilder = WxMpXmlOutMessage.TEXT().content(message.getContent()); + builder = WxMpXmlOutMessage.TEXT().content(message.getContent()); break; case WxConsts.XmlMsgType.IMAGE: - messageBuilder = WxMpXmlOutMessage.IMAGE().mediaId(message.getMediaId()); + builder = WxMpXmlOutMessage.IMAGE().mediaId(message.getMediaId()); break; case WxConsts.XmlMsgType.VOICE: - messageBuilder = WxMpXmlOutMessage.VOICE().mediaId(message.getMediaId()); + builder = WxMpXmlOutMessage.VOICE().mediaId(message.getMediaId()); break; case WxConsts.XmlMsgType.VIDEO: - messageBuilder = WxMpXmlOutMessage.VIDEO().mediaId(message.getMediaId()) + builder = WxMpXmlOutMessage.VIDEO().mediaId(message.getMediaId()) .title(message.getTitle()).description(message.getDescription()); break; case WxConsts.XmlMsgType.NEWS: - messageBuilder = WxMpXmlOutMessage.NEWS().articles(convertList02(message.getArticles())); + builder = WxMpXmlOutMessage.NEWS().articles(convertList02(message.getArticles())); + break; + case WxConsts.XmlMsgType.MUSIC: + builder = WxMpXmlOutMessage.MUSIC().title(message.getTitle()).description(message.getDescription()) + .musicUrl(message.getMusicUrl()).hqMusicUrl(message.getHqMusicUrl()) + .thumbMediaId(message.getThumbMediaId()); break; default: throw new IllegalArgumentException("不支持的消息类型:" + message.getType()); } // 通用字段 - messageBuilder.fromUser(account.getAccount()); - messageBuilder.toUser(message.getOpenid()); - return messageBuilder.build(); + builder.fromUser(account.getAccount()); + builder.toUser(message.getOpenid()); + return builder.build(); } List convertList02(List list); + default WxMpKefuMessage convert(MpMessageSendReqVO sendReqVO, MpUserDO user) { + me.chanjar.weixin.mp.builder.kefu.BaseBuilder builder; + // 个性化字段 + switch (sendReqVO.getType()) { + case WxConsts.KefuMsgType.TEXT: + builder = WxMpKefuMessage.TEXT().content(sendReqVO.getContent()); + break; + case WxConsts.KefuMsgType.IMAGE: + builder = WxMpKefuMessage.IMAGE().mediaId(sendReqVO.getMediaId()); + break; + case WxConsts.KefuMsgType.VOICE: + builder = WxMpKefuMessage.VOICE().mediaId(sendReqVO.getMediaId()); + break; + case WxConsts.KefuMsgType.VIDEO: + builder = WxMpKefuMessage.VIDEO().mediaId(sendReqVO.getMediaId()) + .title(sendReqVO.getTitle()).description(sendReqVO.getDescription()); + break; + case WxConsts.KefuMsgType.NEWS: + builder = WxMpKefuMessage.NEWS().articles(convertList03(sendReqVO.getArticles())); + break; + case WxConsts.KefuMsgType.MUSIC: + builder = WxMpKefuMessage.MUSIC().title(sendReqVO.getTitle()).description(sendReqVO.getDescription()) + .thumbMediaId(sendReqVO.getThumbMediaId()) + .musicUrl(sendReqVO.getMusicUrl()).hqMusicUrl(sendReqVO.getHqMusicUrl()); + break; + default: + throw new IllegalArgumentException("不支持的消息类型:" + sendReqVO.getType()); + } + // 通用字段 + builder.toUser(user.getOpenid()); + return builder.build(); + } + List convertList03(List list); + + default MpMessageDO convert(WxMpKefuMessage wxMessage, MpAccountDO account, MpUserDO user) { + MpMessageDO message = convert(wxMessage); + if (account != null) { + message.setAccountId(account.getId()).setAppId(account.getAppId()); + } + if (user != null) { + message.setUserId(user.getId()).setOpenid(user.getOpenid()); + } + return message; + } + @Mappings(value = { + @Mapping(source = "msgType", target = "type"), + @Mapping(target = "createTime", ignore = true), + }) + MpMessageDO convert(WxMpKefuMessage bean); + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java index d69b58f35..4c0d8790e 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java @@ -124,7 +124,7 @@ public class MpMessageDO extends BaseDO { /** * 缩略图的媒体 id,通过素材管理中的接口上传多媒体文件,得到的 id * - * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC */ private String thumbMediaId; /** @@ -176,6 +176,21 @@ public class MpMessageDO extends BaseDO { @TableField(typeHandler = ArticleTypeHandler.class) private List
articles; + /** + * 音乐链接 + * + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC + */ + private String musicUrl; + /** + * 高质量音乐链接 + * + * WIFI 环境优先使用该链接播放音乐 + * + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC + */ + private String hqMusicUrl; + // ========= 事件推送 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html /** diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/util/MpUtils.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/util/MpUtils.java index 4a01b09ba..ebf44e741 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/util/MpUtils.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/util/MpUtils.java @@ -39,6 +39,11 @@ public class MpUtils { */ public interface NewsGroup {} + /** + * Music 类型的消息,参数校验 Group + */ + public interface MusicGroup {} + /** * 校验消息的格式是否符合要求 * @@ -64,6 +69,9 @@ public class MpUtils { case WxConsts.XmlMsgType.NEWS: group = NewsGroup.class; break; + case WxConsts.XmlMsgType.MUSIC: + group = MusicGroup.class; + break; default: log.error("[validateMessage][未知的消息类型({})]", message); throw new IllegalArgumentException("不支持的消息类型:" + type); diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java index 8082f7e60..c997cae1d 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageService.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mp.service.message; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO; +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageSendReqVO; import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; @@ -45,4 +46,14 @@ public interface MpMessageService { */ WxMpXmlOutMessage sendOutMessage(@Valid MpMessageSendOutReqBO sendReqBO); + /** + * 使用公众号,给用户发送【客服】消息 + * + * 注意,该方法会真实发送消息 + * + * @param sendReqVO 消息内容 + * @return 消息编号 + */ + Long sendKefuMessage(MpMessageSendReqVO sendReqVO); + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java index ee7b8c46c..a71bc64a8 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.infra.api.file.FileApi; import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessagePageReqVO; +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.MpMessageSendReqVO; import cn.iocoder.yudao.module.mp.convert.message.MpMessageConvert; import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; @@ -21,6 +22,7 @@ import cn.iocoder.yudao.module.mp.service.user.MpUserService; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; import org.springframework.context.annotation.Lazy; @@ -77,12 +79,7 @@ public class MpMessageServiceImpl implements MpMessageService { // 记录消息 MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user); message.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom()); - if (StrUtil.isNotEmpty(message.getMediaId())) { - message.setMediaUrl(mediaDownload(mpService, message.getMediaId())); - } - if (StrUtil.isNotEmpty(message.getThumbMediaId())) { - message.setThumbMediaUrl(mediaDownload(mpService, message.getThumbMediaId())); - } + downloadMessageMedia(mpService, message); mpMessageMapper.insert(message); } @@ -106,6 +103,48 @@ public class MpMessageServiceImpl implements MpMessageService { return MpMessageConvert.INSTANCE.convert02(message, account); } + @Override + public Long sendKefuMessage(MpMessageSendReqVO sendReqVO) { + // 校验消息格式 + MpUtils.validateMessage(validator, sendReqVO.getType(), sendReqVO); + + // 获得关联信息 + MpUserDO user = mpUserService.getRequiredUser(sendReqVO.getUserId()); + MpAccountDO account = mpAccountService.getRequiredAccount(user.getAccountId()); + + // 发送客服消息 + WxMpKefuMessage wxMessage = MpMessageConvert.INSTANCE.convert(sendReqVO, user); + WxMpService mpService = mpServiceFactory.getRequiredMpService(user.getAppId()); + try { + boolean result = mpService.getKefuService().sendKefuMessage(wxMessage); + System.out.println(result); + } catch (WxErrorException e) { + throw new RuntimeException(e); + } + + // 记录消息 + MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user); + message.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom()); + downloadMessageMedia(mpService, message); + mpMessageMapper.insert(message); + return message.getId(); + } + + /** + * 下载消息使用到的媒体文件,并上传到文件服务 + * + * @param mpService 公众号 Service + * @param message 消息 + */ + private void downloadMessageMedia(WxMpService mpService, MpMessageDO message) { + if (StrUtil.isNotEmpty(message.getMediaId())) { + message.setMediaUrl(downloadMedia(mpService, message.getMediaId())); + } + if (StrUtil.isNotEmpty(message.getThumbMediaId())) { + message.setThumbMediaUrl(downloadMedia(mpService, message.getThumbMediaId())); + } + } + /** * 下载微信媒体文件的内容,并上传到文件服务 * @@ -115,7 +154,7 @@ public class MpMessageServiceImpl implements MpMessageService { * @param mediaId 媒体文件编号 * @return 上传后的 URL */ - private String mediaDownload(WxMpService mpService, String mediaId) { + private String downloadMedia(WxMpService mpService, String mediaId) { try { // 第一步,从公众号下载媒体文件 File file = mpService.getMaterialService().mediaDownload(mediaId); diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/bo/MpMessageSendOutReqBO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/bo/MpMessageSendOutReqBO.java index 26719fffe..04c209cc5 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/bo/MpMessageSendOutReqBO.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/message/bo/MpMessageSendOutReqBO.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.mp.service.message.bo; import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO; -import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils; import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils.*; import lombok.Data; import me.chanjar.weixin.common.api.WxConsts; @@ -36,7 +35,7 @@ public class MpMessageSendOutReqBO { /** * 消息类型 * - * 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS + * 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS、MUSIC */ @NotEmpty(message = "消息类型不能为空") public String type; @@ -56,6 +55,7 @@ public class MpMessageSendOutReqBO { */ @NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class}) private String mediaId; + // TODO 芋艿:考虑去掉 /** * 媒体 URL * @@ -64,6 +64,15 @@ public class MpMessageSendOutReqBO { @NotEmpty(message = "消息内容不能为空", groups = {ImageGroup.class, VoiceGroup.class, VideoGroup.class}) private String mediaUrl; + /** + * 缩略图的媒体 id + * + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO、MUSIC + */ + @NotEmpty(message = "消息内容不能为空", groups = {MusicGroup.class}) + private String thumbMediaId; + // TODO 芋艿:考虑去掉 + /** * 标题 * @@ -88,4 +97,18 @@ public class MpMessageSendOutReqBO { @NotNull(message = "图文消息不能为空", groups = NewsGroup.class) private List articles; + /** + * 音乐链接 + * + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC + */ + private String musicUrl; + + /** + * 高质量音乐链接 + * + * 消息类型为 {@link WxConsts.XmlMsgType} 的 MUSIC + */ + private String hqMusicUrl; + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserService.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserService.java index 61bb4f820..bdefc0e60 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserService.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/user/MpUserService.java @@ -9,6 +9,9 @@ import me.chanjar.weixin.mp.bean.result.WxMpUser; import java.util.Collection; import java.util.List; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.USER_NOT_EXISTS; + /** * 公众号粉丝 Service 接口 * @@ -33,6 +36,20 @@ public interface MpUserService { */ MpUserDO getUser(String appId, String openId); + /** + * 获得公众号粉丝 + * + * @param id 编号 + * @return 公众号粉丝 + */ + default MpUserDO getRequiredUser(Long id) { + MpUserDO user = getUser(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + return user; + } + /** * 获得公众号粉丝列表 * diff --git a/yudao-ui-admin/src/views/mp/components/wx-msg/main.vue b/yudao-ui-admin/src/views/mp/components/wx-msg/main.vue index df81ddae3..4ecb1e6a5 100644 --- a/yudao-ui-admin/src/views/mp/components/wx-msg/main.vue +++ b/yudao-ui-admin/src/views/mp/components/wx-msg/main.vue @@ -140,7 +140,6 @@ nickname: '公众号', avatar: require("@/assets/images/wechat.png"), }, - qqMapKey: '' // } }, created() {