Commit 7237bc07bb7ab656c8ce5ac1de07053cf8517b27

Authored by jason
1 parent c7f6f921

钱包退款的实现

Showing 15 changed files with 202 additions and 29 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
@@ -27,6 +27,12 @@ public interface PayClientFactory { @@ -27,6 +27,12 @@ public interface PayClientFactory {
27 <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode, 27 <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
28 Config config); 28 Config config);
29 29
30 - <Config extends PayClientConfig> void createOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient); 30 + /**
  31 + * 新增或更新代理支付客户端
  32 + * @param channelId 渠道编号
  33 + * @param delegatePayClient 代理支付客户端
  34 + * @param <Config> 支付配置
  35 + */
  36 + <Config extends PayClientConfig> void addOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient);
31 37
32 } 38 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
@@ -53,7 +53,7 @@ public class PayClientFactoryImpl implements PayClientFactory { @@ -53,7 +53,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
53 } 53 }
54 54
55 @Override 55 @Override
56 - public <Config extends PayClientConfig> void createOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient) { 56 + public <Config extends PayClientConfig> void addOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient) {
57 clients.put(channelId, delegatePayClient); 57 clients.put(channelId, delegatePayClient);
58 } 58 }
59 59
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/delegate/DelegatePayClient.java
@@ -10,6 +10,8 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; @@ -10,6 +10,8 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
10 import java.util.Map; 10 import java.util.Map;
11 11
12 /** 12 /**
  13 + * 代理支付 Client 的抽象类。用于支付 Client 由其它模块实现, 例如钱包支付
  14 + *
13 * @author jason 15 * @author jason
14 */ 16 */
15 public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> { 17 public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> {
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
@@ -44,6 +44,9 @@ public interface ErrorCodeConstants { @@ -44,6 +44,9 @@ public interface ErrorCodeConstants {
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 ErrorCode WALLET_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足");
  47 + ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易");
  48 + ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对");
  49 + ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款");
47 50
48 // ========== 示例订单 1007900000 ========== 51 // ========== 示例订单 1007900000 ==========
49 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); 52 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
@@ -13,7 +13,8 @@ import lombok.Getter; @@ -13,7 +13,8 @@ import lombok.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 + PAYMENT(3, "支付"),
  17 + PAYMENT_REFUND(4, "支付退款");
17 18
18 // TODO 后续增加 19 // TODO 后续增加
19 /** 20 /**
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
@@ -37,6 +37,11 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; { @@ -37,6 +37,11 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; {
37 .eq(PayRefundDO::getNo, no)); 37 .eq(PayRefundDO::getNo, no));
38 } 38 }
39 39
  40 + default PayRefundDO selectByNo(String no){
  41 + return selectOne(new LambdaQueryWrapperX<PayRefundDO>()
  42 + .eq(PayRefundDO::getNo, no));
  43 + }
  44 +
40 default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) { 45 default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) {
41 return update(update, new LambdaQueryWrapper<PayRefundDO>() 46 return update(update, new LambdaQueryWrapper<PayRefundDO>()
42 .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status)); 47 .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status));
@@ -71,5 +76,4 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; { @@ -71,5 +76,4 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; {
71 default List<PayRefundDO> selectListByStatus(Integer status) { 76 default List<PayRefundDO> selectListByStatus(Integer status) {
72 return selectList(PayRefundDO::getStatus, status); 77 return selectList(PayRefundDO::getStatus, status);
73 } 78 }
74 -  
75 } 79 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java
@@ -26,6 +26,15 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac @@ -26,6 +26,15 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac
26 query.orderByDesc(PayWalletTransactionDO::getTransactionTime); 26 query.orderByDesc(PayWalletTransactionDO::getTransactionTime);
27 return selectPage(pageParam, query); 27 return selectPage(pageParam, query);
28 } 28 }
  29 +
  30 + default PayWalletTransactionDO selectByNo(String no) {
  31 + return selectOne(PayWalletTransactionDO::getNo, no);
  32 + }
  33 +
  34 + default PayWalletTransactionDO selectByWalletIdAndBiz(Long walletId, Long bizId, Integer bizType) {
  35 + return selectOne(PayWalletTransactionDO::getWalletId, walletId, PayWalletTransactionDO::getBizId,
  36 + bizId, PayWalletTransactionDO::getBizType, bizType);
  37 + }
29 } 38 }
30 39
31 40
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java
1 package cn.iocoder.yudao.module.pay.framework.pay.wallet; 1 package cn.iocoder.yudao.module.pay.framework.pay.wallet;
2 2
3 -import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; 3 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
8 import cn.iocoder.yudao.framework.pay.core.client.impl.delegate.DelegatePayClient; 9 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.dal.dataobject.wallet.PayWalletTransactionDO;
10 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; 11 import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
@@ -12,6 +13,8 @@ import lombok.extern.slf4j.Slf4j; @@ -12,6 +13,8 @@ import lombok.extern.slf4j.Slf4j;
12 13
13 import java.util.Map; 14 import java.util.Map;
14 15
  16 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
  17 +
15 /** 18 /**
16 * 钱包支付的 PayClient 实现类 19 * 钱包支付的 PayClient 实现类
17 * 20 *
@@ -19,7 +22,6 @@ import java.util.Map; @@ -19,7 +22,6 @@ import java.util.Map;
19 */ 22 */
20 @Slf4j 23 @Slf4j
21 public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> { 24 public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> {
22 -  
23 private PayWalletService payWalletService; 25 private PayWalletService payWalletService;
24 26
25 public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) { 27 public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) {
@@ -38,13 +40,24 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; { @@ -38,13 +40,24 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; {
38 40
39 @Override 41 @Override
40 protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { 42 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(), ""); 43 + try {
  44 + PayWalletTransactionDO payWalletTransaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());
  45 + return PayOrderRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreator(),
  46 + payWalletTransaction.getTransactionTime(),
  47 + reqDTO.getOutTradeNo(), "WALLET_PAY_SUCCESS");
  48 + } catch (Throwable ex) {
  49 + log.error("[doUnifiedOrder] 失败", ex);
  50 + String errorCode = String.valueOf(INTERNAL_SERVER_ERROR);
  51 + String errorMsg = INTERNAL_SERVER_ERROR.getMsg();
  52 + if (ex instanceof ServiceException) {
  53 + ServiceException serviceException = (ServiceException) ex;
  54 + errorCode = String.valueOf(serviceException.getCode());
  55 + errorMsg = serviceException.getMessage();
  56 + }
  57 + return PayOrderRespDTO.closedOf(errorCode, errorMsg, reqDTO.getOutTradeNo(), ex);
  58 + }
45 } 59 }
46 60
47 -  
48 @Override 61 @Override
49 protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) { 62 protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) {
50 throw new UnsupportedOperationException("钱包支付无支付回调"); 63 throw new UnsupportedOperationException("钱包支付无支付回调");
@@ -52,12 +65,27 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; { @@ -52,12 +65,27 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; {
52 65
53 @Override 66 @Override
54 protected PayOrderRespDTO doGetOrder(String outTradeNo) { 67 protected PayOrderRespDTO doGetOrder(String outTradeNo) {
55 - return null; 68 + throw new UnsupportedOperationException("待实现");
56 } 69 }
57 70
58 @Override 71 @Override
59 protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 72 protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
60 - return null; 73 + try {
  74 + PayWalletTransactionDO payWalletTransaction = payWalletService.refund(reqDTO.getOutRefundNo(),
  75 + reqDTO.getRefundPrice(), reqDTO.getReason());
  76 + return PayRefundRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getTransactionTime(),
  77 + reqDTO.getOutRefundNo(), "WALLET_REFUND_SUCCESS");
  78 + } catch (Throwable ex) {
  79 + log.error("[doUnifiedRefund] 失败", ex);
  80 + String errorCode = String.valueOf(INTERNAL_SERVER_ERROR);
  81 + String errorMsg = INTERNAL_SERVER_ERROR.getMsg();
  82 + if (ex instanceof ServiceException) {
  83 + ServiceException serviceException = (ServiceException) ex;
  84 + errorCode = String.valueOf(serviceException.getCode());
  85 + errorMsg = serviceException.getMessage();
  86 + }
  87 + return PayRefundRespDTO.failureOf(errorCode, errorMsg, reqDTO.getOutRefundNo(), ex);
  88 + }
61 } 89 }
62 90
63 @Override 91 @Override
@@ -67,6 +95,6 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; { @@ -67,6 +95,6 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; {
67 95
68 @Override 96 @Override
69 protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) { 97 protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
70 - return null; 98 + throw new UnsupportedOperationException("待实现");
71 } 99 }
72 } 100 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
@@ -81,6 +81,7 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -81,6 +81,7 @@ public class PayChannelServiceImpl implements PayChannelService {
81 log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); 81 log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
82 } 82 }
83 log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size()); 83 log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
  84 + // 钱包 client 需要和其它 client 分开了创建
