Commit 910ffa7a5ac82cf6ba2b7e1f1219e95e69aaf95f

Authored by jason
1 parent c880dbfa

钱包充值退款

sql/mysql/pay_wallet.sql
@@ -10,6 +10,7 @@ CREATE TABLE `pay_wallet` @@ -10,6 +10,7 @@ CREATE TABLE `pay_wallet`
10 `balance` int NOT NULL DEFAULT 0 COMMENT '余额,单位分', 10 `balance` int NOT NULL DEFAULT 0 COMMENT '余额,单位分',
11 `total_expense` int NOT NULL DEFAULT 0 COMMENT '累计支出,单位分', 11 `total_expense` int NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
12 `total_recharge` int NOT NULL DEFAULT 0 COMMENT '累计充值,单位分', 12 `total_recharge` int NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
  13 + `freeze_price` int NOT NULL DEFAULT 0 COMMENT '冻结金额,单位分',
13 `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', 14 `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
14 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 15 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
15 `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', 16 `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
@@ -62,6 +63,7 @@ CREATE TABLE `pay_wallet_recharge` @@ -62,6 +63,7 @@ CREATE TABLE `pay_wallet_recharge`
62 `refund_pay_price` int NOT NULL DEFAULT 0 COMMENT '退款支付金额', 63 `refund_pay_price` int NOT NULL DEFAULT 0 COMMENT '退款支付金额',
63 `refund_bonus_price` int NOT NULL DEFAULT 0 COMMENT '退款钱包赠送金额', 64 `refund_bonus_price` int NOT NULL DEFAULT 0 COMMENT '退款钱包赠送金额',
64 `refund_time` datetime NULL COMMENT '退款时间', 65 `refund_time` datetime NULL COMMENT '退款时间',
  66 + `refund_status` int NOT NULL DEFAULT 0 COMMENT '退款状态',
65 `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', 67 `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
66 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', 68 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
67 `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', 69 `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
@@ -46,6 +46,7 @@ public interface ErrorCodeConstants { @@ -46,6 +46,7 @@ public interface ErrorCodeConstants {
46 ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易"); 46 ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易");
47 ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对"); 47 ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对");
48 ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款"); 48 ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款");
  49 + ErrorCode WALLET_FREEZE_PRICE_NOT_ENOUGH = new ErrorCode(1007007005, "钱包冻结余额不足");
49 50
50 // TODO @jason:把钱包充值,单独搞个错误码段哈; 51 // TODO @jason:把钱包充值,单独搞个错误码段哈;
51 52
@@ -54,7 +55,12 @@ public interface ErrorCodeConstants { @@ -54,7 +55,12 @@ public interface ErrorCodeConstants {
54 ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1007007007, "钱包充值更新支付状态失败,支付单编号不匹配"); 55 ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1007007007, "钱包充值更新支付状态失败,支付单编号不匹配");
55 ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007007008, "钱包充值更新支付状态失败,支付单状态不是【支付成功】状态"); 56 ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007007008, "钱包充值更新支付状态失败,支付单状态不是【支付成功】状态");
56 ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1007007009, "钱包充值更新支付状态失败,支付单金额不匹配"); 57 ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1007007009, "钱包充值更新支付状态失败,支付单金额不匹配");
57 - 58 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_NOT_PAID = new ErrorCode(1007900010, "钱包发起退款失败,钱包充值订单未支付");
  59 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUNDED = new ErrorCode(1007900011, "钱包发起退款失败,钱包充值订单已退款");
  60 + ErrorCode WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH = new ErrorCode(1007900012, "钱包发起退款失败,钱包余额不足");
  61 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007900013, "钱包退款更新失败,钱包退款单编号不匹配");
  62 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007900014, "钱包退款更新失败,退款订单不存在");
  63 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007900015, "钱包退款更新失败,退款单金额不匹配");
