diff --git a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java index c9e20cbe0..aae6c9d33 100644 --- a/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java +++ b/yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.trade.enums; // TODO @芋艿:枚举 + /** * 通知模板枚举类 * @@ -17,4 +18,15 @@ public interface MessageTemplateConstants { String TRADE_AFTER_SALE_CHANGE = "售后进度通知"; + /** + * 售后进度通知相关参数枚举 + * + * @author HUIHUI + */ + class TradeAfterSaleChangeReqParams { + + public static final String ORDER_DELIVERY = "order_delivery"; + + } + } diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java index f6b727aef..8863de335 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/MessageTemplateConstants.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.pay.enums; // TODO @芋艿:枚举 + /** * 通知模板枚举类 * @@ -12,4 +13,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"; // 充值状态 + + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/package-info.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/package-info.java new file mode 100644 index 000000000..2e26571ef --- /dev/null +++ b/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; \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/subscribe/SubscribeMessageClient.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/message/subscribe/SubscribeMessageClient.java new file mode 100644 index 000000000..cdb42a519 --- /dev/null +++ b/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 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 messages, Integer userType, Long userId, + String path) { + socialClientApi.sendSubscribeMessage(templateTitle, messages, userType, userId, SocialTypeEnum.WECHAT_MINI_APP.getType(), path); + } + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java index b26318922..d801aa262 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java +++ b/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 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 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 diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java index a75c398a7..e91b136d1 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java +++ b/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 messages, Integer userType, Long userId, + Integer socialType, String path); + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxSubscribeMessageSendReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxSubscribeMessageSendReqDTO.java index 069864272..d6e7b9a78 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxSubscribeMessageSendReqDTO.java +++ b/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 { */ private Map messages; - public SocialWxSubscribeMessageSendReqDTO addData(String key, String value) { - if (messages == null) { - messages = new HashMap<>(); - } - messages.put(key, value); - return this; - } - } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java index 77d676d96..2885f19eb 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java +++ b/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 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 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(); + } + }