Bläddra i källkod

code review:钱包实现

YunaiV 1 år sedan
förälder
incheckning
6132443d26
21 ändrade filer med 211 tillägg och 129 borttagningar
  1. 6 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/delegate/DelegatePayClient.java
  2. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClientTest.java
  3. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClientTest.java
  4. 32 18
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
  5. 27 12
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClientTest.java
  6. 2 2
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
  7. 6 4
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/PayWalletBizTypeEnum.java
  8. 11 3
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/WalletTransactionQueryTypeEnum.java
  9. 1 0
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletController.java
  10. 3 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/AppPayWalletTransactionPageReqVO.java
  11. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/AppPayWalletTransactionRespVO.java
  12. 5 6
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletDO.java
  13. 19 15
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletTransactionDO.java
  14. 2 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
  15. 5 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java
  16. 9 6
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java
  17. 1 0
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
  18. 6 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
  19. 41 22
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
  20. 18 14
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java
  21. 12 10
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java

+ 6 - 3
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/delegate/DelegatePayClient.java

@@ -9,14 +9,17 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
 
 import java.util.Map;
 
+// TODO @jason:其它模块,主要是无法 pay client 初始化存在问题,所以我感觉,是不是可以搞个 PayClientInitializer 接口。这样,PayClientFactory 去 get 这个支付模式对应的 PayClientInitializer,通过它来创建。具体注入的地方,可以在 PayChannel init 方法那;
 /**
- * 代理支付 Client 的抽象类。用于支付 Client 由其它模块实现, 例如钱包支付
+ * 代理支付 Client 的抽象类。
+ *
+ * 用于支付 Client 由其它模块实现,例如钱包支付
  *
  * @author jason
  */