84 List<PayChannelDO> walletChannels = new ArrayList<>(); 85 List<PayChannelDO> walletChannels = new ArrayList<>();
85 List<PayChannelDO> otherChannels = new ArrayList<>(); 86 List<PayChannelDO> otherChannels = new ArrayList<>();
86 channels.forEach(t -> { 87 channels.forEach(t -> {
@@ -97,7 +98,7 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -97,7 +98,7 @@ public class PayChannelServiceImpl implements PayChannelService {
97 walletChannels.forEach(payChannel -> { 98 walletChannels.forEach(payChannel -> {
98 WalletPayClient walletPayClient = new WalletPayClient(payChannel.getId(), payChannel.getCode(), 99 WalletPayClient walletPayClient = new WalletPayClient(payChannel.getId(), payChannel.getCode(),
99 (NonePayClientConfig) payChannel.getConfig(), payWalletService); 100 (NonePayClientConfig) payChannel.getConfig(), payWalletService);
100 - payClientFactory.createOrUpdateDelegatePayClient(payChannel.getId(), walletPayClient); 101 + payClientFactory.addOrUpdateDelegatePayClient(payChannel.getId(), walletPayClient);
101 }); 102 });
102 this.channelCache = channels; 103 this.channelCache = channels;
103 }); 104 });
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
@@ -25,6 +25,14 @@ public interface PayRefundService { @@ -25,6 +25,14 @@ public interface PayRefundService {
25 PayRefundDO getRefund(Long id); 25 PayRefundDO getRefund(Long id);
26 26
27 /** 27 /**
  28 + * 获得退款订单
  29 + *
  30 + * @param no 外部退款单号
  31 + * @return 退款订单
  32 + */
  33 + PayRefundDO getRefundByNo(String no);
  34 +
  35 + /**
28 * 获得指定应用的退款数量 36 * 获得指定应用的退款数量
29 * 37 *
30 * @param appId 应用编号 38 * @param appId 应用编号
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
@@ -75,6 +75,11 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -75,6 +75,11 @@ public class PayRefundServiceImpl implements PayRefundService {
75 } 75 }
76 76
77 @Override 77 @Override
  78 + public PayRefundDO getRefundByNo(String no) {
  79 + return refundMapper.selectByNo(no);
  80 + }
  81 +
  82 + @Override
78 public Long getRefundCountByAppId(Long appId) { 83 public Long getRefundCountByAppId(Long appId) {
79 return refundMapper.selectCountByAppId(appId); 84 return refundMapper.selectCountByAppId(appId);
80 } 85 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
@@ -17,5 +17,18 @@ public interface PayWalletService { @@ -17,5 +17,18 @@ public interface PayWalletService {
17 */ 17 */
18 PayWalletDO getPayWallet(Long userId, Integer userType); 18 PayWalletDO getPayWallet(Long userId, Integer userType);
19 19
  20 + /**
  21 + * 钱包支付
  22 + * @param outTradeNo 外部订单号
  23 + * @param price 金额
  24 + */
20 PayWalletTransactionDO pay(String outTradeNo, Integer price); 25 PayWalletTransactionDO pay(String outTradeNo, Integer price);
  26 +
  27 + /**
  28 + * 钱包支付退款
  29 + * @param outRefundNo 外部退款号
  30 + * @param refundPrice 退款金额
  31 + * @param reason 退款原因
  32 + */
  33 + PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason);
21 } 34 }
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.order.PayOrderExtensionDO;
  4 +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
