Procházet zdrojové kódy

钱包 review 修改

jason před 1 rokem
rodič
revize
91b03e7c93

+ 9 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java

@@ -5,6 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
+import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
@@ -23,9 +25,7 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -70,6 +70,12 @@ public class PayOrderController {
     @PostMapping("/submit")
     @Operation(summary = "提交支付订单")
     public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
+        if (Objects.equals(reqVO.getChannelCode(), PayChannelEnum.WALLET.getCode())) {
+            Map<String, String> channelExtras = reqVO.getChannelExtras() == null ? new HashMap<>(8) : reqVO.getChannelExtras();
+            channelExtras.put("user_id", String.valueOf(WebFrameworkUtils.getLoginUserId()));
+            channelExtras.put("user_type", String.valueOf(WebFrameworkUtils.getLoginUserType()));
+            reqVO.setChannelExtras(channelExtras);
+        }
         PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP());
         return success(respVO);
     }

+ 51 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.pay.dal.mysql.wallet;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
@@ -12,6 +14,55 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
         return selectOne(PayWalletDO::getUserId, userId,
                 PayWalletDO::getUserType, userType);
     }
+
+    /**
+     * 当余额减少时候更新
+     *
+     * @param bizType 业务类型
+     * @param balance 当前余额
+     * @param totalRecharge 当前累计充值
+     * @param totalExpense 当前累计支出
+     * @param price 支出的金额
+     * @param id 钱包 id
+     */
+    default int updateWhenDecBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
+                                     Long totalExpense, Integer price, Long id) {
+        PayWalletDO updateDO = new PayWalletDO().setBalance(balance - price);
+        if(bizType == PayWalletBizTypeEnum.PAYMENT){
+            updateDO.setTotalExpense(totalExpense + price);
+        }
+        if (bizType == PayWalletBizTypeEnum.RECHARGE_REFUND) {
+            updateDO.setTotalRecharge(totalRecharge - price);
+        }
+        return update(updateDO,
+                new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
+                        .eq(PayWalletDO::getBalance, balance)
+                        .ge(PayWalletDO::getBalance, price));
+    }
+
+    /**
+     * 当余额增加时候更新
+     *
+     * @param bizType  业务类型
+     * @param balance  当前余额
+     * @param totalRecharge 当前累计充值
+     * @param totalExpense 当前累计支出
+     * @param price 金额
+     * @param id 钱包 id
+     */
+    default int updateWhenIncBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
+                                     Long totalExpense, Integer price, Long id) {
+        PayWalletDO updateDO = new PayWalletDO().setBalance(balance + price);
+        if (bizType == PayWalletBizTypeEnum.PAYMENT_REFUND) {
+            updateDO.setTotalExpense(totalExpense - price);
+        }
+        if (bizType == PayWalletBizTypeEnum.RECHARGE) {
+            updateDO.setTotalExpense(totalRecharge + price);
+        }
+        return update(updateDO,
+                new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
+                        .eq(PayWalletDO::getBalance, balance));
+    }
 }
 
 

+ 8 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java

@@ -1,5 +1,7 @@
 package cn.iocoder.yudao.module.pay.framework.pay.wallet;
 
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@@ -41,7 +43,12 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
     @Override
     protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         try {
-            PayWalletTransactionDO transaction = wallService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());
+            String userId = MapUtil.getStr(reqDTO.getChannelExtras(), "user_id");
+            String userType = MapUtil.getStr(reqDTO.getChannelExtras(), "user_type");
+            Assert.notEmpty(userId, "用户 id 不能为空");
+            Assert.notEmpty(userType, "用户类型不能为空");
+            PayWalletTransactionDO transaction = wallService.pay(Long.valueOf(userId), Integer.valueOf(userType),
+                    reqDTO.getOutTradeNo(), reqDTO.getPrice());
             return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
                     transaction.getTransactionTime(),
                     reqDTO.getOutTradeNo(), transaction);

+ 32 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.service.wallet;
 
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
 
 /**
  * 钱包 Service 接口
@@ -20,15 +21,43 @@ public interface PayWalletService {
     PayWalletDO getPayWallet(Long userId, Integer userType);
 
     /**
-     * 钱包支付
+     * 钱包订单支付
      *
      * @param outTradeNo 外部订单号
      * @param price 金额
      */
