Commit 1f98c1f5a6f1fbe70653503d360cb4f0c2f9fb84

Authored by YunaiV
1 parent 69c5591b

!222 feat:【mp】完善“模版消息”的功能

README.md
@@ -232,18 +232,19 @@ @@ -232,18 +232,19 @@
232 232
233 ### 微信公众号 233 ### 微信公众号
234 234
235 -| | 功能 | 描述 |  
236 -|-----|--------|-------------------------------|  
237 -| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 |  
238 -| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 |  
239 -| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |  
240 -| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 |  
241 -| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |  
242 -| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 |  
243 -| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 |  
244 -| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |  
245 -| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 |  
246 -| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 | 235 +| | 功能 | 描述 |
  236 +|----|--------|-------------------------------|
  237 +| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 |
  238 +| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 |
  239 +| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
  240 +| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 |
  241 +| 🚀 | 模版消息 | 配置和发送模版消息,用于向粉丝推送通知类消息 |
  242 +| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
  243 +| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 |
  244 +| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 |
  245 +| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
  246 +| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 |
  247 +| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 |
247 248
248 ### 商城系统 249 ### 商城系统
249 250
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageTemplateController.http 0 → 100644
  1 +### 请求 /mp/message-template/get 接口 => 成功
  2 +GET {{baseUrl}}/mp/message-template/get?id=1
  3 +Content-Type: application/json
  4 +Authorization: Bearer {{token}}
  5 +tenant-id: {{adminTenantId}}
  6 +
  7 +### 请求 /mp/message-template/list 接口 => 成功
  8 +GET {{baseUrl}}/mp/message-template/list?accountId=1
  9 +Content-Type: application/json
  10 +Authorization: Bearer {{token}}
  11 +tenant-id: {{adminTenantId}}
  12 +
  13 +### 请求 /mp/message-template/delete 接口 => 成功
  14 +DELETE {{baseUrl}}/mp/message-template/delete?id=1
  15 +Content-Type: application/json
  16 +Authorization: Bearer {{token}}
  17 +tenant-id: {{adminTenantId}}
  18 +
  19 +### 请求 /mp/message-template/sync 接口 => 成功
  20 +POST {{baseUrl}}/mp/message-template/sync?accountId=5
  21 +Content-Type: application/json
  22 +Authorization: Bearer {{token}}
  23 +tenant-id: {{adminTenantId}}
  24 +
  25 +### 请求 /mp/message-template/send 接口 => 成功
  26 +POST {{baseUrl}}/mp/message-template/send
  27 +Content-Type: application/json
  28 +Authorization: Bearer {{token}}
  29 +tenant-id: {{adminTenantId}}
  30 +
  31 +{
  32 + "id": 66,
  33 + "userId": 65,
  34 + "url": "https://example.com",
  35 + "data": {
  36 + "result": "领奖成功",
  37 + "withdrawMoney": "1000.00元",
  38 + "withdrawTime": "2024-01-01 10:00:00",
  39 + "cardInfo": "工商银行(尾号1234)",
  40 + "arrivedTime": "2024-01-01 10:30:00"
  41 + }
  42 +}
  43 +
  44 +// "miniprogram": "{\"appid\":\"wx1234567890\",\"pagepath\":\"pages/index/index\"}",
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageTemplateController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.controller.admin.message;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateListReqVO;
  6 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateRespVO;
  7 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateSendReqVO;
  8 +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageTemplateDO;
  9 +import cn.iocoder.yudao.module.mp.service.message.MpMessageTemplateService;
  10 +import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.Parameter;
  12 +import io.swagger.v3.oas.annotations.tags.Tag;
  13 +import jakarta.annotation.Resource;
  14 +import jakarta.validation.Valid;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.validation.annotation.Validated;
  17 +import org.springframework.web.bind.annotation.*;
  18 +
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Tag(name = "管理后台 - 公众号模版消息")
  24 +@RestController
  25 +@RequestMapping("/mp/message-template")
  26 +@Validated
  27 +public class MpMessageTemplateController {
  28 +
  29 + @Resource
  30 + private MpMessageTemplateService messageTemplateService;
  31 +
  32 + @DeleteMapping("/delete")
  33 + @Operation(summary = "删除模版消息")
  34 + @Parameter(name = "id", description = "编号", required = true)
  35 + @PreAuthorize("@ss.hasPermission('mp:message-template:delete')")
  36 + public CommonResult<Boolean> deleteMessageTemplate(@RequestParam("id") Long id) {
  37 + messageTemplateService.deleteMessageTemplate(id);
  38 + return success(true);
  39 + }
  40 +
  41 + @GetMapping("/get")
  42 + @Operation(summary = "获得模版消息")
  43 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  44 + @PreAuthorize("@ss.hasPermission('mp:message-template:query')")
  45 + public CommonResult<MpMessageTemplateRespVO> getMessageTemplate(@RequestParam("id") Long id) {
  46 + MpMessageTemplateDO msgTemplate = messageTemplateService.getMessageTemplate(id);
  47 + return success(BeanUtils.toBean(msgTemplate, MpMessageTemplateRespVO.class));
  48 + }
  49 +
  50 + @GetMapping("/list")
  51 + @Operation(summary = "获得模版消息列表")
  52 + @Parameter(name = "accountId", description = "公众号账号的编号", required = true, example = "2048")
  53 + @PreAuthorize("@ss.hasPermission('mp:message-template:query')")
  54 + public CommonResult<List<MpMessageTemplateRespVO>> getMessageTemplateList(MpMessageTemplateListReqVO listReqVO) {
  55 + List<MpMessageTemplateDO> list = messageTemplateService.getMessageTemplateList(listReqVO);
  56 + return success(BeanUtils.toBean(list, MpMessageTemplateRespVO.class));
  57 + }
  58 +
  59 + @PostMapping("/sync")
  60 + @Operation(summary = "同步公众号模板")
  61 + @Parameter(name = "accountId", description = "公众号账号的编号", required = true, example = "2048")
  62 + @PreAuthorize("@ss.hasPermission('mp:message-template:sync')")
  63 + public CommonResult<Boolean> syncMessageTemplate(@RequestParam("accountId") Long accountId) {
  64 + messageTemplateService.syncMessageTemplate(accountId);
  65 + return success(true);
  66 + }
  67 +
  68 + @PostMapping("/send")
  69 + @Operation(summary = "给粉丝发送模版消息")
  70 + @PreAuthorize("@ss.hasPermission('mp:message-template:send')")
  71 + public CommonResult<Boolean> sendMessageTemplate(@Valid @RequestBody MpMessageTemplateSendReqVO sendReqVO) {
  72 + messageTemplateService.sendMessageTempalte(sendReqVO);
  73 + return success(true);
  74 + }
  75 +
  76 +}
