Commit 3be6ee271b4b4a86a1fa454c609d5cb63a738f65

Authored by 芋道源码
Committed by Gitee
2 parents 345f41b1 ab4e10cc

!1023 【优化】SYSTEM: 根据代码评审优化订阅消息

Merge pull request !1023 from puhui999/develop
Showing 12 changed files with 126 additions and 252 deletions
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java
@@ -11,19 +11,4 @@ public interface MessageTemplateConstants { @@ -11,19 +11,4 @@ public interface MessageTemplateConstants {
11 11
12 String PAY_WALLET_CHANGE = "充值成功通知"; 12 String PAY_WALLET_CHANGE = "充值成功通知";
13 13
14 - // TODO @puhui999:这种建议不枚举,直接写~嘿嘿。  
15 - /**  
16 - * 充值成功通知模版参数  
17 - *  
18 - * @author HUIHUI  
19 - */  
20 - class PayWalletChangeTemplateParams {  
21 -  
22 - public static final String NO = "character_string1"; // 流水编号  
23 - public static final String PRICE = "amount2"; // 充值金额  
24 - public static final String PAY_TIME = "time3"; // 充值时间  
25 - public static final String STATUS = "phrase4"; // 充值状态  
26 -  
27 - }  
28 -  
29 } 14 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/package-info.java deleted
1 -package cn.iocoder.yudao.module.pay.message;  
2 \ No newline at end of file 0 \ No newline at end of file
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/subscribe/SubscribeMessageClient.java deleted
1 -package cn.iocoder.yudao.module.pay.message.subscribe;  
2 -  
3 -import cn.iocoder.yudao.module.system.api.social.SocialClientApi;  
4 -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;  
5 -import jakarta.annotation.Resource;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -import org.springframework.scheduling.annotation.Async;  
8 -import org.springframework.stereotype.Component;  
9 -  
10 -import java.util.Map;  
11 -  
12 -import static cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants.PAY_WALLET_CHANGE;  
13 -  
14 -// TODO @puhui999:建议可以先直接调用,不要新建一个 client。  
15 -/**  
16 - * 订阅消息  
17 - *  
18 - * @author HUIHUI  
19 - */  
20 -@Component  
21 -@Slf4j  
22 -public class SubscribeMessageClient {  
23 -  
24 - public static final String WALLET_MONEY_PATH = "pages/user/wallet/money"; // 钱包详情页  
25 -  
26 - @Resource  
27 - public SocialClientApi socialClientApi;  
28 -  
29 - /**  
30 - * 发送钱包充值通知  
31 - *  
32 - * @param messages 消息  
33 - * @param userType 用户类型  
34 - * @param userId 用户编号  
35 - */  
36 - @Async  
37 - public void sendPayWalletChangeMessage(Map<String, String> messages, Integer userType, Long userId) {  
38 - sendWxMessage(PAY_WALLET_CHANGE, messages, userType, userId, WALLET_MONEY_PATH);  
39 - }  
40 -  
41 -  
42 - /**  
43 - * 发送微信订阅消息  
44 - *  
45 - * @param templateTitle 模版标题  
46 - * @param messages 消息  
47 - * @param userType 用户类型  
48 - * @param userId 用户编号  
49 - * @param path 点击模板卡片后的跳转页面,仅限本小程序内的页面  
50 - */  
51 - private void sendWxMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,  
52 - String path) {  
53 - socialClientApi.sendSubscribeMessage(templateTitle, messages, userType, userId, SocialTypeEnum.WECHAT_MINI_APP.getType(), path);  
54 - }  
55 -  
56 -}  
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.pay.service.wallet; @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.pay.service.wallet;
2 2
3 import cn.hutool.core.date.LocalDateTimeUtil; 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 -import cn.hutool.core.map.MapUtil; 5 +import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageParam; 6 import cn.iocoder.yudao.framework.common.pojo.PageParam;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
@@ -15,21 +15,22 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; @@ -15,21 +15,22 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
15 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 15 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
16 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; 16 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO;
17 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper; 17 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper;
18 -import cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants;  
19 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 18 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
20 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; 19 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
21 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; 20 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
22 -import cn.iocoder.yudao.module.pay.message.subscribe.SubscribeMessageClient;  
23 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 21 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
24 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 22 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  23 +import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
  24 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeMessageSendReqDTO;
  25 +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
