mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 18:28:43 +08:00 
			
		
		
		
	mp:增加上传临时素材的接口
This commit is contained in:
		| @@ -4,9 +4,6 @@ import lombok.*; | ||||
| import io.swagger.annotations.*; | ||||
| import javax.validation.constraints.*; | ||||
|  | ||||
| /** | ||||
|  * @author fengdan | ||||
|  */ | ||||
| @ApiModel("管理后台 - 公众号账号更新 Request VO") | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
|   | ||||
| @@ -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<MpMaterialUploadRespVO> uploadTemporaryMaterial( | ||||
|             @Valid MpMaterialUploadTemporaryReqVO reqVO) throws IOException { | ||||
|         MpMaterialDO material = mpMaterialService.uploadTemporaryMaterial(reqVO); | ||||
|         return success(MpMaterialConvert.INSTANCE.convert(material)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
| } | ||||
| @@ -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. <a href="https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html">临时素材</a> | ||||
|  * 2. <a href="https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html">永久素材</a> | ||||
|  * | ||||
|  * @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; | ||||
|  | ||||
| } | ||||
| @@ -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<MpMaterialDO> { | ||||
|  | ||||
|     default MpMaterialDO selectByAccountIdAndMediaId(Long accountId, String mediaId) { | ||||
|         return selectOne(MpMaterialDO::getAccountId, accountId, | ||||
|                 MpMaterialDO::getMediaId, mediaId); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -4,15 +4,12 @@ | ||||
| --> | ||||
| <template> | ||||
|   <el-tabs type="border-card" v-model="objData.repType" @tab-click="handleClick"> | ||||
|     <!-- 类型 1:文本 --> | ||||
|     <el-tab-pane name="text"> | ||||
|       <span slot="label"><i class="el-icon-document"></i> 文本</span> | ||||
|       <el-input | ||||
|         type="textarea" | ||||
|         :rows="5" | ||||
|         placeholder="请输入内容" | ||||
|         v-model="objData.repContent"> | ||||
|       </el-input> | ||||
|       <el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="objData.content" /> | ||||
|     </el-tab-pane> | ||||
|     <!-- 类型 2:图片 --> | ||||
|     <el-tab-pane name="image"> | ||||
|       <span slot="label"><i class="el-icon-picture"></i> 图片</span> | ||||
|       <el-row> | ||||
| @@ -29,19 +26,10 @@ | ||||
|               <el-button type="success" @click="openMaterial">素材库选择<i class="el-icon-circle-check el-icon--right"></i></el-button> | ||||
|             </el-col> | ||||
|             <el-col :span="12" class="col-add"> | ||||
|               <el-upload | ||||
|                 :action="actionUrl" | ||||
|                 :headers="headers" | ||||
|                 multiple | ||||
|                 :limit="1" | ||||
|                 :on-success="handleUploadSuccess" | ||||
|                 :file-list="fileList" | ||||
|                 :before-upload="beforeImageUpload" | ||||
|                 :data="uploadData"> | ||||
|               <el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :file-list="fileList" :data="uploadData" | ||||
|                          :before-upload="beforeImageUpload" :on-success="handleUploadSuccess"> | ||||
|                 <el-button type="primary">上传图片</el-button> | ||||
|                 <div slot="tip" class="el-upload__tip"> | ||||
|                   支持bmp/png/jpeg/jpg/gif格式,大小不超过2M | ||||
|                 </div> | ||||
|                 <div slot="tip" class="el-upload__tip">支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M</div> | ||||
|               </el-upload> | ||||
|             </el-col> | ||||
|           </el-row> | ||||
| @@ -69,12 +57,7 @@ | ||||
|               <el-button type="success" @click="openMaterial">素材库选择<i class="el-icon-circle-check el-icon--right"></i></el-button> | ||||
|             </el-col> | ||||
|             <el-col :span="12" class="col-add"> | ||||
|               <el-upload | ||||
|                 :action="actionUrl" | ||||
|                 :headers="headers" | ||||
|                 multiple | ||||
|                 :limit="1" | ||||
|                 :on-success="handleUploadSuccess" | ||||
|               <el-upload :action="actionUrl" :headers="headers" multiple :limit="1" :on-success="handleUploadSuccess" | ||||
|                 :file-list="fileList" | ||||
|                 :before-upload="beforeVoiceUpload" | ||||
|                 :data="uploadData"> | ||||
| @@ -188,9 +171,9 @@ | ||||
|     }, | ||||
|     props: { | ||||
|       objData:{ | ||||
|         type:Object | ||||
|         type: Object | ||||
|       }, | ||||
|       //图文类型:1、已发布图文;2、草稿箱图文 | ||||
|       // 图文类型:1、已发布图文;2、草稿箱图文 | ||||
|       newsType:{ | ||||
|         type: String, | ||||
|         default: "1" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV