Browse Source

钱包 review 修改

jason 1 year ago
parent
commit
5858f57fe9

+ 2 - 2
sql/mysql/pay_wallet.sql

@@ -8,8 +8,8 @@ CREATE TABLE `pay_wallet`
     `user_id`        bigint   NOT NULL COMMENT '用户编号',
     `user_type`      tinyint  NOT NULL DEFAULT 0 COMMENT '用户类型',
     `balance`        int      NOT NULL DEFAULT 0 COMMENT '余额,单位分',
-    `total_expense`  bigint      NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
-    `total_recharge` bigint      NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
+    `total_expense`  int      NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
+    `total_recharge` int      NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
     `creator`        varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
     `create_time`    datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
     `updater`        varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java

@@ -11,9 +11,9 @@ public class AppPayWalletRespVO {
     private Integer balance;
 
     @Schema(description = "累计支出, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
-    private Long totalExpense;
+    private Integer totalExpense;
 
     @Schema(description = "累计充值, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
-    private Long totalRecharge;
+    private Integer totalRecharge;
 
 }

+ 3 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletTransactionConvert.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.convert.wallet;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionRespVO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
+import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
@@ -12,4 +13,6 @@ public interface PayWalletTransactionConvert {
     PayWalletTransactionConvert INSTANCE = Mappers.getMapper(PayWalletTransactionConvert.class);
 
     PageResult<AppPayWalletTransactionRespVO> convertPage(PageResult<PayWalletTransactionDO> page);
+
+    PayWalletTransactionDO convert(CreateWalletTransactionBO bo);
 }

+ 2 - 4
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletDO.java

@@ -37,8 +37,6 @@ public class PayWalletDO extends BaseDO {
      */
     private Integer userType;
 
-    // TODO @jason:三个都搞 integer?应该要统一哈
-
     /**
      * 余额,单位分
      */
@@ -47,10 +45,10 @@ public class PayWalletDO extends BaseDO {
     /**
      * 累计支出,单位分
      */
-    private Long totalExpense;
+    private Integer totalExpense;
     /**
      * 累计充值,单位分
      */
-    private Long totalRecharge;
+    private Integer totalRecharge;
 
 }

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

@@ -3,8 +3,7 @@ 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 com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
@@ -15,56 +14,31 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
                 PayWalletDO::getUserType, userType);
     }
 
-    // TODO @jason:减少时,需要 update price -= ? where price >= ?,避免并发问题。现在基于 price 来过滤,虽然也能解决并发问题,但是冲突概率会高一点;可以看到 TradeBrokerageUserMapper 的做法;
     /**
-     * 当余额减少时候更新
+     * 当消费退款时候, 更新钱包
      *
-     * @param bizType 业务类型
-     * @param balance 当前余额
-     * @param totalRecharge 当前累计充值
-     * @param totalExpense 当前累计支出
-     * @param price 支出的金额
+     * @param price 消费金额
      * @param id 钱包 id
      */
-    default int updateWhenDecBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
-                                     Long totalExpense, Integer price, Long id) {
-        // TODO @jason:这种偏判断的,最红放在 service 层;mapper 可以写多个方法;
-        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));
+    default int updateWhenConsumptionRefund(Integer price, Long id){
+        LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
+                .setSql(" balance = balance + " + price + ", total_expense = total_expense - " + price)
+                .eq(PayWalletDO::getId, id);
+        return update(null, lambdaUpdateWrapper);
     }
 