58 // ========== 示例订单 1007900000 ========== 64 // ========== 示例订单 1007900000 ==========
59 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); 65 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");
60 ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态"); 66 ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态");
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletRechargeController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.controller.admin.wallet;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  5 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
  6 +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletRechargeService;
  7 +import io.swagger.v3.oas.annotations.Operation;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.tags.Tag;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.validation.annotation.Validated;
  12 +import org.springframework.web.bind.annotation.*;
  13 +
  14 +import javax.annotation.Resource;
  15 +import javax.annotation.security.PermitAll;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  18 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
  19 +
  20 +@Tag(name = "管理后台 - 钱包充值")
  21 +@RestController
  22 +@RequestMapping("/pay/wallet-recharge")
  23 +@Validated
  24 +@Slf4j
  25 +public class PayWalletRechargeController {
  26 +
  27 + @Resource
  28 + private PayWalletRechargeService walletRechargeService;
  29 +
  30 +
  31 + @GetMapping("/refund")
  32 + @Operation(summary = "发起钱包充值退款")
  33 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  34 + public CommonResult<Boolean> refundWalletRecharge(@RequestParam("id") Long id) {
  35 + walletRechargeService.refundWalletRecharge(id,getClientIP());
  36 + return success(true);
  37 + }
  38 +
  39 + @PostMapping("/update-refunded")
  40 + @Operation(summary = "更新钱包充值为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
  41 + @PermitAll // 无需登录, 内部校验实现
  42 + @OperateLog(enable = false) // 禁用操作日志,因为没有操作人
  43 + public CommonResult<Boolean> updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
  44 + walletRechargeService.updateWalletRechargeRefunded(
  45 + Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId());
  46 + return success(true);
  47 + }
  48 +}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletRechargeController.java
@@ -53,7 +53,4 @@ public class AppPayWalletRechargeController { @@ -53,7 +53,4 @@ public class AppPayWalletRechargeController {
53 notifyReqDTO.getPayOrderId()); 53 notifyReqDTO.getPayOrderId());
54 return success(true); 54 return success(true);
55 } 55 }
56 -  
57 - // TODO @jason:管理后台,是不是可以搞个发起退款;  
58 -  
59 } 56 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletDO.java
@@ -43,6 +43,11 @@ public class PayWalletDO extends BaseDO { @@ -43,6 +43,11 @@ public class PayWalletDO extends BaseDO {
43 private Integer balance; 43 private Integer balance;
44 44
45 /** 45 /**
  46 + * 冻结金额,单位分
  47 + */
  48 + private Integer freezePrice;
  49 +
  50 + /**
46 * 累计支出,单位分 51 * 累计支出,单位分
47 */ 52 */
48 private Integer totalExpense; 53 private Integer totalExpense;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletRechargeDO.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.dal.dataobject.wallet; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.dal.dataobject.wallet;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
  6 +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
6 import com.baomidou.mybatisplus.annotation.KeySequence; 7 import com.baomidou.mybatisplus.annotation.KeySequence;
7 import com.baomidou.mybatisplus.annotation.TableId; 8 import com.baomidou.mybatisplus.annotation.TableId;
8 import com.baomidou.mybatisplus.annotation.TableName; 9 import com.baomidou.mybatisplus.annotation.TableName;
@@ -99,4 +100,11 @@ public class PayWalletRechargeDO extends BaseDO { @@ -99,4 +100,11 @@ public class PayWalletRechargeDO extends BaseDO {
99 */ 100 */
100 private LocalDateTime refundTime; 101 private LocalDateTime refundTime;
101 102
  103 + /**
  104 + * 退款状态
  105 + *
  106 + * 枚举 {@link PayRefundStatusEnum}
  107 + */
  108 + private Integer refundStatus;
  109 +
102 } 110 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java
@@ -56,7 +56,51 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; { @@ -56,7 +56,51 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; {
56 .eq(PayWalletDO::getId, id); 56 .eq(PayWalletDO::getId, id);
57 return update(null, lambdaUpdateWrapper); 57 return update(null, lambdaUpdateWrapper);
58 } 58 }
59 - 59 +
  60 + /**
  61 + * 冻结钱包部分余额
  62 + * @param id 钱包 id
  63 + * @param price 冻结金额
  64 + */
  65 + default int freezePrice(Long id, Integer price){
  66 + LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
  67 + .setSql(" balance = balance - " + price
  68 + + ", freeze_price = freeze_price + " + price)
  69 + .eq(PayWalletDO::getId, id)
  70 + .ge(PayWalletDO::getBalance, price); // cas 逻辑
  71 + return update(null, lambdaUpdateWrapper);
  72 + }
  73 +
  74 + /**
  75 + * 解冻钱包余额
  76 + * @param id 钱包 id
  77 + * @param price 解冻金额
  78 + */
  79 + default int unFreezePrice(Long id, Integer price){
  80 + LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
  81 + .setSql(" balance = balance + " + price
  82 + + ", freeze_price = freeze_price - " + price)
  83 + .eq(PayWalletDO::getId, id)
  84 + .ge(PayWalletDO::getFreezePrice, price); // cas 逻辑
  85 + return update(null, lambdaUpdateWrapper);
  86 + }
  87 +
  88 + /**
  89 + * 当充值退款时, 更新钱包
  90 + * @param id 钱包 id
  91 + * @param price 退款金额
  92 + */
  93 + default int updateWhenRechargeRefund(Long id, Integer price){
  94 + LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
  95 + .setSql(" freeze_price = freeze_price - " + price
  96 + + ", total_recharge = total_recharge - " + price)
  97 + .eq(PayWalletDO::getId, id)
  98 + .ge(PayWalletDO::getFreezePrice, price)
  99 + .ge(PayWalletDO::getTotalRecharge, price);// cas 逻辑
  100 + return update(null, lambdaUpdateWrapper);
  101 + }
  102 +
  103 +
60 } 104 }
61 105
62 106
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletRechargeMapper.java
@@ -13,6 +13,11 @@ public interface PayWalletRechargeMapper extends BaseMapperX&lt;PayWalletRechargeDO @@ -13,6 +13,11 @@ public interface PayWalletRechargeMapper extends BaseMapperX&lt;PayWalletRechargeDO
13 .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getPayStatus, wherePayStatus)); 13 .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getPayStatus, wherePayStatus));
14 } 14 }
15 15
  16 + default int updateByIdAndRefunded(Long id, Integer whereRefundStatus, PayWalletRechargeDO updateObj){
  17 + return update(updateObj, new LambdaQueryWrapperX<PayWalletRechargeDO>()
  18 + .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getRefundStatus, whereRefundStatus));
  19 + }
  20 +