-public abstract  class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> {
+public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> {
 
-    private  final DelegatePayClient<Config> delegate;
+    private final DelegatePayClient<Config> delegate;
 
     public DelegatePayClient(Long channelId, String channelCode, PayClientConfig config) {
         super(channelId, channelCode, config);

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClientTest.java

@@ -168,7 +168,7 @@ public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
     @Test
     @DisplayName("支付宝 Client 统一退款:抛出业务异常")
     public void testUnifiedRefund_throwServiceException() throws AlipayApiException {
-        // mock
+        // mock 方法
         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
                 .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
         // 准备请求参数
@@ -182,7 +182,7 @@ public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
     @Test
     @DisplayName("支付宝 Client 统一退款:抛出系统异常")
     public void testUnifiedRefund_throwPayException() throws AlipayApiException {
-        // mock
+        // mock 方法
         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
                 .thenThrow(new RuntimeException("系统异常"));
         // 准备请求参数

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClientTest.java

@@ -70,7 +70,7 @@ public class AlipayPcPayClientTest extends AbstractAlipayClientTest {
     @Test
     @DisplayName("支付宝 PC 网站支付:Form Display Mode 下单成功")
     public void testUnifiedOrder_formSuccess() throws AlipayApiException {
-        // mock
+        // mock 方法
         String notifyUrl = randomURL();
         AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode(""));
         when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradePagePayRequest>) request -> true),
@@ -99,7 +99,7 @@ public class AlipayPcPayClientTest extends AbstractAlipayClientTest {
     @Test
     @DisplayName("支付宝 PC 网站支付:渠道返回失败")
     public void testUnifiedOrder_channelFailed() throws AlipayApiException {
-        // mock
+        // mock 方法
         String subCode = randomString();
         String subMsg = randomString();
         AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> {

+ 32 - 18
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java

@@ -39,9 +39,9 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
     }
 
     @Test
-    @DisplayName("支付宝扫描支付下单成功")
-    public void test_unified_order_success() throws AlipayApiException {
-        // 准备返回对象
+    @DisplayName("支付宝扫描支付下单成功")
+    public void testUnifiedOrder_success() throws AlipayApiException {
+        // mock 方法
         String notifyUrl = randomURL();
         String qrCode = randomString();
         Integer price = randomInteger();
@@ -49,7 +49,6 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
             o.setQrCode(qrCode);
             o.setSubCode("");
         });
-        // mock
         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
             assertEquals(notifyUrl, request.getNotifyUrl());
             return true;
@@ -58,18 +57,25 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
         String outTradeNo = randomString();
         PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
 
+        // 调用
         PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
         // 断言
         assertEquals(WAITING.getStatus(), resp.getStatus());
-        assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());
         assertEquals(outTradeNo, resp.getOutTradeNo());
-        assertEquals(qrCode, resp.getDisplayContent());
+        assertNull(resp.getChannelOrderNo());
+        assertNull(resp.getChannelUserId());
+        assertNull(resp.getSuccessTime());
+        assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());
+        assertEquals(response.getQrCode(), resp.getDisplayContent());
         assertSame(response, resp.getRawData());
+        assertNull(resp.getChannelErrorCode());
+        assertNull(resp.getChannelErrorMsg());
     }
 
     @Test
-    @DisplayName("支付宝扫描支付,渠道返回失败")
-    public void test_unified_order_channel_failed() throws AlipayApiException {
+    @DisplayName("支付宝扫描支付:渠道返回失败")
+    public void testUnifiedOrder_channelFailed() throws AlipayApiException {
+        // mock 方法
         String notifyUrl = randomURL();
         String subCode = randomString();
         String subMsg = randomString();
@@ -87,47 +93,55 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
         String outTradeNo = randomString();
         PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
 
+        // 调用
         PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
         // 断言
         assertEquals(CLOSED.getStatus(), resp.getStatus());
+        assertEquals(outTradeNo, resp.getOutTradeNo());
+        assertNull(resp.getChannelOrderNo());
+        assertNull(resp.getChannelUserId());
+        assertNull(resp.getSuccessTime());
+        assertNull(resp.getDisplayMode());
+        assertNull(resp.getDisplayContent());
+        assertSame(response, resp.getRawData());
         assertEquals(subCode, resp.getChannelErrorCode());
         assertEquals(subMsg, resp.getChannelErrorMsg());
-        assertSame(response, resp.getRawData());
     }
 
     @Test
     @DisplayName("支付宝扫描支付, 抛出系统异常")
-    public void test_unified_order_throw_pay_exception() throws AlipayApiException {
-        // 准备请求参数
+    public void testUnifiedOrder_throwPayException() throws AlipayApiException {
+        // mock 方法
         String outTradeNo = randomString();
         String notifyUrl = randomURL();
         Integer price = randomInteger();
-        // mock
         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
             assertEquals(notifyUrl, request.getNotifyUrl());
             return true;
         }))).thenThrow(new RuntimeException("系统异常"));
         // 准备请求参数
         PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo,price);
-        // 断言
+
+        // 调用,并断言
         assertThrows(PayException.class, () -> client.unifiedOrder(reqDTO));
     }
 
     @Test
-    @DisplayName("支付宝 Client 统一下单,抛出业务异常")
-    public void test_unified_order_throw_service_exception() throws AlipayApiException {
-        // 准备请求参数
+    @DisplayName("支付宝 Client 统一下单抛出业务异常")
+    public void testUnifiedOrder_throwServiceException() throws AlipayApiException {
+        // mock 方法
         String outTradeNo = randomString();
         String notifyUrl = randomURL();
         Integer price = randomInteger();
-        // mock
         when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
             assertEquals(notifyUrl, request.getNotifyUrl());
             return true;
         }))).thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
         // 准备请求参数
         PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
-        // 断言
+
+        // 调用,并断言
         assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
     }
+
 }

+ 27 - 12
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClientTest.java

@@ -42,9 +42,9 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
     }
 
     @Test
-    @DisplayName("支付宝 H5 支付下单成功")
-    public void test_unified_order_success() throws AlipayApiException {
-        // 准备响应对象
+    @DisplayName("支付宝 H5 支付下单成功")
+    public void testUnifiedOrder_success() throws AlipayApiException {
+        // mock 方法
         String h5Body = randomString();
         Integer price = randomInteger();
         AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
@@ -52,7 +52,6 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
             o.setBody(h5Body);
         });
         String notifyUrl = randomURL();
-        // mock
         when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> {
             assertInstanceOf(AlipayTradeWapPayModel.class, request.getBizModel());
             AlipayTradeWapPayModel bizModel = (AlipayTradeWapPayModel) request.getBizModel();
@@ -60,37 +59,53 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
             assertEquals(notifyUrl, request.getNotifyUrl());
             return true;
         }), eq(Method.GET.name()))).thenReturn(response);
-
+        // 准备请求参数
         String outTradeNo = randomString();
         PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
+
+        // 调用
         PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
+        // 断言
         assertEquals(WAITING.getStatus(), resp.getStatus());
-        assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
         assertEquals(outTradeNo, resp.getOutTradeNo());
-        assertEquals(h5Body, resp.getDisplayContent());
+        assertNull(resp.getChannelOrderNo());
+        assertNull(resp.getChannelUserId());
+        assertNull(resp.getSuccessTime());
+        assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
+        assertEquals(response.getBody(), resp.getDisplayContent());
         assertSame(response, resp.getRawData());
+        assertNull(resp.getChannelErrorCode());
+        assertNull(resp.getChannelErrorMsg());
     }
 
     @Test
-    @DisplayName("支付宝 H5 支付,渠道返回失败")
+    @DisplayName("支付宝 H5 支付渠道返回失败")
     public void test_unified_order_channel_failed() throws AlipayApiException {
-        // 准备响应对象
+        // mock 方法
         String subCode = randomString();
         String subMsg = randomString();
         AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
             o.setSubCode(subCode);
             o.setSubMsg(subMsg);
         });
-        // mock
         when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> true),
                 eq(Method.GET.name()))).thenReturn(response);
-        PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
+        String outTradeNo = randomString();
+        PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
 
+        // 调用
         PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
         // 断言
         assertEquals(CLOSED.getStatus(), resp.getStatus());
+        assertEquals(outTradeNo, resp.getOutTradeNo());
+        assertNull(resp.getChannelOrderNo());
+        assertNull(resp.getChannelUserId());
+        assertNull(resp.getSuccessTime());
+        assertNull(resp.getDisplayMode());
+        assertNull(resp.getDisplayContent());
+        assertSame(response, resp.getRawData());
         assertEquals(subCode, resp.getChannelErrorCode());
         assertEquals(subMsg, resp.getChannelErrorMsg());
-        assertSame(response, resp.getRawData());
     }
+
 }

+ 2 - 2
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java

@@ -41,9 +41,9 @@ public interface ErrorCodeConstants {
     ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
     ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款");
 
-    // ========== 钱包模块(退款) 1007007000 ==========
+    // ========== 钱包模块 1007007000 ==========
     ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在");
-    ErrorCode WALLET_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足");
+    ErrorCode WALLET_BALANCE_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足");
     ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易");
     ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对");
     ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款");

+ 6 - 4
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/WalletBizTypeEnum.java → yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/PayWalletBizTypeEnum.java

@@ -10,20 +10,22 @@ import lombok.Getter;
  */
 @AllArgsConstructor
 @Getter
-public enum WalletBizTypeEnum {
+public enum PayWalletBizTypeEnum {
+
     RECHARGE(1, "充值"),
     RECHARGE_REFUND(2, "充值退款"),
     PAYMENT(3, "支付"),
     PAYMENT_REFUND(4, "支付退款");
 
     // TODO 后续增加
+
     /**
      * 业务分类
      */
-    private final Integer bizType;
-
+    private final Integer type;
     /**
      * 说明
      */
-    private final String desc;
+    private final String description;
+
 }

+ 11 - 3
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/WalletTransactionQueryTypeEnum.java

@@ -7,20 +7,27 @@ import lombok.Getter;
 
 import java.util.Arrays;
 
+// TODO @jason:可以简化,直接 PageVO 定义两个 Integer;
 /**
- * 钱包明细查询类型
+ * 钱包流水查询类型
  *
  * @author jason
  */
 @AllArgsConstructor
 @Getter
 public enum WalletTransactionQueryTypeEnum implements IntArrayValuable  {
+
     RECHARGE(1, "充值"),
     EXPENSE(2, "消费");
 
+    /**
+     * 类型
+     */
     private final Integer type;
-
-    private final String desc;
+    /**
+     * 描述
+     */
+    private final String description;
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(WalletTransactionQueryTypeEnum::getType).toArray();
 
@@ -32,4 +39,5 @@ public enum WalletTransactionQueryTypeEnum implements IntArrayValuable  {
     public static WalletTransactionQueryTypeEnum valueOf(Integer type) {
         return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
     }
+
 }

+ 1 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletController.java

@@ -38,4 +38,5 @@ public class AppPayWalletController {
         PayWalletDO payWallet = payWalletService.getPayWallet(getLoginUserId(), UserTypeEnum.MEMBER.getValue());
         return success(PayWalletConvert.INSTANCE.convert(payWallet));
     }
+
 }

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

@@ -6,11 +6,12 @@ import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
-@Schema(description = "用户 APP - 钱包余额明细分页 Request VO")
+@Schema(description = "用户 APP - 钱包流水分页 Request VO")
 @Data
 public class AppPayWalletTransactionPageReqVO extends PageParam {
 
-    @Schema(description = "余额明细查询分类",  example = "1")
+    @Schema(description = "流水查询分类",  example = "1")
     @InEnum(value = WalletTransactionQueryTypeEnum.class, message = "查询类型必须是 {value}")
     private Integer type;
+
 }

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

@@ -5,7 +5,7 @@ import lombok.Data;
 
 import java.time.LocalDateTime;
 
-@Schema(description = "用户 APP - 钱包余额明细分页 Response VO")
+@Schema(description = "用户 APP - 钱包流水分页 Response VO")
 @Data
 public class AppPayWalletTransactionRespVO {
     @Schema(description = "交易金额, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")

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

@@ -8,7 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 
 /**
- * 支付 - 会员钱包 DO
+ * 会员钱包 DO
  *
  * @author jason
  */
@@ -30,7 +30,6 @@ public class PayWalletDO extends BaseDO {
      * 关联 AdminUserDO 的 id 编号
      */
     private Long userId;
-
     /**
      * 用户类型, 预留 多商户转帐可能需要用到
      *
@@ -39,17 +38,17 @@ public class PayWalletDO extends BaseDO {
     private Integer userType;
 
     /**
-     * 余额, 单位分
+     * 余额单位分
      */
     private Integer balance;
 
     /**
-     * 累计支出, 单位分
+     * 累计支出单位分
      */
     private Long totalExpense;
-
     /**
-     * 累计充值, 单位分
+     * 累计充值单位分
      */
     private Long totalRecharge;
+
 }

+ 19 - 15
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletTransactionDO.java

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.pay.dal.dataobject.wallet;
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -10,7 +10,7 @@ import lombok.Data;
 import java.time.LocalDateTime;
 
 /**
- * 支付-会员钱包明细 DO
+ * 会员钱包流水 DO
  *
  * @author jason
  */
@@ -24,47 +24,51 @@ public class PayWalletTransactionDO extends BaseDO {
      */
     @TableId
     private Long id;
+    /**
+     * 流水号
+     */
+    private String no;
 
     /**
-     * 会员钱包 id
+     * 钱包编号
+     *
      * 关联 {@link PayWalletDO#getId()}
      */
     private Long walletId;
 
     /**
-     * 钱包交易业务分类
-     * 关联枚举 {@link WalletBizTypeEnum#getBizType()}
+     * 关联业务分类
+     *
+     * 枚举 {@link PayWalletBizTypeEnum#getType()}
      */
     private Integer bizType;
-
+    // TODO @jason:使用 string;因为可能有业务是 string 接入哈。
     /**
      * 关联业务编号
      */
     private Long bizId;
 
-    /**
-     * 流水号
-     */
-    private String no;
-
     /**
      * 附加说明
      */
     private String description;
 
+    // TODO @jason:使用 price 哈。项目里,金额都是用这个为主;
     /**
-     * 交易金额, 单位分
-     * 正值表示余额增加,负值表示余额减少
+     * 交易金额,单位分
+     *
+     * 正值表示余额增加,负值表示余额减少
      */
     private Integer amount;
-
     /**
-     * 交易后余额,单位分
+     * 交易后余额单位分
      */
     private Integer balance;
 
+    // TODO @jason:使用 createTime 就够啦
     /**
      * 交易时间
      */
     private LocalDateTime transactionTime;
+
 }

+ 2 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java

@@ -37,9 +37,8 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> {
                 .eq(PayRefundDO::getNo, no));
     }
 
-    default PayRefundDO selectByNo(String no){
-        return selectOne(new LambdaQueryWrapperX<PayRefundDO>()
-                .eq(PayRefundDO::getNo, no));
+    default PayRefundDO selectByNo(String no) {
+        return selectOne(PayRefundDO::getNo, no);
     }
 
     default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) {

+ 5 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java

@@ -23,7 +23,7 @@ public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransac
         if (WalletTransactionQueryTypeEnum.EXPENSE == queryType ) {
             query.lt(PayWalletTransactionDO::getAmount, 0);
         }
-        query.orderByDesc(PayWalletTransactionDO::getTransactionTime);
+        query.orderByDesc(PayWalletTransactionDO::getId);
         return selectPage(pageParam, query);
     }
 
@@ -32,9 +32,11 @@ public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransac
     }
 
     default PayWalletTransactionDO selectByWalletIdAndBiz(Long walletId, Long bizId, Integer bizType) {
-        return selectOne(PayWalletTransactionDO::getWalletId, walletId, PayWalletTransactionDO::getBizId,
-                bizId, PayWalletTransactionDO::getBizType, bizType);
+        return selectOne(PayWalletTransactionDO::getWalletId, walletId,
+                PayWalletTransactionDO::getBizId, bizId,
+                PayWalletTransactionDO::getBizType, bizType);
     }
+
 }
 
 

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

