Commit c7f6f921598990cc0b3709e03e46628a9e8f7fbd

Authored by jason
1 parent 1baa7f4a

钱包支付 Client 的实现

Showing 15 changed files with 285 additions and 6 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
1 package cn.iocoder.yudao.framework.pay.core.client; 1 package cn.iocoder.yudao.framework.pay.core.client;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.impl.delegate.DelegatePayClient;
  4 +
3 /** 5 /**
4 * 支付客户端的工厂接口 6 * 支付客户端的工厂接口
5 * 7 *
@@ -25,4 +27,6 @@ public interface PayClientFactory { @@ -25,4 +27,6 @@ public interface PayClientFactory {
25 <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode, 27 <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
26 Config config); 28 Config config);
27 29
  30 + <Config extends PayClientConfig> void createOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient);
  31 +
28 } 32 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/NonePayClientConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl;
  2 +
  3 +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
  4 +import lombok.Data;
  5 +
  6 +import javax.validation.Validator;
  7 +
  8 +/**
  9 + * 无需任何配置 PayClientConfig 实现类
  10 + *
  11 + * @author jason
  12 + */
  13 +@Data
  14 +public class NonePayClientConfig implements PayClientConfig {
  15 +
  16 + /**
  17 + * 配置名称
  18 + * <p>
  19 + * 如果不加任何属性,JsonUtils.parseObject2 解析会报错,所以暂时加个名称
  20 + */
  21 + private String name;
  22 +
  23 + public NonePayClientConfig(){
  24 + this.name = "none-config";
  25 + }
  26 +
  27 + @Override
  28 + public void validate(Validator validator) {
  29 + // 无任何配置不需要校验
  30 + }
  31 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
6 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 6 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*; 7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*;
  8 +import cn.iocoder.yudao.framework.pay.core.client.impl.delegate.DelegatePayClient;
8 import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClient; 9 import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClient;
9 import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClientConfig; 10 import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClientConfig;
10 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.*; 11 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.*;
@@ -51,6 +52,11 @@ public class PayClientFactoryImpl implements PayClientFactory { @@ -51,6 +52,11 @@ public class PayClientFactoryImpl implements PayClientFactory {
51 } 52 }
52 } 53 }
53 54
  55 + @Override
  56 + public <Config extends PayClientConfig> void createOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient) {
  57 + clients.put(channelId, delegatePayClient);
  58 + }
  59 +
54 @SuppressWarnings("unchecked") 60 @SuppressWarnings("unchecked")
55 private <Config extends PayClientConfig> AbstractPayClient<Config> createPayClient( 61 private <Config extends PayClientConfig> AbstractPayClient<Config> createPayClient(
56 Long channelId, String channelCode, Config config) { 62 Long channelId, String channelCode, Config config) {
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/delegate/DelegatePayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.delegate;
  2 +
  3 +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
  9 +
  10 +import java.util.Map;
  11 +
  12 +/**
  13 + * @author jason
  14 + */
  15 +public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> {
  16 +
  17 + private final DelegatePayClient<Config> delegate;
  18 +
  19 + public DelegatePayClient(Long channelId, String channelCode, PayClientConfig config) {
  20 + super(channelId, channelCode, config);
  21 + delegate = this;
  22 + }
  23 +
  24 + @Override
  25 + protected void doInit() {
  26 + delegate.doInit();
  27 + }
  28 +
  29 + @Override
  30 + protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
  31 + return delegate.doUnifiedOrder(reqDTO);
  32 + }
  33 +
  34 + @Override
  35 + protected PayOrderRespDTO doGetOrder(String outTradeNo) {
  36 + return delegate.doGetOrder(outTradeNo);
  37 + }
  38 +
  39 + @Override
  40 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  41 + return delegate.doUnifiedRefund(reqDTO);
  42 + }
  43 +
  44 + @Override
  45 + protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
  46 + return delegate.doGetRefund(outTradeNo, outRefundNo);
  47 + }
  48 +
  49 + @Override
  50 + protected PayRefundRespDTO doParseRefundNotify(Map<String,String> params, String body) {
  51 + return delegate.doParseRefundNotify(params, body);
  52 + }
  53 +
  54 + @Override
  55 + protected PayOrderRespDTO doParseOrderNotify(Map<String,String> params, String body) {
  56 + return delegate.doParseOrderNotify(params, body);
  57 + }
  58 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/channel/PayChannelEnum.java
