Browse Source

Merge branch 'develop' of https://gitee.com/puhui999/ruoyi-vue-pro into develop

# Conflicts:
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java
#	yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java
YunaiV 7 months ago
parent
commit
f46d47a966

+ 11 - 0
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java

@@ -18,4 +18,15 @@ public interface MessageTemplateConstants {
 
     String TRADE_AFTER_SALE_CHANGE = "售后进度通知";
 
+    /**
+     * 售后进度通知相关参数枚举
+     *
+     * @author HUIHUI
+     */
+    class TradeAfterSaleChangeReqParams {
+
+        public static final String ORDER_DELIVERY = "order_delivery";
+
+    }
+
 }

+ 14 - 0
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java

@@ -11,4 +11,18 @@ public interface MessageTemplateConstants {
 
     String PAY_WALLET_CHANGE = "充值成功通知";
 
+    /**
+     * 充值成功通知模版参数
+     *
+     * @author HUIHUI
+     */
+    class PayWalletChangeTemplateParams {
+
+        public static final String NO = "character_string1"; // 流水编号
+        public static final String PRICE = "amount2"; // 充值金额
+        public static final String PAY_TIME = "time3"; // 充值时间
+        public static final String STATUS = "phrase4"; // 充值状态
+
+    }
+
 }

+ 1 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/package-info.java

@@ -0,0 +1 @@
+package cn.iocoder.yudao.module.pay.message;

+ 55 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/subscribe/SubscribeMessageClient.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.pay.message.subscribe;
+
+import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
+import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+import static cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants.PAY_WALLET_CHANGE;
+
+/**
+ * 订阅消息
+ *
+ * @author HUIHUI
+ */
+@Component
+@Slf4j
+public class SubscribeMessageClient {
+
+    public static final String WALLET_MONEY_PATH = "pages/user/wallet/money"; // 钱包详情页
+
+    @Resource
+    public SocialClientApi socialClientApi;
+
+    /**
+     * 发送钱包充值通知
+     *
+     * @param messages 消息
+     * @param userType 用户类型
+     * @param userId   用户编号
+     */
+    @Async
+    public void sendPayWalletChangeMessage(Map<String, String> messages, Integer userType, Long userId) {
+        sendWxMessage(PAY_WALLET_CHANGE, messages, userType, userId, WALLET_MONEY_PATH);
+    }
+
+
+    /**
+     * 发送微信订阅消息
+     *
+     * @param templateTitle 模版标题
+     * @param messages      消息
+     * @param userType      用户类型
+     * @param userId        用户编号
+     * @param path          点击模板卡片后的跳转页面,仅限本小程序内的页面
+     */
+    private void sendWxMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,
+                               String path) {
+        socialClientApi.sendSubscribeMessage(templateTitle, messages, userType, userId, SocialTypeEnum.WECHAT_MINI_APP.getType(), path);
+    }
+
+}

+ 26 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.pay.service.wallet;
 
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
@@ -13,24 +15,28 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper;
-import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.MessageTemplateConstants;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
+import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.message.subscribe.SubscribeMessageClient;
 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import jakarta.annotation.Resource;
 import java.time.Duration;
 import java.time.LocalDateTime;
+import java.util.Map;
 import java.util.Objects;
 
 import static cn.hutool.core.util.ObjectUtil.notEqual;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+import static cn.iocoder.yudao.framework.common.util.number.MoneyUtils.fenToYuanStr;
 import static cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert.INSTANCE;
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
@@ -61,6 +67,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
     private PayRefundService payRefundService;
     @Resource
     private PayWalletRechargePackageService payWalletRechargePackageService;
+    @Resource
+    private SubscribeMessageClient subscribeMessageClient;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -96,7 +104,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
 
     @Override
     public PageResult<PayWalletRechargeDO> getWalletRechargePackagePage(Long userId, Integer userType,
-                                                                               PageParam pageReqVO, Boolean payStatus) {
+                                                                        PageParam pageReqVO, Boolean payStatus) {
         PayWalletDO wallet = payWalletService.getOrCreateWallet(userId, userType);
         return walletRechargeMapper.selectPage(pageReqVO, wallet.getId(), payStatus);
     }
@@ -126,6 +134,21 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
         // TODO 需要钱包中加个可提现余额
         payWalletService.addWalletBalance(walletRecharge.getWalletId(), String.valueOf(id),
                 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice());
+
+        // 4. 发送订阅消息
+        sendPayWalletChangeMessage(payOrderId, walletRecharge);
+    }
+
+    private void sendPayWalletChangeMessage(Long payOrderId, PayWalletRechargeDO walletRecharge) {
+        PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId());
+        Map<String, String> messages = MapUtil.newConcurrentHashMap(4);
+        messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.NO, String.valueOf(payOrderId));
+        messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.PRICE,
+                fenToYuanStr(walletRecharge.getTotalPrice()));
+        messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.STATUS, "充值成功");
+        messages.put(MessageTemplateConstants.PayWalletChangeTemplateParams.PAY_TIME,
+                LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
+        subscribeMessageClient.sendPayWalletChangeMessage(messages, wallet.getUserType(), wallet.getUserId());
     }
 
     @Override