-    PayWalletTransactionDO pay(String outTradeNo, Integer price);
+    PayWalletTransactionDO pay(Long userId, Integer userType, String outTradeNo, Integer price);
+
+
+    /**
+     * 扣减钱包余额
+     *
+     * @param userId  用户 id
+     * @param userType 用户类型
+     * @param bizId 业务关联 id
+     * @param bizType 业务关联分类
+     * @param price 扣减金额
+     * @return 钱包流水
+     */
+    PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
+                                               Long bizId, PayWalletBizTypeEnum bizType, Integer price);
+
+
+    /**
+     * 增加钱包余额
+     *
+     * @param userId 用户 id
+     * @param userType 用户类型
+     * @param bizId 业务关联 id
+     * @param bizType 业务关联分类
+     * @param price 增加金额
+     * @return 钱包流水
+     */
+    PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
+                                            Long bizId, PayWalletBizTypeEnum bizType, Integer price);
 
     /**
-     * 钱包支付退款
+     * 钱包订单支付退款
      *
      * @param outRefundNo 外部退款号
      * @param refundPrice 退款金额

+ 57 - 41
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java

@@ -1,11 +1,13 @@
 package cn.iocoder.yudao.module.pay.service.wallet;
 
+import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper;
 import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
 import lombok.extern.slf4j.Slf4j;
@@ -16,9 +18,8 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
 
+import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.TOO_MANY_REQUESTS;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
-import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType;
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
 import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT_REFUND;
@@ -60,42 +61,70 @@ public class PayWalletServiceImpl implements  PayWalletService {
         return payWalletMapper.selectByUserIdAndType(userId, userType);
     }
 
-    // TODO @jason:可以做的更抽象一点;pay(bizType, bizId, price);reduceWalletBalance;
-    // TODO @jason:最好是,明确传入哪个 userId 或者 walletId;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public PayWalletTransactionDO pay(String outTradeNo, Integer price) {
-        // 1.1 判断支付交易拓展单是否存
+    public PayWalletTransactionDO pay(Long userId, Integer userType, String outTradeNo, Integer price) {
+        // 判断支付交易拓展单是否存
         PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo);
         if (orderExtension == null) {
             throw exception(ORDER_EXTENSION_NOT_FOUND);
         }
+        return reduceWalletBalance(userId, userType, orderExtension.getOrderId(), PAYMENT, price);
+    }
+
+    @Override
+    public PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
+                                                      Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
+        // 1.1 判断钱包是否有效
+        PayWalletDO payWallet = validatePayWallet(userId, userType);
         // 1.2 判断余额是否足够
-        PayWalletDO payWallet = validatePayWallet();
         int afterBalance = payWallet.getBalance() - price;
         if (afterBalance < 0) {
             throw exception(WALLET_BALANCE_NOT_ENOUGH);
         }
 
         // 2.1 扣除余额
-        // TODO @jason:不要直接整个更新;而是 new 一个出来更新;然后要考虑并发,要 where 余额 > price,以及 - price
-        payWallet.setBalance(afterBalance);
-        payWallet.setTotalExpense(payWallet.getTotalExpense() + price);
-        payWalletMapper.updateById(payWallet);
+        int number = payWalletMapper.updateWhenDecBalance(bizType,payWallet.getBalance(), payWallet.getTotalRecharge(),
+                payWallet.getTotalExpense(), price, payWallet.getId());
+        if (number == 0) {
+            throw exception(TOO_MANY_REQUESTS);
+        }
 
-        // 2.2 生成钱包流水
+        // 2.2 生成钱包流水 TODO 根据 bizType 生成 NO
         String walletNo = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
         PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
-                .setNo(walletNo).setAmount(price * -1).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
-                .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getType());
+                .setNo(walletNo).setAmount(-price).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
+                .setBizId(bizId).setBizType(bizType.getType()).setDescription(bizType.getDescription());
         payWalletTransactionService.createWalletTransaction(walletTransaction);
         return walletTransaction;
     }
 
-    // TODO @jason:不要在 service 里去使用用户上下文,这样和 request 就耦合了。
-    private PayWalletDO validatePayWallet() {
-        Long userId = getLoginUserId();
-        Integer userType = getLoginUserType();
+    @Override
+    public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType, Long bizId,
+                                                   PayWalletBizTypeEnum bizType, Integer price) {
+        // 1.1 判断钱包是否有效
+        PayWalletDO payWallet = validatePayWallet(userId, userType);
+
+        // 2.1 增加余额
+        int number = payWalletMapper.updateWhenIncBalance(bizType, payWallet.getBalance(), payWallet.getTotalRecharge(),
+                payWallet.getTotalExpense(), price, payWallet.getId());
+        if (number == 0) {
+            throw exception(TOO_MANY_REQUESTS);
+        }
+
+        // 2.2 生成钱包流水 TODO 根据 bizType 生成 NO
+        String walletNo = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
+        PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
+                .setNo(walletNo).setAmount(price).setBalance(payWallet.getBalance()+price).setTransactionTime(LocalDateTime.now())
+                .setBizId(bizId).setBizType(bizType.getType())
+                .setDescription(bizType.getDescription());
+        payWalletTransactionService.createWalletTransaction(newWalletTransaction);
+        return newWalletTransaction;
+    }
+
+
+    private PayWalletDO validatePayWallet(Long userId, Integer userType) {
         PayWalletDO payWallet = getPayWallet(userId, userType);
         if (payWallet == null) {
             log.error("[validatePayWallet] 用户 {} 钱包不存在", userId);
@@ -104,7 +133,7 @@ public class PayWalletServiceImpl implements  PayWalletService {
         return payWallet;
     }
 
-    // TODO @jason:可以做的更抽象一点;pay(bizType, bizId, price);addWalletBalance;这样,如果后续充值,应该也是能复用这个方法的;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason) {
@@ -114,23 +143,11 @@ public class PayWalletServiceImpl implements  PayWalletService {
             throw exception(REFUND_NOT_FOUND);
         }
         // 1.2 校验是否可以退款
-        PayWalletDO payWallet = validatePayWallet();
-        validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), payWallet.getId(), refundPrice);
-
-        // TODO @jason:不要直接整个更新;而是 new 一个出来更新;然后要考虑并发,要 where 余额 + 金额
-        Integer afterBalance = payWallet.getBalance() + refundPrice;
-        payWallet.setBalance(afterBalance);
-        payWallet.setTotalExpense(payWallet.getTotalExpense() + refundPrice * -1L);
-        payWalletMapper.updateById(payWallet);
+        Long walletId =  validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(),  refundPrice);
 
-        // 2.2 生成钱包流水
-        String walletNo = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
-        PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
-                .setNo(walletNo).setAmount(refundPrice).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
-                .setBizId(payRefund.getId()).setBizType(PAYMENT_REFUND.getType())
-                .setDescription(reason);
-        payWalletTransactionService.createWalletTransaction(newWalletTransaction);
-        return newWalletTransaction;
+        PayWalletDO payWallet = payWalletMapper.selectById(walletId);
+        Assert.notNull(payWallet, "钱包 {} 不存在", walletId);
+        return addWalletBalance(payWallet.getUserId(), payWallet.getUserType(),payRefund.getId(), PAYMENT_REFUND, refundPrice);
     }
 
     /**
@@ -138,24 +155,23 @@ public class PayWalletServiceImpl implements  PayWalletService {
      *
      * @param refundId 支付退款单 id
      * @param walletPayNo 钱包支付 no
-     * @param walletId 钱包 id
      */