-    // TODO @jason:类似上面的修改建议哈;
     /**
-     * 当余额增加时候更新
+     * 当消费时候, 更新钱包
      *
-     * @param bizType  业务类型
-     * @param balance  当前余额
-     * @param totalRecharge 当前累计充值
-     * @param totalExpense 当前累计支出
-     * @param price 金额
+     * @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));
+    default int updateWhenConsumption(Integer price, Long id){
+        LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
+                .setSql(" balance = balance - " + price + ", total_expense = total_expense + " + price)
+                .eq(PayWalletDO::getId, id)
+                .ge(PayWalletDO::getBalance, price); // cas 逻辑
+        return update(null, lambdaUpdateWrapper);
     }
 }
 

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

@@ -62,13 +62,12 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
     @Override
     protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         try {
-            // TODO @jason:直接 getLong 和 getInt 会不会更简洁哈
-            String userId = MapUtil.getStr(reqDTO.getChannelExtras(), USER_ID_KEY);
-            String userType = MapUtil.getStr(reqDTO.getChannelExtras(), USER_TYPE_KEY);
-            Assert.notEmpty(userId, "用户 id 不能为空");
-            Assert.notEmpty(userType, "用户类型不能为空");
-            PayWalletTransactionDO transaction = wallService.orderPay(Long.valueOf(userId), Integer.valueOf(userType),
-                    reqDTO.getOutTradeNo(), reqDTO.getPrice());
+            Long userId = MapUtil.getLong(reqDTO.getChannelExtras(), USER_ID_KEY);
+            Integer userType = MapUtil.getInt(reqDTO.getChannelExtras(), USER_TYPE_KEY);
+            Assert.notNull(userId, "用户 id 不能为空");
+            Assert.notNull(userType, "用户类型不能为空");
+            PayWalletTransactionDO transaction = wallService.orderPay(userId, userType, reqDTO.getOutTradeNo(),
+                    reqDTO.getPrice());
             return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
                     transaction.getCreateTime(),
                     reqDTO.getOutTradeNo(), transaction);

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

@@ -6,10 +6,10 @@ 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 cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -18,7 +18,6 @@ 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.module.pay.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
@@ -33,20 +32,8 @@ import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYM
 @Slf4j
 public class PayWalletServiceImpl implements  PayWalletService {
 
-    /**
-     * 余额支付的 no 前缀
-     */
-    private static final String WALLET_PAY_NO_PREFIX = "WP";
-    /**
-     * 余额退款的 no 前缀
-     */
-    private static final String WALLET_REFUND_NO_PREFIX = "WR";
-
     @Resource
     private PayWalletMapper walletMapper;
-    @Resource
-    private PayNoRedisDAO noRedisDAO;
-
     @Resource
     private PayWalletTransactionService walletTransactionService;
     @Resource
@@ -61,7 +48,7 @@ public class PayWalletServiceImpl implements  PayWalletService {
         PayWalletDO wallet = walletMapper.selectByUserIdAndType(userId, userType);
         if (wallet == null) {
             wallet = new PayWalletDO().setUserId(userId).setUserType(userType)
-                    .setBalance(0).setTotalExpense(0L).setTotalRecharge(0L);
+                    .setBalance(0).setTotalExpense(0).setTotalRecharge(0);
             wallet.setCreateTime(LocalDateTime.now());
             walletMapper.insert(wallet);
         }
@@ -127,68 +114,50 @@ public class PayWalletServiceImpl implements  PayWalletService {
                                                       Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
         // 1.1 获取钱包
         PayWalletDO payWallet = getOrCreateWallet(userId, userType);
-        // 1.2 判断余额是否足够
-        int afterBalance = payWallet.getBalance() - price;
-        if (afterBalance < 0) {
-            throw exception(WALLET_BALANCE_NOT_ENOUGH);
-        }
-
-        // TODO jason:建议基于 where price >= 来做哈;然后抛出 WALLET_BALANCE_NOT_ENOUGH
         // 2.1 扣除余额
-        int number = walletMapper.updateWhenDecBalance(bizType, payWallet.getBalance(),
-                payWallet.getTotalRecharge(), payWallet.getTotalExpense(), price, payWallet.getId());
+        int number = 0 ;
+        switch (bizType) {
+            case PAYMENT: {
+                number = walletMapper.updateWhenConsumption(price, payWallet.getId());
+                break;
+            }
+            case RECHARGE_REFUND: {
+                // TODO
+                break;
+            }
+        }
         if (number == 0) {
-            throw exception(TOO_MANY_REQUESTS);
+            throw exception(WALLET_BALANCE_NOT_ENOUGH);
         }
-
+        int afterBalance = payWallet.getBalance() - price;
         // 2.2 生成钱包流水
-        // TODO @jason:walletNo 交给 payWalletTransactionService 自己生成哈;
-        String walletNo = generateWalletNo(bizType);
-        PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
-                .setNo(walletNo).setPrice(-price).setBalance(afterBalance)
-                .setBizId(String.valueOf(bizId)).setBizType(bizType.getType()).setTitle(bizType.getDescription());
-        // TODO @jason:是不是可以 createWalletTransaction 搞个 bo 参数,然后 PayWalletTransactionDO 交回给 walletTransactionService 更好;然后把参数简化下
-        walletTransactionService.createWalletTransaction(walletTransaction);
-        return walletTransaction;
+        CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
+                .setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
+                .setBizType(bizType.getType()).setTitle(bizType.getDescription());
+        return walletTransactionService.createWalletTransaction(bo);
     }
 
     @Override
     public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
                                                    Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
-        // 1.1 获取钱包
+        // 获取钱包
         PayWalletDO payWallet = getOrCreateWallet(userId, userType);
-
-        // 2.1 增加余额
-        // TODO @jason:类似上面的思路哈;
-        int number = walletMapper.updateWhenIncBalance(bizType, payWallet.getBalance(), payWallet.getTotalRecharge(),
-                payWallet.getTotalExpense(), price, payWallet.getId());
-        if (number == 0) {
-            throw exception(TOO_MANY_REQUESTS);
-        }
-
-        // 2.2 生成钱包流水
-        String walletNo = generateWalletNo(bizType);
-        PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
-                .setNo(walletNo).setPrice(price).setBalance(payWallet.getBalance()+price)
-                .setBizId(String.valueOf(bizId)).setBizType(bizType.getType())
-                .setTitle(bizType.getDescription());
-        walletTransactionService.createWalletTransaction(newWalletTransaction);
-        return newWalletTransaction;
-    }
-
-    private String generateWalletNo(PayWalletBizTypeEnum bizType) {
-        // TODO @jason:对于余额来说,是不是直接 W+序号就行了,它其实不关注业务;;;不然就耦合啦
-        String no = "";
-        switch(bizType){
-            case PAYMENT:
-                no = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
+        switch (bizType) {
+            case PAYMENT_REFUND: {
+                // 更新退款
+                walletMapper.updateWhenConsumptionRefund(price, payWallet.getId());
                 break;
-            case PAYMENT_REFUND:
-                no = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
+            }
+            case RECHARGE: {
+                //TODO
                 break;
-            default :
+            }
         }
-        return no;
+        // 2.2 生成钱包流水
+        CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
+                .setPrice(price).setBalance(payWallet.getBalance()+price).setBizId(String.valueOf(bizId))
+                .setBizType(bizType.getType()).setTitle(bizType.getDescription());
+        return walletTransactionService.createWalletTransaction(bo);
     }
 
 }

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

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
 
 /**
  * 钱包余额流水 Service 接口
@@ -25,10 +26,10 @@ public interface PayWalletTransactionService {
     /**
      * 新增钱包余额流水
      *
-     * @param payWalletTransaction 余额流水
-     * @return id
+     * @param bo 创建钱包流水 bo
+     * @return 新建的钱包 do
      */