25 import jakarta.annotation.Resource; 26 import jakarta.annotation.Resource;
26 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
  28 +import org.springframework.scheduling.annotation.Async;
27 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
28 import org.springframework.transaction.annotation.Transactional; 30 import org.springframework.transaction.annotation.Transactional;
29 31
30 import java.time.Duration; 32 import java.time.Duration;
31 import java.time.LocalDateTime; 33 import java.time.LocalDateTime;
32 -import java.util.Map;  
33 import java.util.Objects; 34 import java.util.Objects;
34 35
35 import static cn.hutool.core.util.ObjectUtil.notEqual; 36 import static cn.hutool.core.util.ObjectUtil.notEqual;
@@ -39,6 +40,7 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString @@ -39,6 +40,7 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
39 import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.fenToYuanStr; 40 import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.fenToYuanStr;
40 import static cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert.INSTANCE; 41 import static cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert.INSTANCE;
41 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; 42 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
  43 +import static cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants.PAY_WALLET_CHANGE;
42 import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*; 44 import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
43 45
44 /** 46 /**
@@ -57,6 +59,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -57,6 +59,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
57 59
58 private static final String WALLET_RECHARGE_ORDER_SUBJECT = "钱包余额充值"; 60 private static final String WALLET_RECHARGE_ORDER_SUBJECT = "钱包余额充值";
59 61
  62 + public static final String WALLET_MONEY_PATH = "pages/user/wallet/money"; // 钱包详情页
  63 +
60 @Resource 64 @Resource
61 private PayWalletRechargeMapper walletRechargeMapper; 65 private PayWalletRechargeMapper walletRechargeMapper;
62 @Resource 66 @Resource
@@ -68,7 +72,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -68,7 +72,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
68 @Resource 72 @Resource
69 private PayWalletRechargePackageService payWalletRechargePackageService; 73 private PayWalletRechargePackageService payWalletRechargePackageService;
70 @Resource 74 @Resource
71 - private SubscribeMessageClient subscribeMessageClient; 75 + public SocialClientApi socialClientApi;
72 76
73 @Override 77 @Override
74 @Transactional(rollbackFor = Exception.class) 78 @Transactional(rollbackFor = Exception.class)
@@ -136,21 +140,21 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -136,21 +140,21 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
136 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice()); 140 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice());
137 141
138 // 4. 发送订阅消息 142 // 4. 发送订阅消息
139 - sendPayWalletChangeMessage(payOrderId, walletRecharge); 143 + getSelf().sendPayWalletChangeMessage(payOrderId, walletRecharge);
140 } 144 }
141 145
142 - // TODO @puhui999:发送,使用异步发送;@Async  
143 - private void sendPayWalletChangeMessage(Long payOrderId, PayWalletRechargeDO walletRecharge) { 146 + @Async
  147 + public void sendPayWalletChangeMessage(Long payOrderId, PayWalletRechargeDO walletRecharge) {
  148 + // 1.1 获得会员钱包信息
144 PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId()); 149 PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId());
145 - // TODO @puhui999:可以使用 MapUtil.builder();另外,不应该是并发 hashmap 哈  
146 - Map<String, String> messages = MapUtil.newConcurrentHashMap(4);  
147 - messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.NO, String.valueOf(payOrderId));  
148 - messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.PRICE,  
149 - fenToYuanStr(walletRecharge.getTotalPrice()));  
150 - messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.STATUS, "充值成功");  
151 - messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.PAY_TIME,  
152 - LocalDateTimeUtil.formatNormal(LocalDateTime.now()));  
153 - subscribeMessageClient.sendPayWalletChangeMessage(messages, wallet.getUserType(), wallet.getUserId()); 150 + // 1.2 构建并发送模版消息
  151 + socialClientApi.sendSubscribeMessage(new SocialWxSubscribeMessageSendReqDTO().setPage(WALLET_MONEY_PATH)
  152 + .setUserId(wallet.getUserId()).setUserType(wallet.getUserType()).setTemplateTitle(PAY_WALLET_CHANGE)
  153 + .setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType())
  154 + // 添加模版消息
  155 + .addMessage("phrase4", "充值成功").addMessage("character_string1", String.valueOf(payOrderId))
  156 + .addMessage("amount2", fenToYuanStr(walletRecharge.getTotalPrice()))
  157 + .addMessage("time3", LocalDateTimeUtil.formatNormal(LocalDateTime.now())));
154 } 158 }
155 159
156 @Override 160 @Override
@@ -307,4 +311,13 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -307,4 +311,13 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
307 return payOrder; 311 return payOrder;
308 } 312 }
309 313
  314 + /**
  315 + * 获得自身的代理对象,解决 AOP 生效问题
  316 + *
  317 + * @return 自己
  318 + */
  319 + private PayWalletRechargeServiceImpl getSelf() {
  320 + return SpringUtil.getBean(getClass());
  321 + }
  322 +