4 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
6 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper; 7 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.dal.redis.no.PayNoRedisDAO;
8 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 9 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
  10 +import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
9 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.context.annotation.Lazy; 12 import org.springframework.context.annotation.Lazy;
11 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
12 import org.springframework.transaction.annotation.Transactional; 14 import org.springframework.transaction.annotation.Transactional;
13 15
14 import javax.annotation.Resource; 16 import javax.annotation.Resource;
15 -  
16 import java.time.LocalDateTime; 17 import java.time.LocalDateTime;
17 18
18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -20,6 +21,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLogi @@ -20,6 +21,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLogi
20 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; 21 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.ErrorCodeConstants.*;
22 import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT; 23 import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT;
  24 +import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT_REFUND;
23 25
24 /** 26 /**
25 * 钱包 Service 实现类 27 * 钱包 Service 实现类
@@ -29,16 +31,20 @@ import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT @@ -29,16 +31,20 @@ import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT
29 @Service 31 @Service
30 @Slf4j 32 @Slf4j
31 public class PayWalletServiceImpl implements PayWalletService { 33 public class PayWalletServiceImpl implements PayWalletService {
32 - private static final String WALLET_CHANNEL_NO_PREFIX = "W";  
33 - @Resource  
34 - @Lazy  
35 - private PayOrderService payOrderService; 34 + private static final String WALLET_PAY_NO_PREFIX = "WP";
  35 + private static final String WALLET_REFUND_NO_PREFIX = "WR";
36 @Resource 36 @Resource
37 private PayWalletMapper payWalletMapper; 37 private PayWalletMapper payWalletMapper;
38 @Resource 38 @Resource
39 private PayWalletTransactionService payWalletTransactionService; 39 private PayWalletTransactionService payWalletTransactionService;
40 @Resource 40 @Resource
41 private PayNoRedisDAO noRedisDAO; 41 private PayNoRedisDAO noRedisDAO;
  42 + @Resource
  43 + @Lazy
  44 + private PayOrderService payOrderService;
  45 + @Resource
  46 + @Lazy
  47 + private PayRefundService payRefundService;
42 48
43 @Override 49 @Override
44 public PayWalletDO getPayWallet(Long userId, Integer userType) { 50 public PayWalletDO getPayWallet(Long userId, Integer userType) {
@@ -53,13 +59,7 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -53,13 +59,7 @@ public class PayWalletServiceImpl implements PayWalletService {
53 if (orderExtension == null) { 59 if (orderExtension == null) {
54 throw exception(ORDER_EXTENSION_NOT_FOUND); 60 throw exception(ORDER_EXTENSION_NOT_FOUND);
55 } 61 }
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 - } 62 + PayWalletDO payWallet = validatePayWallet();
63 // 判断余额是否足够 63 // 判断余额是否足够
64 int afterBalance = payWallet.getBalance() - price; 64 int afterBalance = payWallet.getBalance() - price;
65 if(afterBalance < 0){ 65 if(afterBalance < 0){
@@ -70,12 +70,73 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -70,12 +70,73 @@ public class PayWalletServiceImpl implements PayWalletService {
70 payWalletMapper.updateById(payWallet); 70 payWalletMapper.updateById(payWallet);
71 71
72 // 生成钱包渠道流水号 72 // 生成钱包渠道流水号
73 - String walletNo = noRedisDAO.generate(WALLET_CHANNEL_NO_PREFIX); 73 + String walletNo = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
74 PayWalletTransactionDO payWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId()) 74 PayWalletTransactionDO payWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
75 - .setNo(walletNo).setAmount(price).setBalance(afterBalance).setTransactionTime(LocalDateTime.now()) 75 + .setNo(walletNo).setAmount(price * -1).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
76 .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getBizType()); 76 .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getBizType());
77 payWalletTransactionService.addPayWalletTransaction(payWalletTransaction); 77 payWalletTransactionService.addPayWalletTransaction(payWalletTransaction);
78 78
79 return payWalletTransaction; 79 return payWalletTransaction;
80 } 80 }
  81 +
  82 + private PayWalletDO validatePayWallet() {
  83 + Long userId = getLoginUserId();
  84 + Integer userType = getLoginUserType();
  85 + PayWalletDO payWallet = getPayWallet(userId, userType);
  86 + if (payWallet == null) {
  87 + log.error("[validatePayWallet] 用户 {} 钱包不存在", userId);
  88 + throw exception(WALLET_NOT_FOUND);
  89 + }
  90 + return payWallet;
  91 + }
  92 +
  93 + @Override
  94 + @Transactional(rollbackFor = Exception.class)
  95 + public PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason) {
  96 + // 判断退款单是否存在
  97 + PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo);
  98 + if (payRefund == null) {
  99 + throw exception(REFUND_NOT_FOUND);
  100 + }
  101 + PayWalletDO payWallet = validatePayWallet();
  102 + // 校验是否可以退款
  103 + validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), payWallet.getId(), refundPrice);
  104 +
  105 + Integer afterBalance = payWallet.getBalance() + refundPrice;
  106 + payWallet.setBalance(afterBalance);
  107 + payWallet.setTotalExpense(payWallet.getTotalExpense() + refundPrice * -1L);
  108 + payWalletMapper.updateById(payWallet);
  109 +
  110 + String walletNo = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
  111 + PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
  112 + .setNo(walletNo).setAmount(refundPrice).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
  113 + .setBizId(payRefund.getId()).setBizType(PAYMENT_REFUND.getBizType())
  114 + .setDescription(reason);
  115 + payWalletTransactionService.addPayWalletTransaction(newWalletTransaction);
  116 +
  117 + return newWalletTransaction;
  118 + }
  119 +
  120 + /**
  121 + * 校验是否能退款
  122 + * @param refundId 支付退款单 id
  123 + * @param walletPayNo 钱包支付 no
  124 + * @param walletId 钱包 id
  125 + */
  126 + private void validateWalletCanRefund(long refundId, String walletPayNo, long walletId, int refundPrice) {
  127 + // 查询钱包支付交易
  128 + PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getPayWalletTransactionByNo(walletPayNo);
  129 + if (payWalletTransaction == null) {
  130 + throw exception(WALLET_TRANSACTION_NOT_FOUND);
  131 + }
  132 + // 原来的支付金额
  133 + int amount = payWalletTransaction.getAmount() * -1;
  134 + if (refundPrice != amount) {
  135 + throw exception(WALLET_REFUND_AMOUNT_ERROR);
  136 + }
  137 + PayWalletTransactionDO refundTransaction = payWalletTransactionService.getPayWalletTransaction(walletId, refundId, PAYMENT_REFUND);
  138 + if (refundTransaction != null) {
  139 + throw exception(WALLET_REFUND_EXIST);
  140 + }
  141 + }