-    // TODO @jason:不要使用基本类型;
-    private void validateWalletCanRefund(long refundId, String walletPayNo, long walletId, int refundPrice) {
+    private Long validateWalletCanRefund(Long refundId, String walletPayNo, Integer refundPrice) {
         // 查询钱包支付交易
         PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getWalletTransactionByNo(walletPayNo);
         if (payWalletTransaction == null) {
             throw exception(WALLET_TRANSACTION_NOT_FOUND);
         }
         // 原来的支付金额
-        int amount = payWalletTransaction.getAmount() * -1; // TODO @jason:直接 - payWalletTransaction.getAmount() 即可;
+        int amount = - payWalletTransaction.getAmount();
         if (refundPrice != amount) {
             throw exception(WALLET_REFUND_AMOUNT_ERROR);
         }
-        PayWalletTransactionDO refundTransaction = payWalletTransactionService.getWalletTransaction(walletId, refundId, PAYMENT_REFUND);
+        PayWalletTransactionDO refundTransaction = payWalletTransactionService.getWalletTransaction(
+                payWalletTransaction.getWalletId(), refundId, PAYMENT_REFUND);
         if (refundTransaction != null) {
             throw exception(WALLET_REFUND_EXIST);
         }
+        return payWalletTransaction.getWalletId();
     }
-
 }