0 \ No newline at end of file 77 \ No newline at end of file
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/template/MpMessageTemplateListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.controller.admin.message.vo.template;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 公众号模版消息列表 Request VO")
  8 +@Data
  9 +public class MpMessageTemplateListReqVO {
  10 +
  11 + @Schema(description = "公众号账号的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
  12 + @NotNull(message = "公众号账号的编号不能为空")
  13 + private Long accountId;
  14 +
  15 +}
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/template/MpMessageTemplateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.controller.admin.message.vo.template;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +@Schema(description = "管理后台 - 公众号模版消息 Response VO")
  9 +@Data
  10 +public class MpMessageTemplateRespVO {
  11 +
  12 + @Schema(description = "模版主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7019")
  13 + private Long id;
  14 +
  15 + @Schema(description = "公众号账号的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  16 + private Long accountId;
  17 +
  18 + @Schema(description = "appId", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx1234567890abcdef")
  19 + private String appId;
  20 +
  21 + @Schema(description = "公众号模板ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "IjkGxO9M_mC9pE5Yl7QYJk1h0Dj2N4lC3oOp6rRsT8u")
  22 + private String templateId;
  23 +
  24 + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单状态提醒")
  25 + private String title;
  26 +
  27 + @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + private String content;
  29 +
  30 + @Schema(description = "模板示例")
  31 + private String example;
  32 +
  33 + @Schema(description = "模板所属行业的一级行业", example = "电商")
  34 + private String primaryIndustry;
  35 +
  36 + @Schema(description = "模板所属行业的二级行业", example = "商品售后")
  37 + private String deputyIndustry;
  38 +
  39 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + private LocalDateTime createTime;
  41 +
  42 +}
0 \ No newline at end of file 43 \ No newline at end of file
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/vo/template/MpMessageTemplateSendReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.controller.admin.message.vo.template;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +import java.util.Map;
  8 +
  9 +@Schema(description = "管理后台 - 公众号消息模版发送 Request VO") // 关联 https://developers.weixin.qq.com/doc/service/api/notify/template/api_sendtemplatemessage.html 文档
  10 +@Data
  11 +public class MpMessageTemplateSendReqVO {
  12 +
  13 + @Schema(description = "模版主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "7019")
  14 + @NotNull(message = "模版主键不能为空")
  15 + private Long id;
  16 +
  17 + @Schema(description = "公众号粉丝的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + @NotNull(message = "公众号粉丝的编号不能为空")
  19 + private Long userId;
  20 +
  21 + @Schema(description = "模板跳转链接")
  22 + private String url;
  23 +
  24 + @Schema(description = "跳转小程序时填写")
  25 + private String miniprogram;
  26 +
  27 + @Schema(description = "模板内容")
  28 + private Map<String, String> data;
  29 +
  30 +}
0 \ No newline at end of file 31 \ No newline at end of file
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageTemplateDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.dal.dataobject.message;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
  5 +import com.baomidou.mybatisplus.annotation.KeySequence;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.*;
  9 +
  10 +/**
  11 + * 公众号模版消息 DO
  12 + *
  13 + * @author dengsl
  14 + */
  15 +@TableName("mp_message_template")
  16 +@KeySequence("mp_message_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class MpMessageTemplateDO extends BaseDO {
  24 +
  25 + /**
  26 + * 主键
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 公众号账号的编号
  32 + *
  33 + * 关联 {@link MpAccountDO#getId()}
  34 + */
  35 + private Long accountId;
  36 + /**
  37 + * 公众号 appId
  38 + *
  39 + * 冗余 {@link MpAccountDO#getAppId()}
  40 + */
  41 + private String appId;
  42 + /**
  43 + * 公众号模板 ID
  44 + */
  45 + private String templateId;
  46 +
  47 + /**
  48 + * 标题
  49 + */
  50 + private String title;
  51 + /**
  52 + * 模板内容
  53 + */
  54 + private String content;
  55 + /**
  56 + * 模板示例
  57 + */
  58 + private String example;
  59 +
  60 + /**
  61 + * 模板所属行业的一级行业
  62 + */
  63 + private String primaryIndustry;
  64 + /**
  65 + * 模板所属行业的二级行业
  66 + */
  67 + private String deputyIndustry;
  68 +
  69 +}
0 \ No newline at end of file 70 \ No newline at end of file
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/message/MpMessageTemplateMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.dal.mysql.message;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateListReqVO;
  5 +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageTemplateDO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +
  8 +import java.util.List;
  9 +
  10 +@Mapper
  11 +public interface MpMessageTemplateMapper extends BaseMapperX<MpMessageTemplateDO> {
  12 +
  13 + default List<MpMessageTemplateDO> selectList(MpMessageTemplateListReqVO listReqVO) {
  14 + return selectList(MpMessageTemplateDO::getAccountId, listReqVO.getAccountId());
  15 + }
  16 +
  17 +}
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java
@@ -61,4 +61,10 @@ public interface ErrorCodeConstants { @@ -61,4 +61,10 @@ public interface ErrorCodeConstants {
61 ErrorCode AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS = new ErrorCode(1_006_009_002, "操作失败,原因:已存在该消息类型的回复"); 61 ErrorCode AUTO_REPLY_ADD_MESSAGE_FAIL_EXISTS = new ErrorCode(1_006_009_002, "操作失败,原因:已存在该消息类型的回复");
62 ErrorCode AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS = new ErrorCode(1_006_009_003, "操作失败,原因:已关在该关键字的回复"); 62 ErrorCode AUTO_REPLY_ADD_KEYWORD_FAIL_EXISTS = new ErrorCode(1_006_009_003, "操作失败,原因:已关在该关键字的回复");
63 63
  64 + // ========== 公众号模版消息 1-006-010-000 ============
  65 + ErrorCode MESSAGE_TEMPLATE_NOT_EXISTS = new ErrorCode(1_006_010_000, "模版消息不存在");
  66 + ErrorCode MESSAGE_TEMPLATE_DELETE_FAIL = new ErrorCode(1_006_010_002, "删除模版消息失败,原因:{}");
  67 + ErrorCode MESSAGE_TEMPLATE_SYNC_FAIL = new ErrorCode(1_006_010_003, "同步模版消息失败,原因:{}");
  68 + ErrorCode MESSAGE_TEMPLATE_SEND_FAIL = new ErrorCode(1_006_010_004, "发送模版消息失败,原因:{}");
  69 +
64 } 70 }
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageTemplateService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.service.message;
  2 +
  3 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateListReqVO;
  4 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateSendReqVO;
  5 +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageTemplateDO;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * 公众号模版消息 Service 接口
  11 + *
  12 + * @author dengsl
  13 + */
  14 +public interface MpMessageTemplateService {
  15 +
  16 + /**
  17 + * 删除模版消息
  18 + *
  19 + * @param id 编号
  20 + */
  21 + void deleteMessageTemplate(Long id);
  22 +
  23 + /**
  24 + * 获得模版消息
  25 + *
  26 + * @param id 编号
  27 + * @return 模版消息
  28 + */
  29 + MpMessageTemplateDO getMessageTemplate(Long id);
  30 +
  31 + /**
  32 + * 获得模版消息列表
  33 + *
  34 + * @param listReqVO 查询条件
  35 + * @return 模版消息列表
  36 + */
  37 + List<MpMessageTemplateDO> getMessageTemplateList(MpMessageTemplateListReqVO listReqVO);
  38 +
  39 + /**
  40 + * 同步公众号已添加的模版消息
  41 + *
  42 + * @param accountId 公众号账号的编号
  43 + */
  44 + void syncMessageTemplate(Long accountId);
  45 +
  46 + /**
  47 + * 使用公众号,给粉丝发送【模版】消息
  48 + *
  49 + * @param sendReqVO 消息内容
  50 + */
  51 + void sendMessageTempalte(MpMessageTemplateSendReqVO sendReqVO);
  52 +
  53 +}
0 \ No newline at end of file 54 \ No newline at end of file
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageTemplateServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.service.message;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateListReqVO;
  8 +import cn.iocoder.yudao.module.mp.controller.admin.message.vo.template.MpMessageTemplateSendReqVO;
  9 +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
  10 +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageTemplateDO;
  11 +import cn.iocoder.yudao.module.mp.dal.dataobject.user.MpUserDO;
  12 +import cn.iocoder.yudao.module.mp.dal.mysql.message.MpMessageTemplateMapper;
  13 +import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
  14 +import cn.iocoder.yudao.module.mp.service.account.MpAccountService;
  15 +import cn.iocoder.yudao.module.mp.service.user.MpUserService;
  16 +import jakarta.annotation.Resource;
  17 +import lombok.extern.slf4j.Slf4j;
  18 +import me.chanjar.weixin.common.error.WxErrorException;
  19 +import me.chanjar.weixin.mp.bean.template.WxMpTemplate;
  20 +import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
  21 +import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
  22 +import org.springframework.context.annotation.Lazy;
  23 +import org.springframework.stereotype.Service;
  24 +import org.springframework.transaction.annotation.Transactional;
  25 +import org.springframework.validation.annotation.Validated;
  26 +
  27 +import java.util.ArrayList;
  28 +import java.util.List;
  29 +import java.util.Map;
  30 +
  31 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  33 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  34 +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*;
  35 +
  36 +/**
  37 + * 公众号模版消息 Service 实现类
  38 + *
  39 + * @author dengsl
  40 + */
  41 +@Service
  42 +@Validated
  43 +@Slf4j
  44 +public class MpMessageTemplateServiceImpl implements MpMessageTemplateService {
  45 +
  46 + @Resource
  47 + @Lazy // 延迟加载,为了解决延迟加载
  48 + private MpServiceFactory mpServiceFactory;
  49 +
  50 + @Resource
  51 + private MpMessageTemplateMapper messageTemplateMapper;
  52 +
  53 + @Resource
  54 + private MpAccountService mpAccountService;
  55 +
  56 + @Resource
  57 + private MpUserService mpUserService;
  58 +
  59 + @Override
  60 + public void deleteMessageTemplate(Long id) {
  61 + // 校验存在
  62 + MpMessageTemplateDO template = validateMsgTemplateExists(id);
  63 +
  64 + // 第一步,删除模板到公众号平台
  65 + try {
  66 + mpServiceFactory.getRequiredMpService(template.getAppId())
  67 + .getTemplateMsgService().delPrivateTemplate(template.getTemplateId());
  68 + } catch (WxErrorException e) {
  69 + throw exception(MESSAGE_TEMPLATE_DELETE_FAIL, e.getError().getErrorMsg());
  70 + }
  71 +
  72 + // 第二步,删除模板到数据库
  73 + messageTemplateMapper.deleteById(id);
  74 + }
  75 +
  76 + private MpMessageTemplateDO validateMsgTemplateExists(Long id) {
  77 + MpMessageTemplateDO template = messageTemplateMapper.selectById(id);
  78 + if (template == null) {
  79 + throw exception(MESSAGE_TEMPLATE_NOT_EXISTS);
  80 + }
  81 + return template;
  82 + }
  83 +
  84 + @Override
  85 + public MpMessageTemplateDO getMessageTemplate(Long id) {
  86 + return messageTemplateMapper.selectById(id);
  87 + }
  88 +
  89 + @Override
  90 + public List<MpMessageTemplateDO> getMessageTemplateList(MpMessageTemplateListReqVO listReqVO) {
  91 + return messageTemplateMapper.selectList(listReqVO);
  92 + }
  93 +
  94 + @Override
  95 + @Transactional(rollbackFor = Exception.class)
  96 + public void syncMessageTemplate(Long accountId) {
  97 + MpAccountDO account = mpAccountService.getRequiredAccount(accountId);
  98 +
  99 + // 第一步,从公众号平台获取最新的模板列表
  100 + List<WxMpTemplate> wxTemplates;
  101 + try {
  102 + wxTemplates = mpServiceFactory.getRequiredMpService(accountId)
  103 + .getTemplateMsgService().getAllPrivateTemplate();
  104 + } catch (WxErrorException e) {
  105 + throw exception(MESSAGE_TEMPLATE_SYNC_FAIL, e.getError().getErrorMsg());
  106 + }
  107 +
  108 + // 第二步,合并更新回自己的数据库
  109 + Map<String, MpMessageTemplateDO> templateMap = convertMap(
  110 + messageTemplateMapper.selectList(new LambdaQueryWrapperX<MpMessageTemplateDO>()
  111 + .eq(MpMessageTemplateDO::getAppId, account.getAppId())),
  112 + MpMessageTemplateDO::getTemplateId);
  113 + wxTemplates.forEach(wxTemplate -> {
  114 + MpMessageTemplateDO template = templateMap.remove(wxTemplate.getTemplateId());
  115 + // 情况一,不存在,新增
  116 + if (template == null) {
  117 + template = new MpMessageTemplateDO().setAccountId(account.getId()).setAppId(account.getAppId())
  118 + .setTemplateId(wxTemplate.getTemplateId()).setTitle(wxTemplate.getTitle())
  119 + .setContent(wxTemplate.getContent()).setExample(wxTemplate.getExample())
  120 + .setPrimaryIndustry(wxTemplate.getPrimaryIndustry()).setDeputyIndustry(wxTemplate.getDeputyIndustry());
  121 + messageTemplateMapper.insert(template);
  122 + return;
  123 + }
  124 + // 情况二,存在,则更新
  125 + messageTemplateMapper.updateById(new MpMessageTemplateDO().setId(template.getId())
  126 + .setTitle(wxTemplate.getTitle()).setContent(wxTemplate.getContent()).setExample(wxTemplate.getExample())
  127 + .setPrimaryIndustry(wxTemplate.getPrimaryIndustry()).setDeputyIndustry(wxTemplate.getDeputyIndustry()));
  128 + });
  129 + // 情况三,部分模板已经不存在了,删除
  130 + if (CollUtil.isNotEmpty(templateMap)) {
  131 + messageTemplateMapper.deleteByIds(convertList(templateMap.values(), MpMessageTemplateDO::getId));
  132 + }
  133 + }
  134 +
  135 + @Override
  136 + public void sendMessageTempalte(MpMessageTemplateSendReqVO sendReqVO) {
  137 + // 获得关联信息
  138 + MpUserDO user = mpUserService.getRequiredUser(sendReqVO.getUserId());
  139 + MpMessageTemplateDO template = validateMsgTemplateExists(sendReqVO.getId());
  140 +
  141 + // 发送模版消息
  142 + WxMpTemplateMessage templateMessage = buildTemplateMessage(template, user, sendReqVO);
  143 + try {
  144 + mpServiceFactory.getRequiredMpService(template.getAppId())
  145 + .getTemplateMsgService().sendTemplateMsg(templateMessage);
  146 + } catch (WxErrorException e) {
  147 + throw exception(MESSAGE_TEMPLATE_SEND_FAIL, e.getError().getErrorMsg());
  148 + }
  149 +
  150 + // 不用记录 MpMessageDO 记录,因为,微信会主动推送,可见文档 https://developers.weixin.qq.com/doc/service/guide/product/template_message/Template_Message_Interface.html
  151 + }
  152 +
  153 + private WxMpTemplateMessage buildTemplateMessage(MpMessageTemplateDO msgTemplateDO, MpUserDO user,
  154 + MpMessageTemplateSendReqVO sendReqVO) {
  155 + List<WxMpTemplateData> data = new ArrayList<>();
  156 + WxMpTemplateMessage.WxMpTemplateMessageBuilder builder = WxMpTemplateMessage.builder()
  157 + .templateId(msgTemplateDO.getTemplateId())
  158 + .data(data)
  159 + .toUser(user.getOpenid());
  160 + // 设置跳转链接
  161 + if (StrUtil.isNotBlank(sendReqVO.getUrl())) {
  162 + builder.url(sendReqVO.getUrl());
  163 + }
  164 + // 设置小程序跳转
  165 + if (StrUtil.isNotBlank(sendReqVO.getMiniprogram())) {
  166 + // https://developers.weixin.qq.com/doc/service/api/notify/template/api_sendtemplatemessage.html#Body__miniprogram
  167 + builder.miniProgram(JsonUtils.parseObject(sendReqVO.getMiniprogram(), WxMpTemplateMessage.MiniProgram.class));
  168 + }
  169 + // 设置模板数据
  170 + if (sendReqVO.getData() != null) {
  171 + sendReqVO.getData().forEach((key, value) -> data.add(new WxMpTemplateData(key, value)));
  172 + }
  173 + return builder.build();
  174 + }
  175 +
  176 +}
0 \ No newline at end of file 177 \ No newline at end of file