Commit 910ffa7a5ac82cf6ba2b7e1f1219e95e69aaf95f
1 parent
c880dbfa
钱包充值退款
Showing
12 changed files
with
300 additions
and
15 deletions
sql/mysql/pay_wallet.sql
| ... | ... | @@ -10,6 +10,7 @@ CREATE TABLE `pay_wallet` |
| 10 | 10 | `balance` int NOT NULL DEFAULT 0 COMMENT '余额,单位分', |
| 11 | 11 | `total_expense` int NOT NULL DEFAULT 0 COMMENT '累计支出,单位分', |
| 12 | 12 | `total_recharge` int NOT NULL DEFAULT 0 COMMENT '累计充值,单位分', |
| 13 | + `freeze_price` int NOT NULL DEFAULT 0 COMMENT '冻结金额,单位分', | |
| 13 | 14 | `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', |
| 14 | 15 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| 15 | 16 | `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者', |
| ... | ... | @@ -62,6 +63,7 @@ CREATE TABLE `pay_wallet_recharge` |
| 62 | 63 | `refund_pay_price` int NOT NULL DEFAULT 0 COMMENT '退款支付金额', |
| 63 | 64 | `refund_bonus_price` int NOT NULL DEFAULT 0 COMMENT '退款钱包赠送金额', |
| 64 | 65 | `refund_time` datetime NULL COMMENT '退款时间', |
| 66 | + `refund_status` int NOT NULL DEFAULT 0 COMMENT '退款状态', | |
| 65 | 67 | `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者', |
| 66 | 68 | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
| 67 | 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 | 46 | ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易"); |
| 47 | 47 | ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对"); |
| 48 | 48 | ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款"); |
| 49 | + ErrorCode WALLET_FREEZE_PRICE_NOT_ENOUGH = new ErrorCode(1007007005, "钱包冻结余额不足"); | |
| 49 | 50 | |
| 50 | 51 | // TODO @jason:把钱包充值,单独搞个错误码段哈; |
| 51 | 52 | |
| ... | ... | @@ -54,7 +55,12 @@ public interface ErrorCodeConstants { |
| 54 | 55 | ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1007007007, "钱包充值更新支付状态失败,支付单编号不匹配"); |
| 55 | 56 | ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007007008, "钱包充值更新支付状态失败,支付单状态不是【支付成功】状态"); |
| 56 | 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 | 64 | // ========== 示例订单 1007900000 ========== |
| 59 | 65 | ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); |
| 60 | 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
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletDO.java
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 | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | 4 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 6 | +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; | |
| 6 | 7 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 7 | 8 | import com.baomidou.mybatisplus.annotation.TableId; |
| 8 | 9 | import com.baomidou.mybatisplus.annotation.TableName; |
| ... | ... | @@ -99,4 +100,11 @@ public class PayWalletRechargeDO extends BaseDO { |
| 99 | 100 | */ |
| 100 | 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<PayWalletDO> { |
| 56 | 56 | .eq(PayWalletDO::getId, id); |
| 57 | 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<PayWalletRechargeDO |
| 13 | 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 | 29 | */ |
| 30 | 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 | 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 | 5 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 6 | +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; | |
| 4 | 7 | import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; |
| 5 | 8 | import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletRechargeConvert; |
| 6 | 9 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 10 | +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; | |
| 7 | 11 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; |
| 8 | 12 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; |
| 9 | 13 | import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper; |
| 10 | 14 | import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; |
| 11 | 15 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 16 | +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; | |
| 12 | 17 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 18 | +import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; | |
| 13 | 19 | import lombok.extern.slf4j.Slf4j; |
| 14 | 20 | import org.springframework.stereotype.Service; |
| 15 | 21 | import org.springframework.transaction.annotation.Transactional; |
| ... | ... | @@ -24,6 +30,7 @@ import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.add |
| 24 | 30 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 25 | 31 | import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; |
| 26 | 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 | 36 | * 钱包充值 Service 实现类 |
| ... | ... | @@ -47,6 +54,8 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 47 | 54 | private PayWalletService payWalletService; |
| 48 | 55 | @Resource |
| 49 | 56 | private PayOrderService payOrderService; |
| 57 | + @Resource | |
| 58 | + private PayRefundService payRefundService; | |
| 50 | 59 | |
| 51 | 60 | @Override |
| 52 | 61 | @Transactional(rollbackFor = Exception.class) |
| ... | ... | @@ -98,6 +107,113 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 98 | 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 | 217 | private PayOrderDO validateWalletRechargerCanPaid(PayWalletRechargeDO walletRecharge, Long payOrderId) { |
| 102 | 218 | // 1.1 校验充值记录的支付状态 |
| 103 | 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 | 50 | /** |
| 51 | 51 | * 扣减钱包余额 |
| 52 | 52 | * |
| 53 | - * @param userId 用户 id | |
| 54 | - * @param userType 用户类型 | |
| 53 | + * @param walletId 钱包 id | |
| 55 | 54 | * @param bizId 业务关联 id |
| 56 | 55 | * @param bizType 业务关联分类 |
| 57 | 56 | * @param price 扣减金额 |
| 58 | 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 | 71 | PayWalletTransactionDO addWalletBalance(Long walletId, String bizId, |
| 73 | 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 | 68 | if (orderExtension == null) { |
| 69 | 69 | throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); |
| 70 | 70 | } |
| 71 | + PayWalletDO wallet = getOrCreateWallet(userId, userType); | |
| 71 | 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 | 76 | @Override |
| ... | ... | @@ -116,10 +117,14 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 116 | 117 | } |
| 117 | 118 | |
| 118 | 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 | 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 | 129 | // 2.1 扣除余额 |
| 125 | 130 | int updateCounts = 0 ; |
| ... | ... | @@ -129,9 +134,13 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 129 | 134 | break; |
| 130 | 135 | } |
| 131 | 136 | case RECHARGE_REFUND: { |
| 132 | - // TODO | |
| 137 | + updateCounts = walletMapper.updateWhenRechargeRefund(payWallet.getId(), price); | |
| 133 | 138 | break; |
| 134 | 139 | } |
| 140 | + default: { | |
| 141 | + // TODO 其它类型待实现 | |
| 142 | + throw new UnsupportedOperationException("待实现"); | |
| 143 | + } | |
| 135 | 144 | } |
| 136 | 145 | if (updateCounts == 0) { |
| 137 | 146 | throw exception(WALLET_BALANCE_NOT_ENOUGH); |
| ... | ... | @@ -163,7 +172,10 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 163 | 172 | walletMapper.updateWhenRecharge(payWallet.getId(), price); |
| 164 | 173 | break; |
| 165 | 174 | } |
| 166 | - // TODO 其它类型;这里可以先跑异常;避免有业务搞错; | |
| 175 | + default: { | |
| 176 | + // TODO 其它类型待实现 | |
| 177 | + throw new UnsupportedOperationException("待实现"); | |
| 178 | + } | |
| 167 | 179 | } |
| 168 | 180 | |
| 169 | 181 | // 2. 生成钱包流水 |
| ... | ... | @@ -173,4 +185,20 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 173 | 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 | } | ... | ... |