Commit 9a926e251be96d0813b121ca05ca9acbc0db4c14

Authored by puhui999
1 parent 46961052

【新增】mall: 钱包充值成功发送订阅消息

yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java
1 package cn.iocoder.yudao.module.trade.enums; 1 package cn.iocoder.yudao.module.trade.enums;
2 2
3 // TODO @芋艿:枚举 3 // TODO @芋艿:枚举
  4 +
4 /** 5 /**
5 * 通知模板枚举类 6 * 通知模板枚举类
6 * 7 *
@@ -17,4 +18,15 @@ public interface MessageTemplateConstants { @@ -17,4 +18,15 @@ public interface MessageTemplateConstants {
17 18
18 String TRADE_AFTER_SALE_CHANGE = "售后进度通知"; 19 String TRADE_AFTER_SALE_CHANGE = "售后进度通知";
19 20
  21 + /**
  22 + * 售后进度通知相关参数枚举
  23 + *
  24 + * @author HUIHUI
  25 + */
  26 + class TradeAfterSaleChangeReqParams {
  27 +
  28 + public static final String ORDER_DELIVERY = "order_delivery";
  29 +
  30 + }
  31 +
20 } 32 }
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java
1 package cn.iocoder.yudao.module.pay.enums; 1 package cn.iocoder.yudao.module.pay.enums;
2 2
3 // TODO @芋艿:枚举 3 // TODO @芋艿:枚举
  4 +
4 /** 5 /**
5 * 通知模板枚举类 6 * 通知模板枚举类
6 * 7 *
@@ -12,4 +13,18 @@ public interface MessageTemplateConstants { @@ -12,4 +13,18 @@ public interface MessageTemplateConstants {
12 13
13 String PAY_WALLET_CHANGE = "充值成功通知"; 14 String PAY_WALLET_CHANGE = "充值成功通知";
14 15
  16 + /**
  17 + * 充值成功通知模版参数
  18 + *
  19 + * @author HUIHUI
  20 + */
  21 + class PayWalletChangeTemplateParams {
  22 +
  23 + public static final String NO = "character_string1"; // 流水编号
  24 + public static final String PRICE = "amount2"; // 充值金额
  25 + public static final String PAY_TIME = "time3"; // 充值时间
  26 + public static final String STATUS = "phrase4"; // 充值状态
  27 +
  28 + }
  29 +
15 } 30 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.message;
0 \ No newline at end of file 2 \ 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 0 → 100644
  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 +/**
  15 + * 订阅消息
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Component
  20 +@Slf4j
  21 +public class SubscribeMessageClient {
  22 +
  23 + public static final String WALLET_MONEY_PATH = "pages/user/wallet/money"; // 钱包详情页
  24 +
  25 + @Resource
  26 + public SocialClientApi socialClientApi;
  27 +
  28 + /**
  29 + * 发送钱包充值通知
  30 + *
  31 + * @param messages 消息
  32 + * @param userType 用户类型
  33 + * @param userId 用户编号
  34 + */
  35 + @Async
  36 + public void sendPayWalletChangeMessage(Map<String, String> messages, Integer userType, Long userId) {
  37 + sendWxMessage(PAY_WALLET_CHANGE, messages, userType, userId, WALLET_MONEY_PATH);
  38 + }
  39 +
  40 +
  41 + /**
  42 + * 发送微信订阅消息
  43 + *
  44 + * @param templateTitle 模版标题
  45 + * @param messages 消息
  46 + * @param userType 用户类型
  47 + * @param userId 用户编号
  48 + * @param path 点击模板卡片后的跳转页面,仅限本小程序内的页面
  49 + */
  50 + private void sendWxMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,
  51 + String path) {
  52 + socialClientApi.sendSubscribeMessage(templateTitle, messages, userType, userId, SocialTypeEnum.WECHAT_MINI_APP.getType(), path);
  53 + }
  54 +
  55 +}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java
1 package cn.iocoder.yudao.module.pay.service.wallet; 1 package cn.iocoder.yudao.module.pay.service.wallet;
2 2
  3 +import cn.hutool.core.date.LocalDateTimeUtil;
3 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
  5 +import cn.hutool.core.map.MapUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageParam; 6 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
@@ -13,24 +15,28 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; @@ -13,24 +15,28 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
13 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 15 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
14 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; 16 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO;
15 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper; 17 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper;
16 -import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; 18 +import cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants;
17 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 19 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
18 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; 20 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
  21 +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
  22 +import cn.iocoder.yudao.module.pay.message.subscribe.SubscribeMessageClient;
19 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 23 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
20 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 24 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  25 +import jakarta.annotation.Resource;
21 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
23 import org.springframework.transaction.annotation.Transactional; 28 import org.springframework.transaction.annotation.Transactional;
24 29
25 -import jakarta.annotation.Resource;  
26 import java.time.Duration; 30 import java.time.Duration;
27 import java.time.LocalDateTime; 31 import java.time.LocalDateTime;
  32 +import java.util.Map;