16 } 21 }
17 22
18 23
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java
@@ -29,4 +29,17 @@ public interface PayWalletRechargeService { @@ -29,4 +29,17 @@ public interface PayWalletRechargeService {
29 */ 29 */
30 void updateWalletRechargerPaid(Long id, Long payOrderId); 30 void updateWalletRechargerPaid(Long id, Long payOrderId);
31 31
  32 + /**
  33 + * 发起钱包充值退款
  34 + * @param id 钱包充值编号
  35 + * @param userIp 用户 ip 地址
  36 + */
  37 + void refundWalletRecharge(Long id, String userIp);
  38 +
  39 + /**
  40 + * 更新钱包充值记录为已退款
  41 + * @param id 钱包充值 id
  42 + * @param payRefundId 退款单id
  43 + */
  44 + void updateWalletRechargeRefunded(Long id, Long payRefundId);
32 } 45 }
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.lang.Assert;
  4 +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
3 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 5 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
  6 +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; 7 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO;
5 import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert; 8 import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 9 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  10 +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
7 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 11 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
8 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 12 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
9 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper; 13 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper;
10 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; 14 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
11 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 15 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
  16 +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
12 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 17 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
  18 +import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
13 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
14 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
15 import org.springframework.transaction.annotation.Transactional; 21 import org.springframework.transaction.annotation.Transactional;
@@ -24,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.add @@ -24,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.add
24 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 30 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
25 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; 31 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
26 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; 32 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
  33 +import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