@@ -22,29 +22,31 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
  */
 @Slf4j
 public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> {
+
     private PayWalletService payWalletService;
 
     public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) {
         super(channelId, channelCode, config);
     }
 
-    public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config, PayWalletService payWalletService) {
+    public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config,
+                           PayWalletService payWalletService) {
         this(channelId, channelCode, config);
         this.payWalletService = payWalletService;
     }
 
     @Override
     protected void doInit() {
-        // 钱包支付 无需初始化
+        // 钱包支付无需初始化
     }
 
     @Override
     protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         try {
-            PayWalletTransactionDO payWalletTransaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());
-            return PayOrderRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreator(),
-                    payWalletTransaction.getTransactionTime(),
-                    reqDTO.getOutTradeNo(), "WALLET_PAY_SUCCESS");
+            PayWalletTransactionDO transaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());
+            return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
+                    transaction.getTransactionTime(),
+                    reqDTO.getOutTradeNo(), "WALLET_PAY_SUCCESS"); // TODO @jason:transaction 作为 traData 好了;
         } catch (Throwable ex) {
             log.error("[doUnifiedOrder] 失败", ex);
             Integer errorCode = INTERNAL_SERVER_ERROR.getCode();
@@ -99,4 +101,5 @@ public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> {
     protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
         throw new UnsupportedOperationException("待实现");
     }
+
 }

+ 1 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java