28 import java.util.Objects; 33 import java.util.Objects;
29 34
30 import static cn.hutool.core.util.ObjectUtil.notEqual; 35 import static cn.hutool.core.util.ObjectUtil.notEqual;
31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
32 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; 37 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
33 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 38 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
  39 +import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.fenToYuanStr;
34 import static cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert.INSTANCE; 40 import static cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert.INSTANCE;
35 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; 41 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
36 import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*; 42 import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
@@ -61,6 +67,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -61,6 +67,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
61 private PayRefundService payRefundService; 67 private PayRefundService payRefundService;
62 @Resource 68 @Resource
63 private PayWalletRechargePackageService payWalletRechargePackageService; 69 private PayWalletRechargePackageService payWalletRechargePackageService;
  70 + @Resource
  71 + private SubscribeMessageClient subscribeMessageClient;
64 72
65 @Override 73 @Override
66 @Transactional(rollbackFor = Exception.class) 74 @Transactional(rollbackFor = Exception.class)
@@ -96,7 +104,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -96,7 +104,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
96 104
97 @Override 105 @Override
98 public PageResult<PayWalletRechargeDO> getWalletRechargePackagePage(Long userId, Integer userType, 106 public PageResult<PayWalletRechargeDO> getWalletRechargePackagePage(Long userId, Integer userType,
99 - PageParam pageReqVO, Boolean payStatus) { 107 + PageParam pageReqVO, Boolean payStatus) {
100 PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType); 108 PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType);
101 return walletRechargeMapper.selectPage(pageReqVO, wallet.getId(), payStatus); 109 return walletRechargeMapper.selectPage(pageReqVO, wallet.getId(), payStatus);
102 } 110 }
@@ -126,6 +134,21 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -126,6 +134,21 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
126 // TODO 需要钱包中加个可提现余额 134 // TODO 需要钱包中加个可提现余额
127 payWalletService.addWalletBalance(walletRecharge.getWalletId(), String.valueOf(id), 135 payWalletService.addWalletBalance(walletRecharge.getWalletId(), String.valueOf(id),
128 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice()); 136 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice());
  137 +
  138 + // 4. 发送订阅消息
  139 + sendPayWalletChangeMessage(payOrderId, walletRecharge);
  140 + }
  141 +
  142 + private void sendPayWalletChangeMessage(Long payOrderId, PayWalletRechargeDO walletRecharge) {
  143 + PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId());
  144 + Map<String, String> messages = MapUtil.newConcurrentHashMap(4);
  145 + messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.NO, String.valueOf(payOrderId));
  146 + messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.PRICE,
  147 + fenToYuanStr(walletRecharge.getTotalPrice()));
  148 + messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.STATUS, "充值成功");
  149 + messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.PAY_TIME,
  150 + LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
  151 + subscribeMessageClient.sendPayWalletChangeMessage(messages, wallet.getUserType(), wallet.getUserId());
129 } 152 }
130 153
131 @Override 154 @Override
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; @@ -5,6 +5,7 @@ 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;
8 9
9 /** 10 /**
10 * 社交应用的 API 接口 11 * 社交应用的 API 接口
@@ -63,4 +64,17 @@ public interface SocialClientApi { @@ -63,4 +64,17 @@ public interface SocialClientApi {
63 */ 64 */
64 void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType); 65 void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType);
65 66
  67 + /**
  68 + * 发送微信小程序订阅消息
  69 + *
  70 + * @param templateTitle 模版标题
  71 + * @param messages 消息
  72 + * @param userType 用户类型
  73 + * @param userId 用户编号
  74 + * @param socialType 社交客服端类型
  75 + * @param path 点击模板卡片后的跳转页面,仅限本小程序内的页面
  76 + */
  77 + void sendSubscribeMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,
  78 + Integer socialType, String path);
  79 +