27 34
28 /** 35 /**
29 * 钱包充值 Service 实现类 36 * 钱包充值 Service 实现类
@@ -47,6 +54,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -47,6 +54,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
47 private PayWalletService payWalletService; 54 private PayWalletService payWalletService;
48 @Resource 55 @Resource
49 private PayOrderService payOrderService; 56 private PayOrderService payOrderService;
  57 + @Resource
  58 + private PayRefundService payRefundService;
50 59
51 @Override 60 @Override
52 @Transactional(rollbackFor = Exception.class) 61 @Transactional(rollbackFor = Exception.class)
@@ -98,6 +107,113 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -98,6 +107,113 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
98 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice()); 107 PayWalletBizTypeEnum.RECHARGE, walletRecharge.getTotalPrice());
99 } 108 }
100 109
  110 + @Override
  111 + @Transactional(rollbackFor = Exception.class)
  112 + public void refundWalletRecharge(Long id, String userIp) {
  113 + // 1.1 获取钱包充值记录
  114 + PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id);
  115 + if (walletRecharge == null) {
  116 + log.error("[refundWalletRecharge][钱包充值记录不存在,钱包充值记录 id({})]", id);
  117 + throw exception(WALLET_RECHARGE_NOT_FOUND);
  118 + }
  119 + // 1.2 校验钱包充值是否可以发起退款
  120 + PayWalletDO wallet = validateWalletRechargeCanRefund(walletRecharge);
  121 + // 2 冻结退款的余额, 暂时只处理赠送的余额也全部退回
  122 + payWalletService.freezePrice(wallet.getId(), walletRecharge.getTotalPrice());
  123 + // 3 创建退款单
  124 + String walletRechargeId = String.valueOf(id);
  125 + String refundId = walletRechargeId + "-refund";
  126 + Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO()
  127 + .setAppId(WALLET_PAY_APP_ID).setUserIp(userIp)
  128 + .setMerchantOrderId(walletRechargeId)
  129 + .setMerchantRefundId(refundId)
  130 + .setReason("想退钱").setPrice(walletRecharge.getPayPrice()));
  131 + // 4 更新充值记录退款单号
  132 + walletRechargeMapper.updateById(new PayWalletRechargeDO().setPayRefundId(payRefundId)
  133 + .setRefundStatus(WAITING.getStatus()).setId(walletRecharge.getId()));
  134 + }
  135 +
  136 + @Override
  137 + @Transactional(rollbackFor = Exception.class)
  138 + public void updateWalletRechargeRefunded(Long id, Long payRefundId) {
  139 + // 1.1 获取钱包充值记录
  140 + PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id);
  141 + if (walletRecharge == null) {
  142 + log.error("[updateWalletRechargerPaid][钱包充值记录不存在,钱包充值记录 id({})]", id);
  143 + throw exception(WALLET_RECHARGE_NOT_FOUND);
  144 + }
  145 + // 1.2 校验钱包充值是否可以更新已退款
  146 + PayRefundDO payRefund = validateWalletRechargeCanRefunded(walletRecharge, payRefundId);
  147 +
  148 + PayWalletRechargeDO updateObj = new PayWalletRechargeDO().setId(id);
  149 + // 退款成功
  150 + if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
  151 + // 2.1 更新钱包余额
  152 + payWalletService.reduceWalletBalance(walletRecharge.getWalletId(), id,
  153 + PayWalletBizTypeEnum.RECHARGE_REFUND, walletRecharge.getTotalPrice());
  154 +
  155 + updateObj.setRefundStatus(SUCCESS.getStatus()).setRefundTime(payRefund.getSuccessTime())
  156 + .setRefundTotalPrice(walletRecharge.getTotalPrice()).setRefundPayPrice(walletRecharge.getPayPrice())
  157 + .setRefundBonusPrice(walletRecharge.getBonusPrice());
  158 + }
  159 + // 退款失败
  160 + if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) {
  161 + // 2.2 解冻余额
  162 + payWalletService.unFreezePrice(walletRecharge.getWalletId(), walletRecharge.getTotalPrice());
  163 +
  164 + updateObj.setRefundStatus(FAILURE.getStatus());
  165 + }
  166 + // 3. 更新钱包充值的退款字段
  167 + walletRechargeMapper.updateByIdAndRefunded(id, WAITING.getStatus(), updateObj);
  168 + }
  169 +
  170 + private PayRefundDO validateWalletRechargeCanRefunded(PayWalletRechargeDO walletRecharge, Long payRefundId) {
  171 + // 1. 校验退款订单匹配
  172 + if (notEqual(walletRecharge.getPayRefundId(), payRefundId)) {
  173 + log.error("[validateWalletRechargeCanRefunded][钱包充值({}) 退款单不匹配({}),请进行处理!钱包充值的数据是:{}]",
  174 + walletRecharge.getId(), payRefundId, toJsonString(walletRecharge));
  175 + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
  176 + }
  177 +
  178 + // 2.1 校验退款订单
  179 + PayRefundDO payRefund = payRefundService.getRefund(payRefundId);
  180 + if (payRefund == null) {
  181 + log.error("[validateWalletRechargeCanRefunded][payRefund({})不存在]", payRefundId);
  182 + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND);
  183 + }
  184 + // 2.2 校验退款金额一致
  185 + if (notEqual(payRefund.getRefundPrice(), walletRecharge.getPayPrice())) {
  186 + log.error("[validateWalletRechargeCanRefunded][钱包({}) payRefund({}) 退款金额不匹配,请进行处理!钱包数据是:{},payRefund 数据是:{}]",
  187 + walletRecharge.getId(), payRefundId, toJsonString(walletRecharge), toJsonString(payRefund));
  188 + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH);
  189 + }
  190 + // 2.3 校验退款订单商户订单是否匹配
  191 + if (notEqual(payRefund.getMerchantOrderId(), walletRecharge.getId().toString())) {
  192 + log.error("[validateWalletRechargeCanRefunded][钱包({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]",
  193 + walletRecharge.getId(), payRefundId, toJsonString(payRefund));
  194 + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
  195 + }
  196 + return payRefund;
  197 + }
  198 +
  199 + private PayWalletDO validateWalletRechargeCanRefund(PayWalletRechargeDO walletRecharge) {
  200 + // 校验充值订单是否支付
  201 + if (!walletRecharge.getPayStatus()) {
  202 + throw exception(WALLET_RECHARGE_REFUND_FAIL_NOT_PAID);
  203 + }
  204 + // 校验充值订单是否已退款
  205 + if (walletRecharge.getPayRefundId() != null) {
  206 + throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUNDED);
  207 + }
  208 + // 校验钱包余额是否足够
  209 + PayWalletDO wallet = payWalletService.getWallet(walletRecharge.getWalletId());
  210 + Assert.notNull(wallet, "用户钱包({}) 不存在", wallet.getId());
  211 + if (wallet.getBalance() < walletRecharge.getTotalPrice()) {
  212 + throw exception(WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH);
  213 + }
  214 + return wallet;
  215 + }
  216 +
101 private PayOrderDO validateWalletRechargerCanPaid(PayWalletRechargeDO walletRecharge, Long payOrderId) { 217 private PayOrderDO validateWalletRechargerCanPaid(PayWalletRechargeDO walletRecharge, Long payOrderId) {
102 // 1.1 校验充值记录的支付状态 218 // 1.1 校验充值记录的支付状态
103 if (walletRecharge.getPayStatus()) { 219 if (walletRecharge.getPayStatus()) {
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
@@ -50,15 +50,14 @@ public interface PayWalletService { @@ -50,15 +50,14 @@ public interface PayWalletService {
50 /** 50 /**
51 * 扣减钱包余额 51 * 扣减钱包余额
52 * 52 *
53 - * @param userId 用户 id  
54 - * @param userType 用户类型 53 + * @param walletId 钱包 id
55 * @param bizId 业务关联 id 54 * @param bizId 业务关联 id
56 * @param bizType 业务关联分类 55 * @param bizType 业务关联分类
57 * @param price 扣减金额 56 * @param price 扣减金额
58 * @return 钱包流水 57 * @return 钱包流水
59 */ 58 */
60 - PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,  
61 - Long bizId, PayWalletBizTypeEnum bizType, Integer price); 59 + PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId,
  60 + PayWalletBizTypeEnum bizType, Integer price);