@@ -83,6 +83,7 @@ public class PayChannelServiceImpl implements PayChannelService {
             log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
             // 钱包 client 需要和其它 client 分开了创建
             List<PayChannelDO> walletChannels = new ArrayList<>();
+            // TODO @jason:有点复杂,看看用 PayClientInitializer 能不能简化
             List<PayChannelDO> otherChannels = new ArrayList<>();
             channels.forEach(t -> {
                 if (PayChannelEnum.WALLET.getCode().equals(t.getCode())) {

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

@@ -10,15 +10,18 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
  */
 public interface PayWalletService {
 
+    // TODO @jason:改成 getOrCreateWallet;因为目前解耦,用户注册时,不会创建钱包;需要这里兜底处理;
     /**
      * 获取钱包信息
-     * @param userId 用户 id
+     *
+     * @param userId 用户编号
      * @param userType 用户类型
      */
     PayWalletDO getPayWallet(Long userId, Integer userType);
 
     /**
      * 钱包支付
+     *
      * @param outTradeNo 外部订单号
      * @param price 金额
      */
@@ -26,9 +29,11 @@ public interface PayWalletService {
 
     /**
      * 钱包支付退款
+     *
      * @param outRefundNo 外部退款号
      * @param refundPrice 退款金额
      * @param reason  退款原因
      */
     PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason);
+
 }

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

@@ -20,8 +20,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 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.WalletBizTypeEnum.PAYMENT;
-import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT_REFUND;
+import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
+import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT_REFUND;
 
 /**
  * 钱包 Service 实现类
@@ -31,14 +31,23 @@ import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT
 @Service
 @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 payWalletMapper;
     @Resource
-    private PayWalletTransactionService payWalletTransactionService;
-    @Resource
     private PayNoRedisDAO noRedisDAO;
+
+    @Resource
+    private PayWalletTransactionService payWalletTransactionService;
     @Resource
     @Lazy
     private PayOrderService payOrderService;
@@ -51,34 +60,39 @@ 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 判断支付交易拓展单是否存
         PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo);
         if (orderExtension == null) {
             throw exception(ORDER_EXTENSION_NOT_FOUND);
         }
+        // 1.2 判断余额是否足够
         PayWalletDO payWallet = validatePayWallet();
-        // 判断余额是否足够
         int afterBalance = payWallet.getBalance() - price;
-        if(afterBalance < 0){
-            throw exception(WALLET_NOT_ENOUGH);
+        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);
 
-        // 生成钱包渠道流水
+        // 2.2 生成钱包流水
         String walletNo = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
-        PayWalletTransactionDO payWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
+        PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
                 .setNo(walletNo).setAmount(price * -1).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
-                .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getBizType());
-        payWalletTransactionService.addPayWalletTransaction(payWalletTransaction);
-
-        return payWalletTransaction;
+                .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getType());
+        payWalletTransactionService.createWalletTransaction(walletTransaction);
+        return walletTransaction;
     }
 
+    // TODO @jason:不要在 service 里去使用用户上下文,这样和 request 就耦合了。
     private PayWalletDO validatePayWallet() {
         Long userId = getLoginUserId();
         Integer userType = getLoginUserType();
@@ -90,53 +104,58 @@ 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) {
-        // 判断退款单是否存在
+        // 1.1 判断退款单是否存在
         PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo);
         if (payRefund == null) {
             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);
 
+        // 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.getBizType())
+                .setBizId(payRefund.getId()).setBizType(PAYMENT_REFUND.getType())
                 .setDescription(reason);
-        payWalletTransactionService.addPayWalletTransaction(newWalletTransaction);
-
+        payWalletTransactionService.createWalletTransaction(newWalletTransaction);
         return newWalletTransaction;
     }
 
     /**
      * 校验是否能退款
+     *
      * @param refundId 支付退款单 id
      * @param walletPayNo 钱包支付 no
      * @param walletId 钱包 id
      */
+    // TODO @jason:不要使用基本类型;
     private void validateWalletCanRefund(long refundId, String walletPayNo, long walletId, int refundPrice) {
         // 查询钱包支付交易
-        PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getPayWalletTransactionByNo(walletPayNo);
+        PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getWalletTransactionByNo(walletPayNo);
         if (payWalletTransaction == null) {
             throw exception(WALLET_TRANSACTION_NOT_FOUND);
         }
         // 原来的支付金额
-        int amount = payWalletTransaction.getAmount() * -1;
+        int amount = payWalletTransaction.getAmount() * -1; // TODO @jason:直接 - payWalletTransaction.getAmount() 即可;
         if (refundPrice != amount) {
             throw exception(WALLET_REFUND_AMOUNT_ERROR);
         }
-        PayWalletTransactionDO refundTransaction = payWalletTransactionService.getPayWalletTransaction(walletId, refundId, PAYMENT_REFUND);
+        PayWalletTransactionDO refundTransaction = payWalletTransactionService.getWalletTransaction(walletId, refundId, PAYMENT_REFUND);
         if (refundTransaction != null) {
             throw exception(WALLET_REFUND_EXIST);
         }
     }
+
 }

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

@@ -3,19 +3,19 @@ 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.AppPayWalletTransactionPageReqVO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
-import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
 
 /**
- * 钱包余额明细 Service 接口
+ * 钱包余额流水 Service 接口
  *
  * @author jason
  */
 public interface PayWalletTransactionService {
 
     /**
-     * 查询钱包余额明细, 分页
+     * 查询钱包余额流水分页
      *
-     * @param userId   用户 id
+     * @param userId   用户编号
      * @param userType 用户类型
      * @param pageVO   分页查询参数
      */
@@ -23,24 +23,28 @@ public interface PayWalletTransactionService {
                                                                 AppPayWalletTransactionPageReqVO pageVO);
 
     /**
-     * 新增钱包余额明细
-     * @param payWalletTransaction 余额明细
+     * 新增钱包余额流水
+     *
+     * @param payWalletTransaction 余额流水
      * @return id
      */
-    Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction);
+    Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction);
 
     /**
-     * 获取钱包余额明细 根据 no
+     * 根据 no,获取钱包余流水
+     *
      * @param no 流水号
      */
-    PayWalletTransactionDO getPayWalletTransactionByNo(String no);
+    PayWalletTransactionDO getWalletTransactionByNo(String no);
 
     /**
-     * 获取钱包
-     * @param walletId 钱包 id
+     * 获取钱包流水
+     *
+     * @param walletId 钱包编号
      * @param bizId  业务编号
-     * @param typeEnum  业务类型
-     * @return 钱包余额明细
+     * @param type  业务类型
+     * @return 钱包流水
      */
-    PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum);
+    PayWalletTransactionDO getWalletTransaction(Long walletId, Long bizId, PayWalletBizTypeEnum type);
+
 }

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