310 } 323 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java
@@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; @@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
5 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
6 6
7 import java.util.List; 7 import java.util.List;
8 -import java.util.Map;  
9 8
10 /** 9 /**
11 * 社交应用的 API 接口 10 * 社交应用的 API 接口
@@ -57,25 +56,11 @@ public interface SocialClientApi { @@ -57,25 +56,11 @@ public interface SocialClientApi {
57 */ 56 */
58 List<SocialWxSubscribeTemplateRespDTO> getSubscribeTemplateList(Integer userType); 57 List<SocialWxSubscribeTemplateRespDTO> getSubscribeTemplateList(Integer userType);
59 58
60 - // TODO @puhui999:sendSubscribeMessage 两个方法,可以融合成一个么?  
61 /** 59 /**
62 * 发送微信小程序订阅消息 60 * 发送微信小程序订阅消息
63 * 61 *
64 * @param reqDTO 请求 62 * @param reqDTO 请求
65 */ 63 */
66 - void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType);  
67 -  
68 - /**  
69 - * 发送微信小程序订阅消息  
70 - *  
71 - * @param templateTitle 模版标题  
72 - * @param messages 消息  
73 - * @param userType 用户类型  
74 - * @param userId 用户编号  
75 - * @param socialType 社交客服端类型  
76 - * @param path 点击模板卡片后的跳转页面,仅限本小程序内的页面  
77 - */  
78 - void sendSubscribeMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,  
79 - Integer socialType, String path); 64 + void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO);
80 65
81 } 66 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxSubscribeMessageSendReqDTO.java
1 package cn.iocoder.yudao.module.system.api.social.dto; 1 package cn.iocoder.yudao.module.system.api.social.dto;
2 2
3 -import jakarta.validation.constraints.NotNull; 3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
4 import lombok.Data; 5 import lombok.Data;
5 6
  7 +import java.util.HashMap;