62 61
63 /** 62 /**
64 * 增加钱包余额 63 * 增加钱包余额
@@ -72,4 +71,18 @@ public interface PayWalletService { @@ -72,4 +71,18 @@ public interface PayWalletService {
72 PayWalletTransactionDO addWalletBalance(Long walletId, String bizId, 71 PayWalletTransactionDO addWalletBalance(Long walletId, String bizId,
73 PayWalletBizTypeEnum bizType, Integer price); 72 PayWalletBizTypeEnum bizType, Integer price);
74 73
  74 + /**
  75 + * 冻结钱包部分余额
  76 + *
  77 + * @param id 钱包编号
  78 + * @param price 冻结金额
  79 + */
  80 + void freezePrice(Long id, Integer price);
  81 +
  82 + /**
  83 + * 解冻钱包余额
  84 + * @param id 钱包编号
  85 + * @param price 解冻金额
  86 + */
  87 + void unFreezePrice(Long id, Integer price);
75 } 88 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
@@ -68,8 +68,9 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -68,8 +68,9 @@ public class PayWalletServiceImpl implements PayWalletService {
68 if (orderExtension == null) { 68 if (orderExtension == null) {
69 throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); 69 throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
70 } 70 }
  71 + PayWalletDO wallet = getOrCreateWallet(userId, userType);