66 } 80 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxSubscribeMessageSendReqDTO.java
@@ -73,12 +73,4 @@ public class SocialWxSubscribeMessageSendReqDTO { @@ -73,12 +73,4 @@ public class SocialWxSubscribeMessageSendReqDTO {
73 */ 73 */
74 private Map<String, String> messages; 74 private Map<String, String> messages;
75 75
76 - public SocialWxSubscribeMessageSendReqDTO addData(String key, String value) {  
77 - if (messages == null) {  
78 - messages = new HashMap<>();  
79 - }  
80 - messages.put(key, value);  
81 - return this;  
82 - }  
83 -  
84 } 76 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java
1 package cn.iocoder.yudao.module.system.api.social; 1 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;
  5 +import cn.hutool.core.collection.CollectionUtil;
  6 +import cn.hutool.core.util.ObjUtil;
  7 +import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.system.api.social.dto.*; 9 import cn.iocoder.yudao.module.system.api.social.dto.*;
6 import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert; 10 import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert;
7 import cn.iocoder.yudao.module.system.service.social.SocialClientService; 11 import cn.iocoder.yudao.module.system.service.social.SocialClientService;
8 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
  13 +import lombok.extern.slf4j.Slf4j;
9 import me.chanjar.weixin.common.bean.WxJsapiSignature; 14 import me.chanjar.weixin.common.bean.WxJsapiSignature;
10 import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; 15 import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
  16 +import org.springframework.beans.factory.annotation.Value;
11 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
12 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
13 19
14 import java.util.List; 20 import java.util.List;
  21 +import java.util.Map;
15 22
16 /** 23 /**
17 * 社交应用的 API 实现类 24 * 社交应用的 API 实现类
@@ -20,10 +27,18 @@ import java.util.List; @@ -20,10 +27,18 @@ import java.util.List;
20 */ 27 */
21 @Service 28 @Service
22 @Validated 29 @Validated
  30 +@Slf4j
23 public class SocialClientApiImpl implements SocialClientApi { 31 public class SocialClientApiImpl implements SocialClientApi {
24 32
  33 + /**
  34 + * 小程序版本
  35 + */
  36 + @Value("${yudao.wxa-code.env-version}")
  37 + public String envVersion;
25 @Resource 38 @Resource
26 private SocialClientService socialClientService; 39 private SocialClientService socialClientService;
  40 + @Resource
  41 + public SocialUserApi socialUserApi;
27 42
28 @Override 43 @Override
29 public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { 44 public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
@@ -58,4 +73,69 @@ public class SocialClientApiImpl implements SocialClientApi { @@ -58,4 +73,69 @@ public class SocialClientApiImpl implements SocialClientApi {
58 socialClientService.sendSubscribeMessage(reqDTO, userType); 73 socialClientService.sendSubscribeMessage(reqDTO, userType);
59 } 74 }
60 75
  76 + public void sendSubscribeMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,
  77 + Integer socialType, String path) {
  78 + // 1.1 获得订阅模版
  79 + SocialWxSubscribeTemplateRespDTO template = getTemplate(templateTitle, userType);
  80 + if (template == null) {
  81 + return;
  82 + }
  83 + // 1.2 获得发送对象的 openId
  84 + String openId = getUserOpenId(userType, userId, socialType);
  85 + if (StrUtil.isBlankIfStr(openId)) {
  86 + return;
  87 + }
  88 +
  89 + // 2. 发送消息
  90 + sendSubscribeMessage(buildMessageSendReqDTO(openId, path, template).setMessages(messages), userType);
  91 + }
  92 +
  93 + /**
  94 + * 构建发送消息请求参数
  95 + *
  96 + * @param openId 接收者(用户)的 openid
  97 + * @param path 点击模板卡片后的跳转页面,仅限本小程序内的页面
  98 + * @param template 订阅模版
  99 + * @return 微信小程序订阅消息发送
  100 + */
  101 + private SocialWxSubscribeMessageSendReqDTO buildMessageSendReqDTO(String openId, String path,
  102 + SocialWxSubscribeTemplateRespDTO template) {
  103 + return new SocialWxSubscribeMessageSendReqDTO().setLang("zh_CN").setMiniprogramState(envVersion)
  104 + .setTemplateId(template.getId()).setToUser(openId).setPage(path);
  105 + }
  106 +
  107 + /**
  108 + * 获得小程序订阅消息模版
  109 + *
  110 + * @param templateTitle 模版标题
  111 + * @param userType 用户类型
  112 + * @return 小程序订阅消息模版
  113 + */
  114 + private SocialWxSubscribeTemplateRespDTO getTemplate(String templateTitle, Integer userType) {
  115 + List<SocialWxSubscribeTemplateRespDTO> templateList = getSubscribeTemplateList(userType);
  116 + if (CollUtil.isEmpty(templateList)) {
  117 + log.warn("[getTemplate][templateTitle({}) userType({}) 没有找到订阅模板]", templateTitle, userType);
  118 + return null;
  119 + }
  120 + return CollectionUtil.findOne(templateList, item -> ObjUtil.equal(item.getTitle(), templateTitle));
  121 + }
  122 +
  123 + /**
  124 + * 获得用户 openId
  125 + *
  126 + * @param userType 用户类型
  127 + * @param userId 用户编号
  128 + * @param socialType 社交类型
  129 + * @return 用户 openId
  130 + */
  131 + private String getUserOpenId(Integer userType, Long userId, Integer socialType) {
  132 + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(userType, userId, socialType);
  133 + if (StrUtil.isBlankIfStr(socialUser.getOpenid())) {
  134 + log.warn("[getUserOpenId][userType({}) userId({}) socialType({}) 会员 openid 缺失]",
  135 + userType, userId, socialType);
  136 + return null;
  137 + }
  138 + return socialUser.getOpenid();
  139 + }
  140 +
61 } 141 }