@@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransact
 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.enums.member.WalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
 import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -16,45 +16,47 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.WALLET_NOT_FOUND;
 
 /**
- * 钱包余额明细 Service 实现类
+ * 钱包流水 Service 实现类
  *
  * @author jason
  */
 @Service
 @Slf4j
 public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
+
     @Resource
     private PayWalletService payWalletService;
+
     @Resource
     private PayWalletTransactionMapper payWalletTransactionMapper;
 
     @Override
     public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
                                                                        AppPayWalletTransactionPageReqVO pageVO) {
-        PayWalletDO payWallet = payWalletService.getPayWallet(userId, userType);
-        if (payWallet == null) {
+        PayWalletDO wallet = payWalletService.getPayWallet(userId, userType);
+        if (wallet == null) {
             log.error("[pageWalletTransaction] 用户 {} 钱包不存在", userId);
             throw exception(WALLET_NOT_FOUND);
         }
-        return payWalletTransactionMapper.selectPageByWalletIdAndQueryType(payWallet.getId(),
+        // TODO @jason:不用 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()) 哈,直接 pageVO 里面判断值比对就好啦;
+        return payWalletTransactionMapper.selectPageByWalletIdAndQueryType(wallet.getId(),
                 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()), pageVO);
     }
 
     @Override
-    public Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
+    public Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
          payWalletTransactionMapper.insert(payWalletTransaction);
          return payWalletTransaction.getId();
     }
 
     @Override
-    public PayWalletTransactionDO getPayWalletTransactionByNo(String no) {
+    public PayWalletTransactionDO getWalletTransactionByNo(String no) {
         return payWalletTransactionMapper.selectByNo(no);
     }
 
     @Override
-    public PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum) {
-        return payWalletTransactionMapper.selectByWalletIdAndBiz(walletId, bizId, typeEnum.getBizType());
+    public PayWalletTransactionDO getWalletTransaction(Long walletId, Long bizId, PayWalletBizTypeEnum type) {
+        return payWalletTransactionMapper.selectByWalletIdAndBiz(walletId, bizId, type.getType());
     }
 
-
 }