71 // 2. 扣减余额 72 // 2. 扣减余额
72 - return reduceWalletBalance(userId, userType, orderExtension.getOrderId(), PAYMENT, price); 73 + return reduceWalletBalance(wallet.getId(), orderExtension.getOrderId(), PAYMENT, price);
73 } 74 }
74 75
75 @Override 76 @Override
@@ -116,10 +117,14 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -116,10 +117,14 @@ public class PayWalletServiceImpl implements PayWalletService {
116 } 117 }
117 118
118 @Override 119 @Override
119 - public PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,  
120 - Long bizId, PayWalletBizTypeEnum bizType, Integer price) { 120 + public PayWalletTransactionDO reduceWalletBalance(Long walletId, Long bizId,
  121 + PayWalletBizTypeEnum bizType, Integer price) {
121 // 1. 获取钱包 122 // 1. 获取钱包
122 - PayWalletDO payWallet = getOrCreateWallet(userId, userType); 123 + PayWalletDO payWallet = getWallet(walletId);
  124 + if (payWallet == null) {
  125 + log.error("[reduceWalletBalance],用户钱包({})不存在.", walletId);
  126 + throw exception(WALLET_NOT_FOUND);
  127 + }
123 128
124 // 2.1 扣除余额 129 // 2.1 扣除余额
125 int updateCounts = 0 ; 130 int updateCounts = 0 ;
@@ -129,9 +134,13 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -129,9 +134,13 @@ public class PayWalletServiceImpl implements PayWalletService {
129 break; 134 break;
130 } 135 }
131 case RECHARGE_REFUND: { 136 case RECHARGE_REFUND: {
132 - // TODO 137 + updateCounts = walletMapper.updateWhenRechargeRefund(payWallet.getId(), price);
133 break; 138 break;
134 } 139 }
  140 + default: {
  141 + // TODO 其它类型待实现
  142 + throw new UnsupportedOperationException("待实现");
  143 + }
135 } 144 }
136 if (updateCounts == 0) { 145 if (updateCounts == 0) {
137 throw exception(WALLET_BALANCE_NOT_ENOUGH); 146 throw exception(WALLET_BALANCE_NOT_ENOUGH);
@@ -163,7 +172,10 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -163,7 +172,10 @@ public class PayWalletServiceImpl implements PayWalletService {
163 walletMapper.updateWhenRecharge(payWallet.getId(), price); 172 walletMapper.updateWhenRecharge(payWallet.getId(), price);
164 break; 173 break;
165 } 174 }
166 - // TODO 其它类型;这里可以先跑异常;避免有业务搞错; 175 + default: {
  176 + // TODO 其它类型待实现
  177 + throw new UnsupportedOperationException("待实现");
  178 + }
167 } 179 }
168 180
169 // 2. 生成钱包流水 181 // 2. 生成钱包流水
@@ -173,4 +185,20 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -173,4 +185,20 @@ public class PayWalletServiceImpl implements PayWalletService {
173 return walletTransactionService.createWalletTransaction(transactionCreateReqBO); 185 return walletTransactionService.createWalletTransaction(transactionCreateReqBO);
174 } 186 }
175 187
  188 + @Override
  189 + public void freezePrice(Long id, Integer price) {
  190 + int updateCounts = walletMapper.freezePrice(id, price);
  191 + if (updateCounts == 0) {
  192 + throw exception(WALLET_BALANCE_NOT_ENOUGH);
  193 + }
  194 + }
  195 +
  196 + @Override
  197 + public void unFreezePrice(Long id, Integer price) {
  198 + int updateCounts = walletMapper.unFreezePrice(id, price);
  199 + if (updateCounts == 0) {
  200 + throw exception(WALLET_FREEZE_PRICE_NOT_ENOUGH);
  201 + }
  202 + }
  203 +
176 } 204 }