+ 14 - 0
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;
 import jakarta.validation.Valid;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 社交应用的 API 接口
@@ -63,4 +64,17 @@ public interface SocialClientApi {
      */
     void sendSubscribeMessage(SocialWxSubscribeMessageSendReqDTO reqDTO, Integer userType);
 
+    /**
+     * 发送微信小程序订阅消息
+     *
+     * @param templateTitle 模版标题
+     * @param messages      消息
+     * @param userType      用户类型
+     * @param userId        用户编号
+     * @param socialType    社交客服端类型
+     * @param path          点击模板卡片后的跳转页面,仅限本小程序内的页面
+     */
+    void sendSubscribeMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,
+                              Integer socialType, String path);
+
 }

+ 80 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java

@@ -1,17 +1,24 @@
 package cn.iocoder.yudao.module.system.api.social;
 
 import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.system.api.social.dto.*;
 import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert;
 import cn.iocoder.yudao.module.system.service.social.SocialClientService;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.bean.WxJsapiSignature;
 import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 社交应用的 API 实现类
@@ -20,10 +27,18 @@ import java.util.List;
  */
 @Service
 @Validated
+@Slf4j
 public class SocialClientApiImpl implements SocialClientApi {
 
+    /**
+     * 小程序版本
+     */
+    @Value("${yudao.wxa-code.env-version}")
+    public String envVersion;
     @Resource
     private SocialClientService socialClientService;
+    @Resource
+    public SocialUserApi socialUserApi;
 
     @Override
     public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) {
@@ -58,4 +73,69 @@ public class SocialClientApiImpl implements SocialClientApi {
         socialClientService.sendSubscribeMessage(reqDTO, userType);
     }
 
+    public void sendSubscribeMessage(String templateTitle, Map<String, String> messages, Integer userType, Long userId,
+                            Integer socialType, String path) {
+        // 1.1 获得订阅模版
+        SocialWxSubscribeTemplateRespDTO template = getTemplate(templateTitle, userType);
+        if (template == null) {
+            return;
+        }
+        // 1.2 获得发送对象的 openId
+        String openId = getUserOpenId(userType, userId, socialType);
+        if (StrUtil.isBlankIfStr(openId)) {
+            return;
+        }
+
+        // 2. 发送消息
+        sendSubscribeMessage(buildMessageSendReqDTO(openId, path, template).setMessages(messages), userType);
+    }
+
+    /**
+     * 构建发送消息请求参数
+     *
+     * @param openId   接收者(用户)的 openid
+     * @param path     点击模板卡片后的跳转页面,仅限本小程序内的页面
+     * @param template 订阅模版
+     * @return 微信小程序订阅消息发送
+     */
+    private SocialWxSubscribeMessageSendReqDTO buildMessageSendReqDTO(String openId, String path,
+                                                                      SocialWxSubscribeTemplateRespDTO template) {
+        return new SocialWxSubscribeMessageSendReqDTO().setLang("zh_CN").setMiniprogramState(envVersion)
+                .setTemplateId(template.getId()).setToUser(openId).setPage(path);
+    }
+
+    /**
+     * 获得小程序订阅消息模版
+     *
+     * @param templateTitle 模版标题
+     * @param userType      用户类型
+     * @return 小程序订阅消息模版
+     */
+    private SocialWxSubscribeTemplateRespDTO getTemplate(String templateTitle, Integer userType) {
+        List<SocialWxSubscribeTemplateRespDTO> templateList = getSubscribeTemplateList(userType);
+        if (CollUtil.isEmpty(templateList)) {
+            log.warn("[getTemplate][templateTitle({}) userType({}) 没有找到订阅模板]", templateTitle, userType);
+            return null;
+        }
+        return CollectionUtil.findOne(templateList, item -> ObjUtil.equal(item.getTitle(), templateTitle));
+    }
+
+    /**
+     * 获得用户 openId
+     *
+     * @param userType   用户类型
+     * @param userId     用户编号
+     * @param socialType 社交类型
+     * @return 用户 openId
+     */
+    private String getUserOpenId(Integer userType, Long userId, Integer socialType) {
+        SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(userType, userId, socialType);
+        if (StrUtil.isBlankIfStr(socialUser.getOpenid())) {
+            log.warn("[getUserOpenId][userType({}) userId({}) socialType({}) 会员 openid 缺失]",
+                    userType, userId, socialType);
+            return null;
+        }
+        return socialUser.getOpenid();
+    }
+
 }