From ec872c702c6a5da948371a16b9aafa11a13a912d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 11 Jan 2023 20:13:20 +0800 Subject: [PATCH] =?UTF-8?q?mp=EF=BC=9A=E5=A2=9E=E5=8A=A0=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E7=B4=A0=E6=9D=90=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/vo/MpAccountUpdateReqVO.java | 3 - .../admin/material/MpMaterialController.http | 0 .../admin/material/MpMaterialController.java | 39 +++++ .../material/vo/MpMaterialUploadRespVO.java | 17 +++ .../vo/MpMaterialUploadTemporaryReqVO.java | 29 ++++ .../convert/material/MpMaterialConvert.java | 25 ++++ .../dal/dataobject/material/MpMaterialDO.java | 95 +++++++++++++ .../dal/mysql/material/MpMaterialMapper.java | 15 ++ .../mp/framework/mp/core/util/MpUtils.java | 21 +++ .../service/material/MpMaterialService.java | 30 ++++ .../material/MpMaterialServiceImpl.java | 133 ++++++++++++++++++ .../service/message/MpMessageServiceImpl.java | 48 ++----- .../src/views/mp/components/wx-msg/main.vue | 2 +- .../src/views/mp/components/wx-reply/main.vue | 35 ++--- 14 files changed, 426 insertions(+), 66 deletions(-) create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadRespVO.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadTemporaryReqVO.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/material/MpMaterialConvert.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/material/MpMaterialDO.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/material/MpMaterialMapper.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialService.java create mode 100644 yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialServiceImpl.java diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/account/vo/MpAccountUpdateReqVO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/account/vo/MpAccountUpdateReqVO.java index 37453892a..f348c22c2 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/account/vo/MpAccountUpdateReqVO.java +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/account/vo/MpAccountUpdateReqVO.java @@ -4,9 +4,6 @@ import lombok.*; import io.swagger.annotations.*; import javax.validation.constraints.*; -/** - * @author fengdan - */ @ApiModel("管理后台 - 公众号账号更新 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http new file mode 100644 index 000000000..e69de29bb diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.java new file mode 100644 index 000000000..c10f81856 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.mp.controller.admin.material; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.mp.controller.admin.material.vo.MpMaterialUploadRespVO; +import cn.iocoder.yudao.module.mp.controller.admin.material.vo.MpMaterialUploadTemporaryReqVO; +import cn.iocoder.yudao.module.mp.convert.material.MpMaterialConvert; +import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO; +import cn.iocoder.yudao.module.mp.service.material.MpMaterialService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.io.IOException; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 公众号素材") +@RestController +@RequestMapping("/mp/material") +@Validated +public class MpMaterialController { + + @Resource + private MpMaterialService mpMaterialService; + + @ApiOperation("上传临时素材") + @PostMapping("/upload-temporary") + public CommonResult uploadTemporaryMaterial( + @Valid MpMaterialUploadTemporaryReqVO reqVO) throws IOException { + MpMaterialDO material = mpMaterialService.uploadTemporaryMaterial(reqVO); + return success(MpMaterialConvert.INSTANCE.convert(material)); + } + +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadRespVO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadRespVO.java new file mode 100644 index 000000000..917cc8941 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadRespVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.mp.controller.admin.material.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("管理后台 - 公众号素材上传结果 Response VO") +@Data +public class MpMaterialUploadRespVO { + + @ApiModelProperty(value = "素材的 media_id", required = true, example = "123") + private String mediaId; + + @ApiModelProperty(value = "素材的 URL", required = true, example = "https://www.iocoder.cn/1.png") + private String url; + +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadTemporaryReqVO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadTemporaryReqVO.java new file mode 100644 index 000000000..f8c73cec2 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/vo/MpMaterialUploadTemporaryReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.mp.controller.admin.material.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@ApiModel("管理后台 - 公众号素材上传临时 Request VO") +@Data +public class MpMaterialUploadTemporaryReqVO { + + @ApiModelProperty(value = "公众号账号的编号", required = true, example = "2048") + @NotNull(message = "公众号账号的编号不能为空") + private Long accountId; + + @ApiModelProperty(value = "文件类型", required = true, example = "image", notes = "参见 WxConsts.MediaFileType 枚举") + @NotEmpty(message = "文件类型不能为空") + private String type; + + @ApiModelProperty(value = "文件附件", required = true) + @NotNull(message = "文件不能为空") + @JsonIgnore // 避免被操作日志,进行序列化,导致报错 + private MultipartFile file; + +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/material/MpMaterialConvert.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/material/MpMaterialConvert.java new file mode 100644 index 000000000..c6215aa02 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/material/MpMaterialConvert.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.mp.convert.material; + +import cn.iocoder.yudao.module.mp.controller.admin.material.vo.MpMaterialUploadRespVO; +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; +import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface MpMaterialConvert { + + MpMaterialConvert INSTANCE = Mappers.getMapper(MpMaterialConvert.class); + + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(source = "account.id", target = "accountId"), + @Mapping(source = "account.appId", target = "appId"), + }) + MpMaterialDO convert(String mediaId, String type, String url, MpAccountDO account); + + MpMaterialUploadRespVO convert(MpMaterialDO bean); + +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/material/MpMaterialDO.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/material/MpMaterialDO.java new file mode 100644 index 000000000..0f599c019 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/material/MpMaterialDO.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.mp.dal.dataobject.material; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; +import me.chanjar.weixin.common.api.WxConsts; + +/** + * 公众号素材 DO + * + * 1. 临时素材 + * 2. 永久素材 + * + * @author 芋道源码 + */ +@TableName("mp_material") +@KeySequence("mp_material_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MpMaterialDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 微信公众号 ID + * + * 关联 {@link MpAccountDO#getId()} + */ + private Long accountId; + /** + * 微信公众号 appid + * + * 冗余 {@link MpAccountDO#getAppId()} + */ + private String appId; + + /** + * 公众号素材 id + */ + private String mediaId; + /** + * 文件类型 + * + * 枚举 {@link WxConsts.MediaFileType} + */ + private String type; + /** + * 是否永久 + * + * true - 永久素材 + * false - 临时素材 + */ + private Boolean permanent; + /** + * 文件服务器的 URL + */ + private String url; + + /** + * 名字 + * + * 只有【永久素材】使用 + */ + private String name; + /** + * 公众号文件 URL + * + * 只有【永久素材】使用 + */ + private String mpUrl; + + /** + * 视频素材的标题 + * + * 只有【永久素材】使用 + */ + private String title; + /** + * 视频素材的描述 + * + * 只有【永久素材】使用 + */ + private String introduction; + +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/material/MpMaterialMapper.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/material/MpMaterialMapper.java new file mode 100644 index 000000000..185ded2f7 --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/material/MpMaterialMapper.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.mp.dal.mysql.material; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MpMaterialMapper extends BaseMapperX { + + default MpMaterialDO selectByAccountIdAndMediaId(Long accountId, String mediaId) { + return selectOne(MpMaterialDO::getAccountId, accountId, + MpMaterialDO::getMediaId, mediaId); + } + +} 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 ebf44e741..02d9ecb75 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 @@ -80,4 +80,25 @@ public class MpUtils { ValidationUtils.validate(validator, message, group); } + /** + * 根据消息类型,获得对应的媒体文件类型 + * + * 注意,不会返回 WxConsts.MediaFileType.THUMB,因为该类型会有明确标注 + * + * @param messageType 消息类型 {@link WxConsts.XmlMsgType} + * @return 媒体文件类型 {@link WxConsts.MediaFileType} + */ + public static String getMediaFileType(String messageType) { + switch (messageType) { + case WxConsts.XmlMsgType.IMAGE: + return WxConsts.MediaFileType.IMAGE; + case WxConsts.XmlMsgType.VOICE: + return WxConsts.MediaFileType.VOICE; + case WxConsts.XmlMsgType.VIDEO: + return WxConsts.MediaFileType.VIDEO; + default: + return WxConsts.MediaFileType.FILE; + } + } + } diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialService.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialService.java new file mode 100644 index 000000000..56d9b645a --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialService.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.mp.service.material; + +import cn.iocoder.yudao.module.mp.controller.admin.material.vo.MpMaterialUploadTemporaryReqVO; +import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO; +import me.chanjar.weixin.common.api.WxConsts; + +import javax.validation.Valid; +import java.io.IOException; + +/** + * 公众号素材 Service 接口 + * + * @author 芋道源码 + */ +public interface MpMaterialService { + + /** + * 获得素材的 URL + * + * 该 URL 来自我们自己的文件服务器存储的 URL,不是公众号存储的 URL + * + * @param accountId 公众号账号编号 + * @param mediaId 公众号素材 id + * @param type 文件类型 {@link WxConsts.MediaFileType} + * @return 素材的 URL + */ + String downloadMaterialUrl(Long accountId, String mediaId, String type); + + MpMaterialDO uploadTemporaryMaterial(@Valid MpMaterialUploadTemporaryReqVO reqVO) throws IOException; +} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialServiceImpl.java b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialServiceImpl.java new file mode 100644 index 000000000..ff6885f6e --- /dev/null +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/material/MpMaterialServiceImpl.java @@ -0,0 +1,133 @@ +package cn.iocoder.yudao.module.mp.service.material; + +import cn.hutool.core.io.FileTypeUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.module.infra.api.file.FileApi; +import cn.iocoder.yudao.module.mp.controller.admin.material.vo.MpMaterialUploadTemporaryReqVO; +import cn.iocoder.yudao.module.mp.convert.material.MpMaterialConvert; +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; +import cn.iocoder.yudao.module.mp.dal.dataobject.material.MpMaterialDO; +import cn.iocoder.yudao.module.mp.dal.mysql.material.MpMaterialMapper; +import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; +import cn.iocoder.yudao.module.mp.service.account.MpAccountService; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; + +/** + * 公众号素材 Service 接口 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class MpMaterialServiceImpl implements MpMaterialService { + + @Resource + private MpMaterialMapper mpMaterialMapper; + + @Resource + private FileApi fileApi; + + @Resource + @Lazy // 延迟加载,解决循环依赖的问题 + private MpAccountService mpAccountService; + + @Resource + @Lazy // 延迟加载,解决循环依赖的问题 + private MpServiceFactory mpServiceFactory; + + @Override + public String downloadMaterialUrl(Long accountId, String mediaId, String type) { + // 第一步,直接从数据库查询。如果已经下载,直接返回 + MpMaterialDO material = mpMaterialMapper.selectByAccountIdAndMediaId(accountId, mediaId); + if (material != null) { + return material.getUrl(); + } + + // 第二步,尝试从临时素材中下载 + String url = downloadMedia(accountId, mediaId); + if (url == null) { + return null; + } + MpAccountDO account = mpAccountService.getRequiredAccount(accountId); + material = MpMaterialConvert.INSTANCE.convert(mediaId, type, url, account) + .setPermanent(false); + mpMaterialMapper.insert(material); + + // 不考虑下载永久素材,因为上传的时候已经保存 + return url; + } + + @Override + public MpMaterialDO uploadTemporaryMaterial(MpMaterialUploadTemporaryReqVO reqVO) throws IOException { + WxMpService mpService = mpServiceFactory.getRequiredMpService(reqVO.getAccountId()); + // 第一步,上传到公众号 + File file = null; + WxMediaUploadResult result; + String mediaId; + String url; + try { + // 写入到临时文件 + file = FileUtil.newFile(FileUtil.getTmpDirPath() + reqVO.getFile().getOriginalFilename()); + reqVO.getFile().transferTo(file); + // 上传到公众号 + result = mpService.getMaterialService().mediaUpload(reqVO.getType(), file); + // 上传到文件服务 + mediaId = ObjUtil.defaultIfNull(result.getMediaId(), result.getThumbMediaId()); + url = uploadFile(mediaId, file); + } catch (WxErrorException e) { + // TODO yunai:待完善 + throw new RuntimeException(e); + } finally { + FileUtil.del(file); + } + + // 第二步,存储到数据库 + MpAccountDO account = mpAccountService.getRequiredAccount(reqVO.getAccountId()); + MpMaterialDO material = MpMaterialConvert.INSTANCE.convert(mediaId, reqVO.getType(), url, account) + .setPermanent(false); + mpMaterialMapper.insert(material); + return material; + } + + /** + * 下载微信媒体文件的内容,并上传到文件服务 + * + * 为什么要下载?媒体文件在微信后台保存时间为 3 天,即 3 天后 media_id 失效。 + * + * @param accountId 公众号账号的编号 + * @param mediaId 媒体文件编号 + * @return 上传后的 URL + */ + public String downloadMedia(Long accountId, String mediaId) { + WxMpService mpService = mpServiceFactory.getMpService(accountId); + for (int i = 0; i < 3; i++) { + try { + // 第一步,从公众号下载媒体文件 + File file = mpService.getMaterialService().mediaDownload(mediaId); + // 第二步,上传到文件服务 + return uploadFile(mediaId, file); + } catch (WxErrorException e) { + log.error("[mediaDownload][media({}) 第 ({}) 次下载失败]", mediaId, i); + } + } + return null; + } + + private String uploadFile(String mediaId, File file) { + String path = mediaId + "." + FileTypeUtil.getType(file); + return fileApi.createFile(path, FileUtil.readBytes(file)); + } + +} 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 229cc52c0..0bd547c65 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 @@ -1,11 +1,8 @@ package cn.iocoder.yudao.module.mp.service.message; -import cn.hutool.core.io.FileTypeUtil; -import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Assert; 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; @@ -17,9 +14,11 @@ import cn.iocoder.yudao.module.mp.enums.message.MpMessageSendFromEnum; import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; import cn.iocoder.yudao.module.mp.framework.mp.core.util.MpUtils; import cn.iocoder.yudao.module.mp.service.account.MpAccountService; +import cn.iocoder.yudao.module.mp.service.material.MpMaterialService; import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; import cn.iocoder.yudao.module.mp.service.user.MpUserService; import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; @@ -31,7 +30,6 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Validator; -import java.io.File; /** * 粉丝消息表 Service 实现类 @@ -48,6 +46,8 @@ public class MpMessageServiceImpl implements MpMessageService { private MpAccountService mpAccountService; @Resource private MpUserService mpUserService; + @Resource + private MpMaterialService mpMaterialService; @Resource private MpMessageMapper mpMessageMapper; @@ -56,9 +56,6 @@ public class MpMessageServiceImpl implements MpMessageService { @Lazy // 延迟加载,解决循环依赖的问题 private MpServiceFactory mpServiceFactory; - @Resource - private FileApi fileApi; - @Resource private Validator validator; @@ -69,7 +66,6 @@ public class MpMessageServiceImpl implements MpMessageService { @Override public void receiveMessage(String appId, WxMpXmlMessage wxMessage) { - WxMpService mpService = mpServiceFactory.getRequiredMpService(appId); // 获得关联信息 MpAccountDO account = mpAccountService.getAccountFromCache(appId); Assert.notNull(account, "公众号账号({}) 不存在", appId); @@ -79,7 +75,7 @@ public class MpMessageServiceImpl implements MpMessageService { // 记录消息 MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user); message.setSendFrom(MpMessageSendFromEnum.USER_TO_MP.getFrom()); - downloadMessageMedia(mpService, message); + downloadMessageMedia(message); mpMessageMapper.insert(message); } @@ -97,6 +93,7 @@ public class MpMessageServiceImpl implements MpMessageService { // 记录消息 MpMessageDO message = MpMessageConvert.INSTANCE.convert(sendReqBO, account, user); message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom()); + // TODO 芋艿:downloadMessageMedia mpMessageMapper.insert(message); // 转换返回 WxMpXmlOutMessage 对象 @@ -124,7 +121,7 @@ public class MpMessageServiceImpl implements MpMessageService { // 记录消息 MpMessageDO message = MpMessageConvert.INSTANCE.convert(wxMessage, account, user); message.setSendFrom(MpMessageSendFromEnum.MP_TO_USER.getFrom()); - downloadMessageMedia(mpService, message); + downloadMessageMedia(message); mpMessageMapper.insert(message); return message; } @@ -132,38 +129,17 @@ public class MpMessageServiceImpl implements MpMessageService { /** * 下载消息使用到的媒体文件,并上传到文件服务 * - * @param mpService 公众号 Service * @param message 消息 */ - private void downloadMessageMedia(WxMpService mpService, MpMessageDO message) { + private void downloadMessageMedia(MpMessageDO message) { if (StrUtil.isNotEmpty(message.getMediaId())) { - message.setMediaUrl(downloadMedia(mpService, message.getMediaId())); + message.setMediaUrl(mpMaterialService.downloadMaterialUrl(message.getAccountId(), + message.getMediaId(), MpUtils.getMediaFileType(message.getType()))); } if (StrUtil.isNotEmpty(message.getThumbMediaId())) { - message.setThumbMediaUrl(downloadMedia(mpService, message.getThumbMediaId())); + message.setThumbMediaUrl(mpMaterialService.downloadMaterialUrl(message.getAccountId(), + message.getThumbMediaId(), WxConsts.MediaFileType.THUMB)); } } - /** - * 下载微信媒体文件的内容,并上传到文件服务 - * - * 为什么要下载?媒体文件在微信后台保存时间为 3 天,即 3 天后 media_id 失效。 - * - * @param mpService 微信公众号 Service - * @param mediaId 媒体文件编号 - * @return 上传后的 URL - */ - private String downloadMedia(WxMpService mpService, String mediaId) { - try { - // 第一步,从公众号下载媒体文件 - File file = mpService.getMaterialService().mediaDownload(mediaId); - // 第二步,上传到文件服务 - String path = mediaId + "." + FileTypeUtil.getType(file); - return fileApi.createFile(path, FileUtil.readBytes(file)); - } catch (WxErrorException e) { - log.error("[mediaDownload][media({}) 下载失败]", mediaId); - } - return null; - } - } 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 4dd3d1f25..850b05d27 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 @@ -158,7 +158,7 @@ import {getMessagePage, sendMessage} from '@/api/mp/message' }, { ...this.objData, type: this.objData.repType, - content: this.objData.repContent, + // content: this.objData.repContent, // TODO 芋艿:临时适配,保证可用 })).then(response => { this.sendLoading = false diff --git a/yudao-ui-admin/src/views/mp/components/wx-reply/main.vue b/yudao-ui-admin/src/views/mp/components/wx-reply/main.vue index 13ac70315..a8e9cdf3b 100644 --- a/yudao-ui-admin/src/views/mp/components/wx-reply/main.vue +++ b/yudao-ui-admin/src/views/mp/components/wx-reply/main.vue @@ -4,15 +4,12 @@ -->