6 import java.util.Map; 8 import java.util.Map;
7 9
8 /** 10 /**
9 * 微信小程序订阅消息发送 Request DTO 11 * 微信小程序订阅消息发送 Request DTO
10 * 12 *
11 - * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html">接口文档</a>  
12 * @author HUIHUI 13 * @author HUIHUI
13 */ 14 */
14 @Data 15 @Data
15 public class SocialWxSubscribeMessageSendReqDTO { 16 public class SocialWxSubscribeMessageSendReqDTO {
16 17
17 - // TODO @puhui999:貌似使用 userId + userType 会不会更合理哈。这样,后端进行查询三方用户的绑定表~  
18 /** 18 /**
19 - * 接收者(用户)的 openid.  
20 - * <pre>  
21 - * 参数:touser  
22 - * 是否必填: 是  
23 - * 描述: 接收者(用户)的 openid  
24 - * </pre> 19 + * 用户 id
  20 + *
  21 + * 关联 MemberUserDO 的 id 编号
  22 + * 关联 AdminUserDO 的 id 编号
25 */ 23 */
26 - @NotNull(message = "接收者(用户)的 openid不能为空")  
27 - private String toUser;  
28 - 24 + private Long userId;
29 /** 25 /**
30 - * 模版消息编号 26 + * 用户类型, 预留 多商户转帐可能需要用到
  27 + *
  28 + * 关联 {@link UserTypeEnum}
31 */ 29 */
32 - @NotNull(message = "模版消息编号不能为空")  
33 - private String templateId; 30 + private Integer userType;
34 31
35 /** 32 /**
36 - * 点击模板卡片后的跳转页面,仅限本小程序内的页面 33 + * 社交类型
37 * 34 *
38 - * 支持带参数,(示例 index?foo=bar )。该字段不填则模板无跳转。 35 + * 枚举 {@link SocialTypeEnum}
39 */ 36 */
40 - private String page; 37 + private Integer socialType;
41 38
42 /** 39 /**
43 - * 跳转小程序类型  
44 - *  
45 - * developer 为开发版;trial 为体验版;formal 为正式版【默认】  
46 - *  
47 - * 枚举 WxMaConstants.MiniProgramState 40 + * 消息模版标题
48 */ 41 */
49 - // TODO @puhui999:这个非必填。如果没有,代码里去默认下;  
50 - @NotNull(message = "跳转小程序类型不能为空")  
51 - private String miniprogramState; 42 + private String templateTitle;
52 43
53 /** 44 /**
54 - * 进入小程序查看的语言类型  
55 - *  
56 - * zh_CN(简体中文)【默认】、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文) 45 + * 点击模板卡片后的跳转页面,仅限本小程序内的页面
57 * 46 *
58 - * 枚举 WxMaConstants.MiniProgramLang 47 + * 支持带参数,(示例 index?foo=bar )。该字段不填则模板无跳转。
59 */ 48 */
60 - // TODO @puhui999:这个非必填。如果没有,代码里去默认下;  
61 - @NotNull(message = "进入小程序查看的语言类型不能为空")  
62 - private String lang; 49 + private String page;
63 50
64 /** 51 /**
65 * 模板内容的参数 52 * 模板内容的参数
66 */ 53 */
67 private Map<String, String> messages; 54 private Map<String, String> messages;
68 55
  56 + public SocialWxSubscribeMessageSendReqDTO addMessage(String key, String value) {
  57 + if (messages == null) {
  58 + messages = new HashMap<>();
  59 + }
  60 + messages.put(key, value);
  61 + return this;
  62 + }
  63 +
69 } 64 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java
@@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.system.api.social; @@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.system.api.social;
2 2
3 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; 3 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
4 import cn.hutool.core.collection.CollUtil; 4 import cn.hutool.core.collection.CollUtil;
5 -import cn.hutool.core.collection.CollectionUtil;  
6 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
7 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.module.system.api.social.dto.*; 8 import cn.iocoder.yudao.module.system.api.social.dto.*;
10 -import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert;  
11 import cn.iocoder.yudao.module.system.service.social.SocialClientService; 9 import cn.iocoder.yudao.module.system.service.social.SocialClientService;
12 import jakarta.annotation.Resource; 10 import jakarta.annotation.Resource;
13 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
@@ -18,7 +16,9 @@ import org.springframework.stereotype.Service; @@ -18,7 +16,9 @@ import org.springframework.stereotype.Service;
18 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
19 17
20 import java.util.List; 18 import java.util.List;
21 -import java.util.Map; 19 +
  20 +import static cn.hutool.core.collection.CollUtil.findOne;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
22 22
23 /** 23 /**
24 * 社交应用的 API 实现类 24 * 社交应用的 API 实现类
@@ -65,80 +65,36 @@ public class SocialClientApiImpl implements SocialClientApi { @@ -65,80 +65,36 @@ public class SocialClientApiImpl implements SocialClientApi {
65 @Override 65 @Override
66 public List<SocialWxSubscribeTemplateRespDTO> getSubscribeTemplateList(Integer userType) { 66 public List<SocialWxSubscribeTemplateRespDTO> getSubscribeTemplateList(Integer userType) {
67 List<TemplateInfo> subscribeTemplate = socialClientService.getSubscribeTemplateList(userType); 67 List<TemplateInfo> subscribeTemplate = socialClientService.getSubscribeTemplateList(userType);
68 - return SocialUserConvert.INSTANCE.convertList(subscribeTemplate); 68 + return convertList(subscribeTemplate, item -> BeanUtils.toBean(item, SocialWxSubscribeTemplateRespDTO.class)
  69 + .setId(item.getPriTmplId()));
69 } 70 }
70 71
71 @Override 72 @Override
72 - public void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType) {  
73 - socialClientService.sendSubscribeMessage(reqDTO, userType);  
74 - }  
75 -  
76 - public void sendSubscribeMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,  
77 - Integer socialType, String path) {  
78 - // TODO @puhui999:建议是,先不拆小方法。因为逻辑的复杂度其实不高哈。合在一个方法里,因为咱写了 1.1 1.2 2. 这样的逻辑,也能一下子看懂。  
79 - // 1.1 获得订阅模版  
80 - SocialWxSubscribeTemplateRespDTO template = getTemplate(templateTitle, userType);  
81 - if (template == null) { 73 + public void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO) {
  74 + // 1.1 获得订阅模版列表
  75 + List<SocialWxSubscribeTemplateRespDTO> templateList = getSubscribeTemplateList(reqDTO.getUserType());
  76 + if (CollUtil.isEmpty(templateList)) {
  77 + log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
82 return; 78 return;
83 } 79 }
84 - // 1.2 获得发送对象的 openId  
85 - String openId = getUserOpenId(userType, userId, socialType);  
86 - if (StrUtil.isBlankIfStr(openId)) { 80 + // 1.2 获得需要使用的模版
  81 + SocialWxSubscribeTemplateRespDTO template = findOne(templateList, item ->
  82 + ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle()));
  83 + if (template == null) {
  84 + log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO);
87 return; 85 return;
88 } 86 }
89 87
90 - // 2. 发送消息  
91 - sendSubscribeMessage(buildMessageSendReqDTO(openId, path, template).setMessages(messages), userType);  
92 - }  
93 -  
94 - /**  
95 - * 构建发送消息请求参数  
96 - *  
97 - * @param openId 接收者(用户)的 openid  
98 - * @param path 点击模板卡片后的跳转页面,仅限本小程序内的页面  
99 - * @param template 订阅模版  
100 - * @return 微信小程序订阅消息发送  
101 - */  
102 - private SocialWxSubscribeMessageSendReqDTO buildMessageSendReqDTO(String openId, String path,  
103 - SocialWxSubscribeTemplateRespDTO template) {  
104 - return new SocialWxSubscribeMessageSendReqDTO().setLang("zh_CN").setMiniprogramState(envVersion)  
105 - .setTemplateId(template.getId()).setToUser(openId).setPage(path);  
106 - }  
107 -  
108 - // TODO @puhui999:建议下沉到 service 实现。  
109 - /**  
110 - * 获得小程序订阅消息模版  
111 - *  
112 - * @param templateTitle 模版标题  
113 - * @param userType 用户类型  
114 - * @return 小程序订阅消息模版  
115 - */  
116 - private SocialWxSubscribeTemplateRespDTO getTemplate(String templateTitle, Integer userType) {  
117 - List<SocialWxSubscribeTemplateRespDTO> templateList = getSubscribeTemplateList(userType);  
118 - if (CollUtil.isEmpty(templateList)) {  
119 - log.warn("[getTemplate][templateTitle({}) userType({}) 没有找到订阅模板]", templateTitle, userType);  
120 - return null;  
121 - }  
122 - return CollectionUtil.findOne(templateList, item -> ObjUtil.equal(item.getTitle(), templateTitle));  
123 - }  
124 -  
125 - // TODO @puhui999:建议下沉到 service 实现。  
126 - /**  
127 - * 获得用户 openId  
128 - *  
129 - * @param userType 用户类型  
130 - * @param userId 用户编号  
131 - * @param socialType 社交类型  
132 - * @return 用户 openId  
133 - */  
134 - private String getUserOpenId(Integer userType, Long userId, Integer socialType) {  
135 - SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(userType, userId, socialType); 88 + // 2. 获得社交用户
  89 + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(reqDTO.getUserType(), reqDTO.getUserId(),
  90 + reqDTO.getSocialType());
136 if (StrUtil.isBlankIfStr(socialUser.getOpenid())) { 91 if (StrUtil.isBlankIfStr(socialUser.getOpenid())) {
137 - log.warn("[getUserOpenId][userType({}) userId({}) socialType({}) 会员 openid 缺失]",  
138 - userType, userId, socialType);  
139 - return null; 92 + log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:会员 openid 缺失]", reqDTO);
  93 + return;
140 } 94 }
141 - return socialUser.getOpenid(); 95 +
  96 + // 3. 发送订阅消息
  97 + socialClientService.sendSubscribeMessage(reqDTO, template.getId(), socialUser.getOpenid());
142 } 98 }
143 99
144 } 100 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http
@@ -6,14 +6,15 @@ Content-Type: application/json @@ -6,14 +6,15 @@ Content-Type: application/json
6 tenant-id: {{adminTenentId}} 6 tenant-id: {{adminTenentId}}
7 7
8 { 8 {
9 - "toUser": "oKNkb4xxw2H135-MVPKtEMkumK08",  
10 - "templateId": "W4ybDTIwCfKHtMKR7fSfx83DtmVKEeXQo3Ti7GCw4_4",  
11 - "miniprogramState": "developer",  
12 - "lang": "zh_CN", 9 + "userId": 247,
  10 + "userType": 1,
  11 + "socialType": 34,
  12 + "templateTitle": "充值成功通知",
  13 + "page": "",
13 "messages": { 14 "messages": {
14 "character_string1":"5616122165165", 15 "character_string1":"5616122165165",
15 "amount2":"1000.00", 16 "amount2":"1000.00",
16 "time3":"2024-01-01 10:10:10", 17 "time3":"2024-01-01 10:10:10",
17 - "phrase4":"成功" 18 + "phrase4": "充值成功"
18 } 19 }
19 } 20 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java
1 package cn.iocoder.yudao.module.system.controller.admin.socail; 1 package cn.iocoder.yudao.module.system.controller.admin.socail;
2 2
3 -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;  
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
7 import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeMessageSendReqDTO; 7 import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeMessageSendReqDTO;
8 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientRespVO; 9 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientRespVO;
@@ -29,6 +29,8 @@ public class SocialClientController { @@ -29,6 +29,8 @@ public class SocialClientController {
29 29
30 @Resource 30 @Resource
31 private SocialClientService socialClientService; 31 private SocialClientService socialClientService;
  32 + @Resource
  33 + private SocialClientApi socialClientApi;
32 34
33 @PostMapping("/create") 35 @PostMapping("/create")
34 @Operation(summary = "创建社交客户端") 36 @Operation(summary = "创建社交客户端")
@@ -75,7 +77,7 @@ public class SocialClientController { @@ -75,7 +77,7 @@ public class SocialClientController {
75 @Operation(summary = "发送订阅消息") // 用于测试 77 @Operation(summary = "发送订阅消息") // 用于测试
76 @PreAuthorize("@ss.hasPermission('system:social-client:query')") 78 @PreAuthorize("@ss.hasPermission('system:social-client:query')")
77 public void sendSubscribeMessage(@RequestBody SocialWxSubscribeMessageSendReqDTO reqDTO) { 79 public void sendSubscribeMessage(@RequestBody SocialWxSubscribeMessageSendReqDTO reqDTO) {
78 - socialClientService.sendSubscribeMessage(reqDTO, UserTypeEnum.MEMBER.getValue()); 80 + socialClientApi.sendSubscribeMessage(reqDTO);
79 } 81 }
80 82
81 } 83 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java
1 package cn.iocoder.yudao.module.system.convert.social; 1 package cn.iocoder.yudao.module.system.convert.social;
2 2
3 -import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;  
4 -import cn.hutool.core.collection.CollUtil;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; 3 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
7 -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeMessageSendReqDTO;  
8 -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeTemplateRespDTO;  
9 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; 4 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO;
10 -import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;  
11 import org.mapstruct.Mapper; 5 import org.mapstruct.Mapper;
12 import org.mapstruct.Mapping; 6 import org.mapstruct.Mapping;
13 import org.mapstruct.factory.Mappers; 7 import org.mapstruct.factory.Mappers;
14 8
15 -import java.util.ArrayList;  
16 -import java.util.List;  
17 -import java.util.Map;  
18 -  
19 -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;  
20 -  
21 @Mapper 9 @Mapper
22 public interface SocialUserConvert { 10 public interface SocialUserConvert {
23 11
@@ -26,25 +14,4 @@ public interface SocialUserConvert { @@ -26,25 +14,4 @@ public interface SocialUserConvert {
26 @Mapping(source = "reqVO.type", target = "socialType") 14 @Mapping(source = "reqVO.type", target = "socialType")
27 SocialUserBindReqDTO convert(Long userId, Integer userType, SocialUserBindReqVO reqVO); 15 SocialUserBindReqDTO convert(Long userId, Integer userType, SocialUserBindReqVO reqVO);
28 16
29 - // TODO @puhui999:要不 convert 直接放到 service 里。  
30 - default WxMaSubscribeMessage convert(SocialWxSubscribeMessageSendReqDTO reqDTO) {  
31 - WxMaSubscribeMessage message = BeanUtils.toBean(reqDTO, WxMaSubscribeMessage.class);  
32 - Map<String, String> messages = reqDTO.getMessages();  
33 - if (CollUtil.isNotEmpty(messages)) {  
34 - messages.keySet().forEach(key -> findAndThen(messages, key, value -> message.addData(new WxMaSubscribeMessage.MsgData(key, value))));  
35 - }  
36 - return message;  
37 - }  
38 -  
39 - // TODO @puhui999:要不 convert 直接放到 service 里。其实可以 BeanUtils.toBean(reqDTO, WxMaSubscribeMessage.class) 来搞的呀。  
40 - @Mapping(target = "id", source = "priTmplId")  
41 - SocialWxSubscribeTemplateRespDTO convert(TemplateInfo templateInfo);  
42 -  
43 - // TODO @puhui999:是不是用 CollectionUtils.convertList 就 ok 啦。  
44 - default List<SocialWxSubscribeTemplateRespDTO> convertList(List<TemplateInfo> subscribeTemplate) {  
45 - List<SocialWxSubscribeTemplateRespDTO> list = new ArrayList<>();  
46 - subscribeTemplate.forEach(templateInfo -> list.add(convert(templateInfo)));  
47 - return list;  
48 - }  
49 -  
50 } 17 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java
@@ -83,9 +83,11 @@ public interface SocialClientService { @@ -83,9 +83,11 @@ public interface SocialClientService {
83 /** 83 /**
84 * 发送微信小程序订阅消息 84 * 发送微信小程序订阅消息
85 * 85 *
86 - * @param reqDTO 请求 86 + * @param reqDTO 请求
  87 + * @param templateId 模版编号
  88 + * @param openId 会员 openId
87 */ 89 */
88 - void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType); 90 + void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, String templateId, String openId);
89 91
90 // =================== 客户端管理 =================== 92 // =================== 客户端管理 ===================
91 93
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
@@ -4,8 +4,10 @@ import cn.binarywang.wx.miniapp.api.WxMaService; @@ -4,8 +4,10 @@ import cn.binarywang.wx.miniapp.api.WxMaService;
4 import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; 4 import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
5 import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; 5 import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
6 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; 6 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
  7 +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
7 import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; 8 import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl;
8 import cn.hutool.core.bean.BeanUtil; 9 import cn.hutool.core.bean.BeanUtil;
  10 +import cn.hutool.core.collection.CollUtil;
9 import cn.hutool.core.lang.Assert; 11 import cn.hutool.core.lang.Assert;
10 import cn.hutool.core.util.ObjUtil; 12 import cn.hutool.core.util.ObjUtil;
11 import cn.hutool.core.util.ReflectUtil; 13 import cn.hutool.core.util.ReflectUtil;
@@ -19,7 +21,6 @@ import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; @@ -19,7 +21,6 @@ import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO;
19 import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeMessageSendReqDTO; 21 import cn.iocoder.yudao.module.system.api.social.dto.SocialWxSubscribeMessageSendReqDTO;
20 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; 22 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
21 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; 23 import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
22 -import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert;  
23 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; 24 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
24 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; 25 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
25 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; 26 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
@@ -51,9 +52,11 @@ import org.springframework.stereotype.Service; @@ -51,9 +52,11 @@ import org.springframework.stereotype.Service;
51 52
52 import java.time.Duration; 53 import java.time.Duration;
53 import java.util.List; 54 import java.util.List;
  55 +import java.util.Map;
54 import java.util.Objects; 56 import java.util.Objects;
55 57
56 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 58 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  59 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
57 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 60 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
58 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 61 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
59 62
@@ -273,18 +276,40 @@ public class SocialClientServiceImpl implements SocialClientService { @@ -273,18 +276,40 @@ public class SocialClientServiceImpl implements SocialClientService {
273 } 276 }
274 277
275 @Override 278 @Override
276 - public void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType) {  
277 - WxMaService service = getWxMaService(userType); 279 + public void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, String templateId, String openId) {
  280 + WxMaService service = getWxMaService(reqDTO.getUserType());
278 try { 281 try {
279 WxMaSubscribeService subscribeService = service.getSubscribeService(); 282 WxMaSubscribeService subscribeService = service.getSubscribeService();
280 - subscribeService.sendSubscribeMsg(SocialUserConvert.INSTANCE.convert(reqDTO)); 283 + subscribeService.sendSubscribeMsg(buildMessageSendReqDTO(reqDTO, templateId, openId));
281 } catch (WxErrorException e) { 284 } catch (WxErrorException e) {
282 - log.error("[sendSubscribeMessage][reqVO({}) userType({}) 发送小程序订阅消息]", reqDTO, userType, e); 285 + log.error("[sendSubscribeMessage][reqVO({}) templateId({}) openId({}) 发送小程序订阅消息失败]", reqDTO, templateId, openId, e);
283 throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR); 286 throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR);
284 } 287 }
285 } 288 }
286 289
287 /** 290 /**
  291 + * 构建发送消息请求参数
  292 + *
  293 + * @param reqDTO 请求
  294 + * @param templateId 模版编号
  295 + * @param openId 会员 openId
  296 + * @return 微信小程序订阅消息发送
  297 + */
  298 + private WxMaSubscribeMessage buildMessageSendReqDTO(SocialWxSubscribeMessageSendReqDTO reqDTO,
  299 + String templateId, String openId) {
  300 + // 1.1 设置订阅消息基本参数
  301 + WxMaSubscribeMessage subscribeMessage = new WxMaSubscribeMessage().setLang("zh_CN").setMiniprogramState(envVersion)
  302 + .setTemplateId(templateId).setToUser(openId).setPage(reqDTO.getPage());
  303 + // 1.2 设置具体消息参数
  304 + Map<String, String> messages = reqDTO.getMessages();
  305 + if (CollUtil.isNotEmpty(messages)) {
  306 + messages.keySet().forEach(key -> findAndThen(messages, key, value ->
  307 + subscribeMessage.addData(new WxMaSubscribeMessage.MsgData(key, value))));
  308 + }
  309 + return subscribeMessage;
  310 + }
  311 +
  312 + /**
288 * 获得 clientId + clientSecret 对应的 WxMpService 对象 313 * 获得 clientId + clientSecret 对应的 WxMpService 对象
289 * 314 *
290 * @param userType 用户类型 315 * @param userType 用户类型