1 package cn.iocoder.yudao.framework.pay.core.enums.channel; 1 package cn.iocoder.yudao.framework.pay.core.enums.channel;
2 2
3 import cn.hutool.core.util.ArrayUtil; 3 import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
5 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; 6 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
6 import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClientConfig; 7 import cn.iocoder.yudao.framework.pay.core.client.impl.mock.MockPayClientConfig;
@@ -29,7 +30,9 @@ public enum PayChannelEnum { @@ -29,7 +30,9 @@ public enum PayChannelEnum {
29 ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class), 30 ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
30 ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class), 31 ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
31 32
32 - MOCK("mock", "模拟支付", MockPayClientConfig.class); 33 + MOCK("mock", "模拟支付", MockPayClientConfig.class),
  34 +
  35 + WALLET("wallet", "钱包支付", NonePayClientConfig.class);
33 36
34 /** 37 /**
35 * 编码 38 * 编码
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
@@ -43,6 +43,7 @@ public interface ErrorCodeConstants { @@ -43,6 +43,7 @@ public interface ErrorCodeConstants {
43 43
44 // ========== 钱包模块(退款) 1007007000 ========== 44 // ========== 钱包模块(退款) 1007007000 ==========
45 ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在"); 45 ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在");
  46 + ErrorCode WALLET_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足");
46 47
47 // ========== 示例订单 1007900000 ========== 48 // ========== 示例订单 1007900000 ==========
48 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); 49 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/WalletBizTypeEnum.java
@@ -12,7 +12,8 @@ import lombok.Getter; @@ -12,7 +12,8 @@ import lombok.Getter;
12 @Getter 12 @Getter
13 public enum WalletBizTypeEnum { 13 public enum WalletBizTypeEnum {
14 RECHARGE(1, "充值"), 14 RECHARGE(1, "充值"),
15 - RECHARGE_REFUND(2, "充值退款"); 15 + RECHARGE_REFUND(2, "充值退款"),
  16 + PAYMENT(3, "支付");
16 17
17 // TODO 后续增加 18 // TODO 后续增加
18 /** 19 /**
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.framework.pay.wallet;
  2 +
  3 +import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.impl.delegate.DelegatePayClient;
  9 +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
  10 +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +
  13 +import java.util.Map;
  14 +
  15 +/**
  16 + * 钱包支付的 PayClient 实现类
  17 + *
  18 + * @author jason
  19 + */
  20 +@Slf4j
  21 +public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> {
  22 +
  23 + private PayWalletService payWalletService;
  24 +
  25 + public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) {
  26 + super(channelId, channelCode, config);
  27 + }
  28 +
  29 + public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config, PayWalletService payWalletService) {
  30 + this(channelId, channelCode, config);
  31 + this.payWalletService = payWalletService;
  32 + }
  33 +
  34 + @Override
  35 + protected void doInit() {
  36 + // 钱包支付 无需初始化
  37 + }
  38 +
  39 + @Override
  40 + protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
  41 + PayWalletTransactionDO payWalletTransaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());
  42 + return PayOrderRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreator(),
  43 + payWalletTransaction.getTransactionTime(),
  44 + reqDTO.getOutTradeNo(), "");
  45 + }
  46 +
  47 +
  48 + @Override
  49 + protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) {
  50 + throw new UnsupportedOperationException("钱包支付无支付回调");
  51 + }
  52 +
  53 + @Override
  54 + protected PayOrderRespDTO doGetOrder(String outTradeNo) {
  55 + return null;
  56 + }
  57 +
  58 + @Override
  59 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  60 + return null;
  61 + }
  62 +
  63 + @Override
  64 + protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) {
  65 + throw new UnsupportedOperationException("钱包支付无退款回调");
  66 + }
  67 +
  68 + @Override
  69 + protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
  70 + return null;
  71 + }
  72 +}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  9 +import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
9 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 10 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
10 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 11 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
11 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 12 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
@@ -15,6 +16,8 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR @@ -15,6 +16,8 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR
15 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert; 16 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
16 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; 17 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
17 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper; 18 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
  19 +import cn.iocoder.yudao.module.pay.framework.pay.wallet.WalletPayClient;
  20 +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
18 import lombok.Getter; 21 import lombok.Getter;
19 import lombok.Setter; 22 import lombok.Setter;
20 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
@@ -26,6 +29,7 @@ import javax.annotation.PostConstruct; @@ -26,6 +29,7 @@ import javax.annotation.PostConstruct;
26 import javax.annotation.Resource; 29 import javax.annotation.Resource;
27 import javax.validation.Validator; 30 import javax.validation.Validator;
28 import java.time.LocalDateTime; 31 import java.time.LocalDateTime;
  32 +import java.util.ArrayList;