81 } 142 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.service.wallet; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.service.wallet;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransactionPageReqVO; 4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransactionPageReqVO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
  6 +import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum;
6 7
7 /** 8 /**
8 * 钱包余额明细 Service 接口 9 * 钱包余额明细 Service 接口
@@ -21,5 +22,25 @@ public interface PayWalletTransactionService { @@ -21,5 +22,25 @@ public interface PayWalletTransactionService {
21 PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType, 22 PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
22 AppPayWalletTransactionPageReqVO pageVO); 23 AppPayWalletTransactionPageReqVO pageVO);
23 24
  25 + /**
  26 + * 新增钱包余额明细
  27 + * @param payWalletTransaction 余额明细
  28 + * @return id
  29 + */
24 Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction); 30 Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction);
  31 +
  32 + /**
  33 + * 获取钱包余额明细 根据 no
  34 + * @param no 流水号
  35 + */
  36 + PayWalletTransactionDO getPayWalletTransactionByNo(String no);
  37 +
  38 + /**
  39 + * 获取钱包
  40 + * @param walletId 钱包 id
  41 + * @param bizId 业务编号
  42 + * @param typeEnum 业务类型
  43 + * @return 钱包余额明细
  44 + */
  45 + PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum);
25 } 46 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransact @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransact
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
7 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper; 7 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper;
  8 +import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum;
8 import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum; 9 import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum;
9 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
@@ -45,5 +46,15 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ @@ -45,5 +46,15 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
45 return payWalletTransaction.getId(); 46 return payWalletTransaction.getId();
46 } 47 }
47 48
  49 + @Override
  50 + public PayWalletTransactionDO getPayWalletTransactionByNo(String no) {
  51 + return payWalletTransactionMapper.selectByNo(no);
  52 + }
  53 +
  54 + @Override
  55 + public PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum) {
  56 + return payWalletTransactionMapper.selectByWalletIdAndBiz(walletId, bizId, typeEnum.getBizType());
  57 + }
  58 +
48 59
49 } 60 }