-    Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction);
+    PayWalletTransactionDO createWalletTransaction(CreateWalletTransactionBO bo);
 
     /**
      * 根据 no,获取钱包余流水

+ 14 - 4
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java

@@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.pay.service.wallet;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
+import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletTransactionConvert;
 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.PayWalletTransactionMapper;
+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.wallet.bo.CreateWalletTransactionBO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -19,11 +22,16 @@ import javax.annotation.Resource;
 @Service
 @Slf4j
 public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
-
+    /**
+     * 钱包流水的 no 前缀
+     */
+    private static final String WALLET_NO_PREFIX = "W";
     @Resource
     private PayWalletService payWalletService;
     @Resource
     private PayWalletTransactionMapper payWalletTransactionMapper;
+    @Resource
+    private PayNoRedisDAO noRedisDAO;
 
     @Override
     public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
@@ -33,9 +41,11 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
     }
 
     @Override
-    public Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
-         payWalletTransactionMapper.insert(payWalletTransaction);
-         return payWalletTransaction.getId();
+    public PayWalletTransactionDO createWalletTransaction(CreateWalletTransactionBO bo) {
+        PayWalletTransactionDO transactionDO = PayWalletTransactionConvert.INSTANCE.convert(bo);
+        transactionDO.setNo(noRedisDAO.generate(WALLET_NO_PREFIX));
+        payWalletTransactionMapper.insert(transactionDO);
+        return transactionDO;
     }
 
     @Override

+ 48 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/bo/CreateWalletTransactionBO.java

@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.module.pay.service.wallet.bo;
+
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
+import lombok.Data;
+
+/**
+ * 创建钱包流水 BO
+ *
+ * @author jason
+ */
+@Data
+public class CreateWalletTransactionBO {
+
+    /**
+     * 钱包编号
+     *
+     */
+    private Long walletId;
+
+    /**
+     * 交易金额,单位分
+     *
+     * 正值表示余额增加,负值表示余额减少
+     */
+    private Integer price;
+
+    /**
+     * 交易后余额,单位分
+     */
+    private Integer balance;
+
+    /**
+     * 关联业务分类
+     *
+     * 枚举 {@link PayWalletBizTypeEnum#getType()}
+     */
+    private Integer bizType;
+
+    /**
+     * 关联业务编号
+     */
+    private String bizId;
+
+    /**
+     * 流水说明
+     */
+    private String title;
+}