29 import java.util.Collection; 33 import java.util.Collection;
30 import java.util.Collections; 34 import java.util.Collections;
31 import java.util.List; 35 import java.util.List;
@@ -56,6 +60,8 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -56,6 +60,8 @@ public class PayChannelServiceImpl implements PayChannelService {
56 60
57 @Resource 61 @Resource
58 private Validator validator; 62 private Validator validator;
  63 + @Resource
  64 + private PayWalletService payWalletService;
59 65
60 /** 66 /**
61 * 初始化 {@link #payClientFactory} 缓存 67 * 初始化 {@link #payClientFactory} 缓存
@@ -75,10 +81,24 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -75,10 +81,24 @@ public class PayChannelServiceImpl implements PayChannelService {
75 log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); 81 log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
76 } 82 }
77 log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size()); 83 log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
78 - 84 + List<PayChannelDO> walletChannels = new ArrayList<>();
  85 + List<PayChannelDO> otherChannels = new ArrayList<>();
  86 + channels.forEach(t -> {
  87 + if (PayChannelEnum.WALLET.getCode().equals(t.getCode())) {
  88 + walletChannels.add(t);
  89 + } else {
  90 + otherChannels.add(t);
  91 + }
  92 + });
79 // 第二步:构建缓存:创建或更新支付 Client 93 // 第二步:构建缓存:创建或更新支付 Client
80 - channels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(), 94 + otherChannels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(),
81 payChannel.getCode(), payChannel.getConfig())); 95 payChannel.getCode(), payChannel.getConfig()));
  96 +
  97 + walletChannels.forEach(payChannel -> {
  98 + WalletPayClient walletPayClient = new WalletPayClient(payChannel.getId(), payChannel.getCode(),
  99 + (NonePayClientConfig) payChannel.getConfig(), payWalletService);
  100 + payClientFactory.createOrUpdateDelegatePayClient(payChannel.getId(), walletPayClient);
  101 + });
82 this.channelCache = channels; 102 this.channelCache = channels;
83 }); 103 });
84 } 104 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
@@ -107,6 +107,14 @@ public interface PayOrderService { @@ -107,6 +107,14 @@ public interface PayOrderService {
107 PayOrderExtensionDO getOrderExtension(Long id); 107 PayOrderExtensionDO getOrderExtension(Long id);
108 108
109 /** 109 /**
  110 + * 获得支付订单
  111 + *
  112 + * @param no 支付订单 no
  113 + * @return 支付订单
  114 + */
  115 + PayOrderExtensionDO getOrderExtensionByNo(String no);
  116 +
  117 + /**
110 * 同步订单的支付状态 118 * 同步订单的支付状态
111 * 119 *
112 * @param minCreateTime 最小创建时间 120 * @param minCreateTime 最小创建时间
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
@@ -416,6 +416,11 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -416,6 +416,11 @@ public class PayOrderServiceImpl implements PayOrderService {
416 } 416 }
417 417
418 @Override 418 @Override
  419 + public PayOrderExtensionDO getOrderExtensionByNo(String no) {
  420 + return orderExtensionMapper.selectByNo(no);
  421 + }
  422 +
  423 + @Override
419 public int syncOrder(LocalDateTime minCreateTime) { 424 public int syncOrder(LocalDateTime minCreateTime) {
420 // 1. 查询指定创建时间内的待支付订单 425 // 1. 查询指定创建时间内的待支付订单
421 List<PayOrderExtensionDO> orderExtensions = orderExtensionMapper.selectListByStatusAndCreateTimeGe( 426 List<PayOrderExtensionDO> orderExtensions = orderExtensionMapper.selectListByStatusAndCreateTimeGe(
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
1 package cn.iocoder.yudao.module.pay.service.wallet; 1 package cn.iocoder.yudao.module.pay.service.wallet;
2 2
3 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 3 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
  4 +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
4 5
5 /** 6 /**
6 * 钱包 Service 接口 7 * 钱包 Service 接口
@@ -15,4 +16,6 @@ public interface PayWalletService { @@ -15,4 +16,6 @@ public interface PayWalletService {
15 * @param userType 用户类型 16 * @param userType 用户类型
16 */ 17 */
17 PayWalletDO getPayWallet(Long userId, Integer userType); 18 PayWalletDO getPayWallet(Long userId, Integer userType);
  19 +
  20 + PayWalletTransactionDO pay(String outTradeNo, Integer price);
18 } 21 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
1 package cn.iocoder.yudao.module.pay.service.wallet; 1 package cn.iocoder.yudao.module.pay.service.wallet;
2 2
  3 +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
3 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
  5 +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
4 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper; 6 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper;
  7 +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
  8 +import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.context.annotation.Lazy;
5 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
  12 +import org.springframework.transaction.annotation.Transactional;
6 13
7 import javax.annotation.Resource; 14 import javax.annotation.Resource;
8 15
  16 +import java.time.LocalDateTime;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
  20 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType;
  21 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
  22 +import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT;
  23 +
9 /** 24 /**
10 * 钱包 Service 实现类 25 * 钱包 Service 实现类
11 * 26 *
12 * @author jason 27 * @author jason
13 */ 28 */
14 @Service 29 @Service
  30 +@Slf4j
15 public class PayWalletServiceImpl implements PayWalletService { 31 public class PayWalletServiceImpl implements PayWalletService {
16 - 32 + private static final String WALLET_CHANNEL_NO_PREFIX = "W";
  33 + @Resource
  34 + @Lazy
  35 + private PayOrderService payOrderService;
17 @Resource 36 @Resource
18 private PayWalletMapper payWalletMapper; 37 private PayWalletMapper payWalletMapper;
  38 + @Resource
  39 + private PayWalletTransactionService payWalletTransactionService;
  40 + @Resource
  41 + private PayNoRedisDAO noRedisDAO;
19 42
20 @Override 43 @Override
21 public PayWalletDO getPayWallet(Long userId, Integer userType) { 44 public PayWalletDO getPayWallet(Long userId, Integer userType) {
22 return payWalletMapper.selectByUserIdAndType(userId, userType); 45 return payWalletMapper.selectByUserIdAndType(userId, userType);
23 } 46 }
  47 +
  48 + @Override
  49 + @Transactional(rollbackFor = Exception.class)
  50 + public PayWalletTransactionDO pay(String outTradeNo, Integer price) {
  51 + // 判断支付交易拓展单是否存在
  52 + PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo);
  53 + if (orderExtension == null) {
  54 + throw exception(ORDER_EXTENSION_NOT_FOUND);
  55 + }
  56 + Long userId = getLoginUserId();
  57 + Integer userType = getLoginUserType();
  58 + PayWalletDO payWallet = getPayWallet(userId, userType);
  59 + if (payWallet == null) {
  60 + log.error("[pay] 用户 {} 钱包不存在", userId);
  61 + throw exception(WALLET_NOT_FOUND);
  62 + }
  63 + // 判断余额是否足够
  64 + int afterBalance = payWallet.getBalance() - price;
  65 + if(afterBalance < 0){
  66 + throw exception(WALLET_NOT_ENOUGH);
  67 + }
  68 + payWallet.setBalance(afterBalance);
  69 + payWallet.setTotalExpense(payWallet.getTotalExpense() + price);
  70 + payWalletMapper.updateById(payWallet);
  71 +
  72 + // 生成钱包渠道流水号
  73 + String walletNo = noRedisDAO.generate(WALLET_CHANNEL_NO_PREFIX);
  74 + PayWalletTransactionDO payWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
  75 + .setNo(walletNo).setAmount(price).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
  76 + .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getBizType());
  77 + payWalletTransactionService.addPayWalletTransaction(payWalletTransaction);
  78 +
  79 + return payWalletTransaction;
  80 + }
24 } 81 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java
@@ -20,4 +20,6 @@ public interface PayWalletTransactionService { @@ -20,4 +20,6 @@ public interface PayWalletTransactionService {
20 */ 20 */
21 PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType, 21 PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
22 AppPayWalletTransactionPageReqVO pageVO); 22 AppPayWalletTransactionPageReqVO pageVO);
  23 +
  24 + Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction);
23 } 25 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java
@@ -21,7 +21,7 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.WALLET_NOT_FO @@ -21,7 +21,7 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.WALLET_NOT_FO
21 */ 21 */
22 @Service 22 @Service
23 @Slf4j 23 @Slf4j
24 -public class PayWalletTransactionServiceImpl implements PayWalletTransactionService{ 24 +public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
25 @Resource 25 @Resource
26 private PayWalletService payWalletService; 26 private PayWalletService payWalletService;
27 @Resource 27 @Resource
@@ -38,4 +38,12 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionSer @@ -38,4 +38,12 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionSer
38 return payWalletTransactionMapper.selectPageByWalletIdAndQueryType(payWallet.getId(), 38 return payWalletTransactionMapper.selectPageByWalletIdAndQueryType(payWallet.getId(),
39 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()), pageVO); 39 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()), pageVO);
40 } 40 }
  41 +
  42 + @Override
  43 + public Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
  44 + payWalletTransactionMapper.insert(payWalletTransaction);
  45 + return payWalletTransaction.getId();
  46 + }
  47 +
  48 +
41 } 49 }