Commit 6132443d26949edd0acbae9c9c865bc92a8afe93

Authored by YunaiV
1 parent 9077f14b

code review:钱包实现

Showing 21 changed files with 211 additions and 129 deletions
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; @@ -9,14 +9,17 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
9 9
10 import java.util.Map; 10 import java.util.Map;
11 11
  12 +// TODO @jason:其它模块,主要是无法 pay client 初始化存在问题,所以我感觉,是不是可以搞个 PayClientInitializer 接口。这样,PayClientFactory 去 get 这个支付模式对应的 PayClientInitializer,通过它来创建。具体注入的地方,可以在 PayChannel init 方法那;
12 /** 13 /**
13 - * 代理支付 Client 的抽象类。用于支付 Client 由其它模块实现, 例如钱包支付 14 + * 代理支付 Client 的抽象类。
  15 + *
  16 + * 用于支付 Client 由其它模块实现,例如钱包支付
14 * 17 *
15 * @author jason 18 * @author jason
16 */ 19 */
17 -public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> { 20 +public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> {
18 21
19 - private final DelegatePayClient<Config> delegate; 22 + private final DelegatePayClient<Config> delegate;
20 23
21 public DelegatePayClient(Long channelId, String channelCode, PayClientConfig config) { 24 public DelegatePayClient(Long channelId, String channelCode, PayClientConfig config) {
22 super(channelId, channelCode, config); 25 super(channelId, channelCode, config);
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 { @@ -168,7 +168,7 @@ public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
168 @Test 168 @Test
169 @DisplayName("支付宝 Client 统一退款:抛出业务异常") 169 @DisplayName("支付宝 Client 统一退款:抛出业务异常")
170 public void testUnifiedRefund_throwServiceException() throws AlipayApiException { 170 public void testUnifiedRefund_throwServiceException() throws AlipayApiException {
171 - // mock 171 + // mock 方法
172 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true))) 172 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
173 .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR)); 173 .thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
174 // 准备请求参数 174 // 准备请求参数
@@ -182,7 +182,7 @@ public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest { @@ -182,7 +182,7 @@ public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
182 @Test 182 @Test
183 @DisplayName("支付宝 Client 统一退款:抛出系统异常") 183 @DisplayName("支付宝 Client 统一退款:抛出系统异常")
184 public void testUnifiedRefund_throwPayException() throws AlipayApiException { 184 public void testUnifiedRefund_throwPayException() throws AlipayApiException {
185 - // mock 185 + // mock 方法
186 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true))) 186 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
187 .thenThrow(new RuntimeException("系统异常")); 187 .thenThrow(new RuntimeException("系统异常"));
188 // 准备请求参数 188 // 准备请求参数
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 { @@ -70,7 +70,7 @@ public class AlipayPcPayClientTest extends AbstractAlipayClientTest {
70 @Test 70 @Test
71 @DisplayName("支付宝 PC 网站支付:Form Display Mode 下单成功") 71 @DisplayName("支付宝 PC 网站支付:Form Display Mode 下单成功")
72 public void testUnifiedOrder_formSuccess() throws AlipayApiException { 72 public void testUnifiedOrder_formSuccess() throws AlipayApiException {
73 - // mock 73 + // mock 方法
74 String notifyUrl = randomURL(); 74 String notifyUrl = randomURL();
75 AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode("")); 75 AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode(""));
76 when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradePagePayRequest>) request -> true), 76 when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradePagePayRequest>) request -> true),
@@ -99,7 +99,7 @@ public class AlipayPcPayClientTest extends AbstractAlipayClientTest { @@ -99,7 +99,7 @@ public class AlipayPcPayClientTest extends AbstractAlipayClientTest {
99 @Test 99 @Test
100 @DisplayName("支付宝 PC 网站支付:渠道返回失败") 100 @DisplayName("支付宝 PC 网站支付:渠道返回失败")
101 public void testUnifiedOrder_channelFailed() throws AlipayApiException { 101 public void testUnifiedOrder_channelFailed() throws AlipayApiException {
102 - // mock 102 + // mock 方法
103 String subCode = randomString(); 103 String subCode = randomString();
104 String subMsg = randomString(); 104 String subMsg = randomString();
105 AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> { 105 AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> {
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 { @@ -39,9 +39,9 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
39 } 39 }
40 40
41 @Test 41 @Test
42 - @DisplayName("支付宝扫描支付下单成功")  
43 - public void test_unified_order_success() throws AlipayApiException {  
44 - // 准备返回对象 42 + @DisplayName("支付宝扫描支付:下单成功")
  43 + public void testUnifiedOrder_success() throws AlipayApiException {
  44 + // mock 方法
45 String notifyUrl = randomURL(); 45 String notifyUrl = randomURL();
46 String qrCode = randomString(); 46 String qrCode = randomString();
47 Integer price = randomInteger(); 47 Integer price = randomInteger();
@@ -49,7 +49,6 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest { @@ -49,7 +49,6 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
49 o.setQrCode(qrCode); 49 o.setQrCode(qrCode);
50 o.setSubCode(""); 50 o.setSubCode("");
51 }); 51 });
52 - // mock  
53 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> { 52 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
54 assertEquals(notifyUrl, request.getNotifyUrl()); 53 assertEquals(notifyUrl, request.getNotifyUrl());
55 return true; 54 return true;
@@ -58,18 +57,25 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest { @@ -58,18 +57,25 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
58 String outTradeNo = randomString(); 57 String outTradeNo = randomString();
59 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price); 58 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
60 59
  60 + // 调用
61 PayOrderRespDTO resp = client.unifiedOrder(reqDTO); 61 PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
62 // 断言 62 // 断言
63 assertEquals(WAITING.getStatus(), resp.getStatus()); 63 assertEquals(WAITING.getStatus(), resp.getStatus());
64 - assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());  
65 assertEquals(outTradeNo, resp.getOutTradeNo()); 64 assertEquals(outTradeNo, resp.getOutTradeNo());
66 - assertEquals(qrCode, resp.getDisplayContent()); 65 + assertNull(resp.getChannelOrderNo());
  66 + assertNull(resp.getChannelUserId());
  67 + assertNull(resp.getSuccessTime());
  68 + assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());
  69 + assertEquals(response.getQrCode(), resp.getDisplayContent());
67 assertSame(response, resp.getRawData()); 70 assertSame(response, resp.getRawData());
  71 + assertNull(resp.getChannelErrorCode());
  72 + assertNull(resp.getChannelErrorMsg());
68 } 73 }
69 74
70 @Test 75 @Test
71 - @DisplayName("支付宝扫描支付,渠道返回失败")  
72 - public void test_unified_order_channel_failed() throws AlipayApiException { 76 + @DisplayName("支付宝扫描支付:渠道返回失败")
  77 + public void testUnifiedOrder_channelFailed() throws AlipayApiException {
  78 + // mock 方法
73 String notifyUrl = randomURL(); 79 String notifyUrl = randomURL();
74 String subCode = randomString(); 80 String subCode = randomString();
75 String subMsg = randomString(); 81 String subMsg = randomString();
@@ -87,47 +93,55 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest { @@ -87,47 +93,55 @@ public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
87 String outTradeNo = randomString(); 93 String outTradeNo = randomString();
88 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price); 94 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
89 95
  96 + // 调用
90 PayOrderRespDTO resp = client.unifiedOrder(reqDTO); 97 PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
91 // 断言 98 // 断言
92 assertEquals(CLOSED.getStatus(), resp.getStatus()); 99 assertEquals(CLOSED.getStatus(), resp.getStatus());
  100 + assertEquals(outTradeNo, resp.getOutTradeNo());
  101 + assertNull(resp.getChannelOrderNo());
  102 + assertNull(resp.getChannelUserId());
  103 + assertNull(resp.getSuccessTime());
  104 + assertNull(resp.getDisplayMode());
  105 + assertNull(resp.getDisplayContent());
  106 + assertSame(response, resp.getRawData());
93 assertEquals(subCode, resp.getChannelErrorCode()); 107 assertEquals(subCode, resp.getChannelErrorCode());
94 assertEquals(subMsg, resp.getChannelErrorMsg()); 108 assertEquals(subMsg, resp.getChannelErrorMsg());
95 - assertSame(response, resp.getRawData());  
96 } 109 }
97 110
98 @Test 111 @Test
99 @DisplayName("支付宝扫描支付, 抛出系统异常") 112 @DisplayName("支付宝扫描支付, 抛出系统异常")
100 - public void test_unified_order_throw_pay_exception() throws AlipayApiException {  
101 - // 准备请求参数 113 + public void testUnifiedOrder_throwPayException() throws AlipayApiException {
  114 + // mock 方法
102 String outTradeNo = randomString(); 115 String outTradeNo = randomString();
103 String notifyUrl = randomURL(); 116 String notifyUrl = randomURL();
104 Integer price = randomInteger(); 117 Integer price = randomInteger();
105 - // mock  
106 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> { 118 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
107 assertEquals(notifyUrl, request.getNotifyUrl()); 119 assertEquals(notifyUrl, request.getNotifyUrl());
108 return true; 120 return true;
109 }))).thenThrow(new RuntimeException("系统异常")); 121 }))).thenThrow(new RuntimeException("系统异常"));
110 // 准备请求参数 122 // 准备请求参数
111 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo,price); 123 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo,price);
112 - // 断言 124 +
  125 + // 调用,并断言
113 assertThrows(PayException.class, () -> client.unifiedOrder(reqDTO)); 126 assertThrows(PayException.class, () -> client.unifiedOrder(reqDTO));
114 } 127 }
115 128
116 @Test 129 @Test
117 - @DisplayName("支付宝 Client 统一下单,抛出业务异常")  
118 - public void test_unified_order_throw_service_exception() throws AlipayApiException {  
119 - // 准备请求参数 130 + @DisplayName("支付宝 Client 统一下单:抛出业务异常")
  131 + public void testUnifiedOrder_throwServiceException() throws AlipayApiException {
  132 + // mock 方法
120 String outTradeNo = randomString(); 133 String outTradeNo = randomString();
121 String notifyUrl = randomURL(); 134 String notifyUrl = randomURL();
122 Integer price = randomInteger(); 135 Integer price = randomInteger();
123 - // mock  
124 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> { 136 when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
125 assertEquals(notifyUrl, request.getNotifyUrl()); 137 assertEquals(notifyUrl, request.getNotifyUrl());
126 return true; 138 return true;
127 }))).thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR)); 139 }))).thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
128 // 准备请求参数 140 // 准备请求参数
129 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price); 141 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
130 - // 断言 142 +
  143 + // 调用,并断言
131 assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO)); 144 assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
132 } 145 }
  146 +
133 } 147 }
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 { @@ -42,9 +42,9 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
42 } 42 }
43 43
44 @Test 44 @Test
45 - @DisplayName("支付宝 H5 支付下单成功")  
46 - public void test_unified_order_success() throws AlipayApiException {  
47 - // 准备响应对象 45 + @DisplayName("支付宝 H5 支付:下单成功")
  46 + public void testUnifiedOrder_success() throws AlipayApiException {
  47 + // mock 方法
48 String h5Body = randomString(); 48 String h5Body = randomString();
49 Integer price = randomInteger(); 49 Integer price = randomInteger();
50 AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> { 50 AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
@@ -52,7 +52,6 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest { @@ -52,7 +52,6 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
52 o.setBody(h5Body); 52 o.setBody(h5Body);
53 }); 53 });
54 String notifyUrl = randomURL(); 54 String notifyUrl = randomURL();
55 - // mock  
56 when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> { 55 when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> {
57 assertInstanceOf(AlipayTradeWapPayModel.class, request.getBizModel()); 56 assertInstanceOf(AlipayTradeWapPayModel.class, request.getBizModel());
58 AlipayTradeWapPayModel bizModel = (AlipayTradeWapPayModel) request.getBizModel(); 57 AlipayTradeWapPayModel bizModel = (AlipayTradeWapPayModel) request.getBizModel();
@@ -60,37 +59,53 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest { @@ -60,37 +59,53 @@ public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
60 assertEquals(notifyUrl, request.getNotifyUrl()); 59 assertEquals(notifyUrl, request.getNotifyUrl());
61 return true; 60 return true;
62 }), eq(Method.GET.name()))).thenReturn(response); 61 }), eq(Method.GET.name()))).thenReturn(response);
63 - 62 + // 准备请求参数
64 String outTradeNo = randomString(); 63 String outTradeNo = randomString();
65 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price); 64 PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
  65 +
  66 + // 调用
66 PayOrderRespDTO resp = client.unifiedOrder(reqDTO); 67 PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
  68 + // 断言
67 assertEquals(WAITING.getStatus(), resp.getStatus()); 69 assertEquals(WAITING.getStatus(), resp.getStatus());
68 - assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());  
69 assertEquals(outTradeNo, resp.getOutTradeNo()); 70 assertEquals(outTradeNo, resp.getOutTradeNo());
70 - assertEquals(h5Body, resp.getDisplayContent()); 71 + assertNull(resp.getChannelOrderNo());
  72 + assertNull(resp.getChannelUserId());
  73 + assertNull(resp.getSuccessTime());
  74 + assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
  75 + assertEquals(response.getBody(), resp.getDisplayContent());
71 assertSame(response, resp.getRawData()); 76 assertSame(response, resp.getRawData());
  77 + assertNull(resp.getChannelErrorCode());
  78 + assertNull(resp.getChannelErrorMsg());
72 } 79 }
73 80
74 @Test 81 @Test
75 - @DisplayName("支付宝 H5 支付,渠道返回失败") 82 + @DisplayName("支付宝 H5 支付:渠道返回失败")
76 public void test_unified_order_channel_failed() throws AlipayApiException { 83 public void test_unified_order_channel_failed() throws AlipayApiException {
77 - // 准备响应对象 84 + // mock 方法
78 String subCode = randomString(); 85 String subCode = randomString();
79 String subMsg = randomString(); 86 String subMsg = randomString();
80 AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> { 87 AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
81 o.setSubCode(subCode); 88 o.setSubCode(subCode);
82 o.setSubMsg(subMsg); 89 o.setSubMsg(subMsg);
83 }); 90 });
84 - // mock  
85 when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> true), 91 when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> true),
86 eq(Method.GET.name()))).thenReturn(response); 92 eq(Method.GET.name()))).thenReturn(response);
87 - PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger()); 93 + String outTradeNo = randomString();
  94 + PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
88 95
  96 + // 调用
89 PayOrderRespDTO resp = client.unifiedOrder(reqDTO); 97 PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
90 // 断言 98 // 断言
91 assertEquals(CLOSED.getStatus(), resp.getStatus()); 99 assertEquals(CLOSED.getStatus(), resp.getStatus());
  100 + assertEquals(outTradeNo, resp.getOutTradeNo());
  101 + assertNull(resp.getChannelOrderNo());
  102 + assertNull(resp.getChannelUserId());
  103 + assertNull(resp.getSuccessTime());
  104 + assertNull(resp.getDisplayMode());
  105 + assertNull(resp.getDisplayContent());
  106 + assertSame(response, resp.getRawData());
92 assertEquals(subCode, resp.getChannelErrorCode()); 107 assertEquals(subCode, resp.getChannelErrorCode());
93 assertEquals(subMsg, resp.getChannelErrorMsg()); 108 assertEquals(subMsg, resp.getChannelErrorMsg());
94 - assertSame(response, resp.getRawData());  
95 } 109 }
  110 +
96 } 111 }
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 { @@ -41,9 +41,9 @@ public interface ErrorCodeConstants {
41 ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); 41 ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
42 ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款"); 42 ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款");
43 43
44 - // ========== 钱包模块(退款) 1007007000 ========== 44 + // ========== 钱包模块 1007007000 ==========
45 ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在"); 45 ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在");
46 - ErrorCode WALLET_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足"); 46 + ErrorCode WALLET_BALANCE_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足");
47 ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易"); 47 ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易");
48 ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对"); 48 ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对");
49 ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款"); 49 ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款");
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/WalletBizTypeEnum.java renamed to 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; @@ -10,20 +10,22 @@ import lombok.Getter;
10 */ 10 */
11 @AllArgsConstructor 11 @AllArgsConstructor
12 @Getter 12 @Getter
13 -public enum WalletBizTypeEnum { 13 +public enum PayWalletBizTypeEnum {
  14 +
14 RECHARGE(1, "充值"), 15 RECHARGE(1, "充值"),
15 RECHARGE_REFUND(2, "充值退款"), 16 RECHARGE_REFUND(2, "充值退款"),
16 PAYMENT(3, "支付"), 17 PAYMENT(3, "支付"),
17 PAYMENT_REFUND(4, "支付退款"); 18 PAYMENT_REFUND(4, "支付退款");
18 19
19 // TODO 后续增加 20 // TODO 后续增加
  21 +
20 /** 22 /**
21 * 业务分类 23 * 业务分类
22 */ 24 */
23 - private final Integer bizType;  
24 - 25 + private final Integer type;
25 /** 26 /**
26 * 说明 27 * 说明
27 */ 28 */
28 - private final String desc; 29 + private final String description;
  30 +
29 } 31 }
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; @@ -7,20 +7,27 @@ import lombok.Getter;
7 7
8 import java.util.Arrays; 8 import java.util.Arrays;
9 9
  10 +// TODO @jason:可以简化,直接 PageVO 定义两个 Integer;
10 /** 11 /**
11 - * 钱包明细查询类型 12 + * 钱包流水查询类型
12 * 13 *
13 * @author jason 14 * @author jason
14 */ 15 */
15 @AllArgsConstructor 16 @AllArgsConstructor
16 @Getter 17 @Getter
17 public enum WalletTransactionQueryTypeEnum implements IntArrayValuable { 18 public enum WalletTransactionQueryTypeEnum implements IntArrayValuable {
  19 +
18 RECHARGE(1, "充值"), 20 RECHARGE(1, "充值"),
19 EXPENSE(2, "消费"); 21 EXPENSE(2, "消费");
20 22
  23 + /**
  24 + * 类型
  25 + */
21 private final Integer type; 26 private final Integer type;
22 -  
23 - private final String desc; 27 + /**
  28 + * 描述
  29 + */
  30 + private final String description;
24 31
25 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(WalletTransactionQueryTypeEnum::getType).toArray(); 32 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(WalletTransactionQueryTypeEnum::getType).toArray();
26 33
@@ -32,4 +39,5 @@ public enum WalletTransactionQueryTypeEnum implements IntArrayValuable { @@ -32,4 +39,5 @@ public enum WalletTransactionQueryTypeEnum implements IntArrayValuable {
32 public static WalletTransactionQueryTypeEnum valueOf(Integer type) { 39 public static WalletTransactionQueryTypeEnum valueOf(Integer type) {
33 return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); 40 return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
34 } 41 }
  42 +
35 } 43 }
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 { @@ -38,4 +38,5 @@ public class AppPayWalletController {
38 PayWalletDO payWallet = payWalletService.getPayWallet(getLoginUserId(), UserTypeEnum.MEMBER.getValue()); 38 PayWalletDO payWallet = payWalletService.getPayWallet(getLoginUserId(), UserTypeEnum.MEMBER.getValue());
39 return success(PayWalletConvert.INSTANCE.convert(payWallet)); 39 return success(PayWalletConvert.INSTANCE.convert(payWallet));
40 } 40 }
  41 +
41 } 42 }
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; @@ -6,11 +6,12 @@ import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum;
6 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
7 import lombok.Data; 7 import lombok.Data;
8 8
9 -@Schema(description = "用户 APP - 钱包余额明细分页 Request VO") 9 +@Schema(description = "用户 APP - 钱包流水分页 Request VO")
10 @Data 10 @Data
11 public class AppPayWalletTransactionPageReqVO extends PageParam { 11 public class AppPayWalletTransactionPageReqVO extends PageParam {
12 12
13 - @Schema(description = "余额明细查询分类", example = "1") 13 + @Schema(description = "流水查询分类", example = "1")
14 @InEnum(value = WalletTransactionQueryTypeEnum.class, message = "查询类型必须是 {value}") 14 @InEnum(value = WalletTransactionQueryTypeEnum.class, message = "查询类型必须是 {value}")
15 private Integer type; 15 private Integer type;
  16 +
16 } 17 }
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; @@ -5,7 +5,7 @@ import lombok.Data;
5 5
6 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
7 7
8 -@Schema(description = "用户 APP - 钱包余额明细分页 Response VO") 8 +@Schema(description = "用户 APP - 钱包流水分页 Response VO")
9 @Data 9 @Data
10 public class AppPayWalletTransactionRespVO { 10 public class AppPayWalletTransactionRespVO {
11 @Schema(description = "交易金额, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") 11 @Schema(description = "交易金额, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
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; @@ -8,7 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.Data; 8 import lombok.Data;
9 9
10 /** 10 /**
11 - * 支付 - 会员钱包 DO 11 + * 会员钱包 DO
12 * 12 *
13 * @author jason 13 * @author jason
14 */ 14 */
@@ -30,7 +30,6 @@ public class PayWalletDO extends BaseDO { @@ -30,7 +30,6 @@ public class PayWalletDO extends BaseDO {
30 * 关联 AdminUserDO 的 id 编号 30 * 关联 AdminUserDO 的 id 编号
31 */ 31 */
32 private Long userId; 32 private Long userId;
33 -  
34 /** 33 /**
35 * 用户类型, 预留 多商户转帐可能需要用到 34 * 用户类型, 预留 多商户转帐可能需要用到
36 * 35 *
@@ -39,17 +38,17 @@ public class PayWalletDO extends BaseDO { @@ -39,17 +38,17 @@ public class PayWalletDO extends BaseDO {
39 private Integer userType; 38 private Integer userType;
40 39
41 /** 40 /**
42 - * 余额, 单位分 41 + * 余额,单位分
43 */ 42 */
44 private Integer balance; 43 private Integer balance;
45 44
46 /** 45 /**
47 - * 累计支出, 单位分 46 + * 累计支出,单位分
48 */ 47 */
49 private Long totalExpense; 48 private Long totalExpense;
50 -  
51 /** 49 /**
52 - * 累计充值, 单位分 50 + * 累计充值,单位分
53 */ 51 */
54 private Long totalRecharge; 52 private Long totalRecharge;
  53 +
55 } 54 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletTransactionDO.java
1 package cn.iocoder.yudao.module.pay.dal.dataobject.wallet; 1 package cn.iocoder.yudao.module.pay.dal.dataobject.wallet;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 -import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum; 4 +import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
6 import com.baomidou.mybatisplus.annotation.TableId; 6 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 7 import com.baomidou.mybatisplus.annotation.TableName;
@@ -10,7 +10,7 @@ import lombok.Data; @@ -10,7 +10,7 @@ import lombok.Data;
10 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
11 11
12 /** 12 /**
13 - * 支付-会员钱包明细 DO 13 + * 会员钱包流水 DO
14 * 14 *
15 * @author jason 15 * @author jason
16 */ 16 */
@@ -24,47 +24,51 @@ public class PayWalletTransactionDO extends BaseDO { @@ -24,47 +24,51 @@ public class PayWalletTransactionDO extends BaseDO {
24 */ 24 */
25 @TableId 25 @TableId
26 private Long id; 26 private Long id;
  27 + /**
  28 + * 流水号
  29 + */
  30 + private String no;
27 31
28 /** 32 /**
29 - * 会员钱包 id 33 + * 钱包编号
  34 + *
30 * 关联 {@link PayWalletDO#getId()} 35 * 关联 {@link PayWalletDO#getId()}
31 */ 36 */
32 private Long walletId; 37 private Long walletId;
33 38
34 /** 39 /**
35 - * 钱包交易业务分类  
36 - * 关联枚举 {@link WalletBizTypeEnum#getBizType()} 40 + * 关联业务分类
  41 + *
  42 + * 枚举 {@link PayWalletBizTypeEnum#getType()}
37 */ 43 */
38 private Integer bizType; 44 private Integer bizType;
39 - 45 + // TODO @jason:使用 string;因为可能有业务是 string 接入哈。
40 /** 46 /**
41 * 关联业务编号 47 * 关联业务编号
42 */ 48 */
43 private Long bizId; 49 private Long bizId;
44 50
45 /** 51 /**
46 - * 流水号  
47 - */  
48 - private String no;  
49 -  
50 - /**  
51 * 附加说明 52 * 附加说明
52 */ 53 */
53 private String description; 54 private String description;
54 55
  56 + // TODO @jason:使用 price 哈。项目里,金额都是用这个为主;
55 /** 57 /**
56 - * 交易金额, 单位分  
57 - * 正值表示余额增加,负值表示余额减少 58 + * 交易金额,单位分
  59 + *
  60 + * 正值表示余额增加,负值表示余额减少
58 */ 61 */
59 private Integer amount; 62 private Integer amount;
60 -  
61 /** 63 /**
62 - * 交易后余额,单位分 64 + * 交易后余额,单位分
63 */ 65 */
64 private Integer balance; 66 private Integer balance;
65 67
  68 + // TODO @jason:使用 createTime 就够啦
66 /** 69 /**
67 * 交易时间 70 * 交易时间
68 */ 71 */
69 private LocalDateTime transactionTime; 72 private LocalDateTime transactionTime;
  73 +
70 } 74 }
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&lt;PayRefundDO&gt; { @@ -37,9 +37,8 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; {
37 .eq(PayRefundDO::getNo, no)); 37 .eq(PayRefundDO::getNo, no));
38 } 38 }
39 39
40 - default PayRefundDO selectByNo(String no){  
41 - return selectOne(new LambdaQueryWrapperX<PayRefundDO>()  
42 - .eq(PayRefundDO::getNo, no)); 40 + default PayRefundDO selectByNo(String no) {
  41 + return selectOne(PayRefundDO::getNo, no);
43 } 42 }
44 43
45 default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) { 44 default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) {
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&lt;PayWalletTransac @@ -23,7 +23,7 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac
23 if (WalletTransactionQueryTypeEnum.EXPENSE == queryType ) { 23 if (WalletTransactionQueryTypeEnum.EXPENSE == queryType ) {
24 query.lt(PayWalletTransactionDO::getAmount, 0); 24 query.lt(PayWalletTransactionDO::getAmount, 0);
25 } 25 }
26 - query.orderByDesc(PayWalletTransactionDO::getTransactionTime); 26 + query.orderByDesc(PayWalletTransactionDO::getId);
27 return selectPage(pageParam, query); 27 return selectPage(pageParam, query);
28 } 28 }
29 29
@@ -32,9 +32,11 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac @@ -32,9 +32,11 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac
32 } 32 }
33 33
34 default PayWalletTransactionDO selectByWalletIdAndBiz(Long walletId, Long bizId, Integer bizType) { 34 default PayWalletTransactionDO selectByWalletIdAndBiz(Long walletId, Long bizId, Integer bizType) {
35 - return selectOne(PayWalletTransactionDO::getWalletId, walletId, PayWalletTransactionDO::getBizId,  
36 - bizId, PayWalletTransactionDO::getBizType, bizType); 35 + return selectOne(PayWalletTransactionDO::getWalletId, walletId,
  36 + PayWalletTransactionDO::getBizId, bizId,
  37 + PayWalletTransactionDO::getBizType, bizType);
37 } 38 }
  39 +
38 } 40 }
39 41
40 42
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 @@ -22,29 +22,31 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
22 */ 22 */
23 @Slf4j 23 @Slf4j
24 public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> { 24 public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> {
  25 +
25 private PayWalletService payWalletService; 26 private PayWalletService payWalletService;
26 27
27 public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) { 28 public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) {
28 super(channelId, channelCode, config); 29 super(channelId, channelCode, config);
29 } 30 }
30 31
31 - public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config, PayWalletService payWalletService) { 32 + public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config,
  33 + PayWalletService payWalletService) {
32 this(channelId, channelCode, config); 34 this(channelId, channelCode, config);
33 this.payWalletService = payWalletService; 35 this.payWalletService = payWalletService;
34 } 36 }
35 37
36 @Override 38 @Override
37 protected void doInit() { 39 protected void doInit() {
38 - // 钱包支付 无需初始化 40 + // 钱包支付,无需初始化
39 } 41 }
40 42
41 @Override 43 @Override
42 protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { 44 protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
43 try { 45 try {
44 - PayWalletTransactionDO payWalletTransaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());  
45 - return PayOrderRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreator(),  
46 - payWalletTransaction.getTransactionTime(),  
47 - reqDTO.getOutTradeNo(), "WALLET_PAY_SUCCESS"); 46 + PayWalletTransactionDO transaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice());
  47 + return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
  48 + transaction.getTransactionTime(),
  49 + reqDTO.getOutTradeNo(), "WALLET_PAY_SUCCESS"); // TODO @jason:transaction 作为 traData 好了;
48 } catch (Throwable ex) { 50 } catch (Throwable ex) {
49 log.error("[doUnifiedOrder] 失败", ex); 51 log.error("[doUnifiedOrder] 失败", ex);
50 Integer errorCode = INTERNAL_SERVER_ERROR.getCode(); 52 Integer errorCode = INTERNAL_SERVER_ERROR.getCode();
@@ -99,4 +101,5 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; { @@ -99,4 +101,5 @@ public class WalletPayClient extends DelegatePayClient&lt;NonePayClientConfig&gt; {
99 protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) { 101 protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
100 throw new UnsupportedOperationException("待实现"); 102 throw new UnsupportedOperationException("待实现");
101 } 103 }
  104 +
102 } 105 }
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 { @@ -83,6 +83,7 @@ public class PayChannelServiceImpl implements PayChannelService {
83 log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size()); 83 log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
84 // 钱包 client 需要和其它 client 分开了创建 84 // 钱包 client 需要和其它 client 分开了创建
85 List<PayChannelDO> walletChannels = new ArrayList<>(); 85 List<PayChannelDO> walletChannels = new ArrayList<>();
  86 + // TODO @jason:有点复杂,看看用 PayClientInitializer 能不能简化
86 List<PayChannelDO> otherChannels = new ArrayList<>(); 87 List<PayChannelDO> otherChannels = new ArrayList<>();
87 channels.forEach(t -> { 88 channels.forEach(t -> {
88 if (PayChannelEnum.WALLET.getCode().equals(t.getCode())) { 89 if (PayChannelEnum.WALLET.getCode().equals(t.getCode())) {
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; @@ -10,15 +10,18 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
10 */ 10 */
11 public interface PayWalletService { 11 public interface PayWalletService {
12 12
  13 + // TODO @jason:改成 getOrCreateWallet;因为目前解耦,用户注册时,不会创建钱包;需要这里兜底处理;
13 /** 14 /**
14 * 获取钱包信息 15 * 获取钱包信息
15 - * @param userId 用户 id 16 + *
  17 + * @param userId 用户编号
16 * @param userType 用户类型 18 * @param userType 用户类型
17 */ 19 */
18 PayWalletDO getPayWallet(Long userId, Integer userType); 20 PayWalletDO getPayWallet(Long userId, Integer userType);
19 21
20 /** 22 /**
21 * 钱包支付 23 * 钱包支付
  24 + *
22 * @param outTradeNo 外部订单号 25 * @param outTradeNo 外部订单号
23 * @param price 金额 26 * @param price 金额
24 */ 27 */
@@ -26,9 +29,11 @@ public interface PayWalletService { @@ -26,9 +29,11 @@ public interface PayWalletService {
26 29
27 /** 30 /**
28 * 钱包支付退款 31 * 钱包支付退款
  32 + *
29 * @param outRefundNo 外部退款号 33 * @param outRefundNo 外部退款号
30 * @param refundPrice 退款金额 34 * @param refundPrice 退款金额
31 * @param reason 退款原因 35 * @param reason 退款原因
32 */ 36 */
33 PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason); 37 PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason);
  38 +
34 } 39 }
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 @@ -20,8 +20,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
20 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; 20 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
21 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; 21 import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType;
22 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; 22 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
23 -import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT;  
24 -import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT_REFUND; 23 +import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
  24 +import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT_REFUND;
25 25
26 /** 26 /**
27 * 钱包 Service 实现类 27 * 钱包 Service 实现类
@@ -31,14 +31,23 @@ import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT @@ -31,14 +31,23 @@ import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT
31 @Service 31 @Service
32 @Slf4j 32 @Slf4j
33 public class PayWalletServiceImpl implements PayWalletService { 33 public class PayWalletServiceImpl implements PayWalletService {
  34 +
  35 + /**
  36 + * 余额支付的 no 前缀
  37 + */
34 private static final String WALLET_PAY_NO_PREFIX = "WP"; 38 private static final String WALLET_PAY_NO_PREFIX = "WP";
  39 + /**
  40 + * 余额退款的 no 前缀
  41 + */
35 private static final String WALLET_REFUND_NO_PREFIX = "WR"; 42 private static final String WALLET_REFUND_NO_PREFIX = "WR";
  43 +
36 @Resource 44 @Resource
37 private PayWalletMapper payWalletMapper; 45 private PayWalletMapper payWalletMapper;
38 @Resource 46 @Resource
39 - private PayWalletTransactionService payWalletTransactionService;  
40 - @Resource  
41 private PayNoRedisDAO noRedisDAO; 47 private PayNoRedisDAO noRedisDAO;
  48 +
  49 + @Resource
  50 + private PayWalletTransactionService payWalletTransactionService;
42 @Resource 51 @Resource
43 @Lazy 52 @Lazy
44 private PayOrderService payOrderService; 53 private PayOrderService payOrderService;
@@ -51,34 +60,39 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -51,34 +60,39 @@ public class PayWalletServiceImpl implements PayWalletService {
51 return payWalletMapper.selectByUserIdAndType(userId, userType); 60 return payWalletMapper.selectByUserIdAndType(userId, userType);
52 } 61 }
53 62
  63 + // TODO @jason:可以做的更抽象一点;pay(bizType, bizId, price);reduceWalletBalance;
  64 + // TODO @jason:最好是,明确传入哪个 userId 或者 walletId;
54 @Override 65 @Override
55 @Transactional(rollbackFor = Exception.class) 66 @Transactional(rollbackFor = Exception.class)
56 public PayWalletTransactionDO pay(String outTradeNo, Integer price) { 67 public PayWalletTransactionDO pay(String outTradeNo, Integer price) {
57 - // 判断支付交易拓展单是否存在 68 + // 1.1 判断支付交易拓展单是否存
58 PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo); 69 PayOrderExtensionDO orderExtension = payOrderService.getOrderExtensionByNo(outTradeNo);
59 if (orderExtension == null) { 70 if (orderExtension == null) {
60 throw exception(ORDER_EXTENSION_NOT_FOUND); 71 throw exception(ORDER_EXTENSION_NOT_FOUND);
61 } 72 }
  73 + // 1.2 判断余额是否足够
62 PayWalletDO payWallet = validatePayWallet(); 74 PayWalletDO payWallet = validatePayWallet();
63 - // 判断余额是否足够  
64 int afterBalance = payWallet.getBalance() - price; 75 int afterBalance = payWallet.getBalance() - price;
65 - if(afterBalance < 0){  
66 - throw exception(WALLET_NOT_ENOUGH); 76 + if (afterBalance < 0) {
  77 + throw exception(WALLET_BALANCE_NOT_ENOUGH);
67 } 78 }
  79 +
  80 + // 2.1 扣除余额
  81 + // TODO @jason:不要直接整个更新;而是 new 一个出来更新;然后要考虑并发,要 where 余额 > price,以及 - price
68 payWallet.setBalance(afterBalance); 82 payWallet.setBalance(afterBalance);
69 payWallet.setTotalExpense(payWallet.getTotalExpense() + price); 83 payWallet.setTotalExpense(payWallet.getTotalExpense() + price);
70 payWalletMapper.updateById(payWallet); 84 payWalletMapper.updateById(payWallet);
71 85
72 - // 生成钱包渠道流水号 86 + // 2.2 生成钱包流水
73 String walletNo = noRedisDAO.generate(WALLET_PAY_NO_PREFIX); 87 String walletNo = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
74 - PayWalletTransactionDO payWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId()) 88 + PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
75 .setNo(walletNo).setAmount(price * -1).setBalance(afterBalance).setTransactionTime(LocalDateTime.now()) 89 .setNo(walletNo).setAmount(price * -1).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
76 - .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getBizType());  
77 - payWalletTransactionService.addPayWalletTransaction(payWalletTransaction);  
78 -  
79 - return payWalletTransaction; 90 + .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getType());
  91 + payWalletTransactionService.createWalletTransaction(walletTransaction);
  92 + return walletTransaction;
80 } 93 }
81 94
  95 + // TODO @jason:不要在 service 里去使用用户上下文,这样和 request 就耦合了。
82 private PayWalletDO validatePayWallet() { 96 private PayWalletDO validatePayWallet() {
83 Long userId = getLoginUserId(); 97 Long userId = getLoginUserId();
84 Integer userType = getLoginUserType(); 98 Integer userType = getLoginUserType();
@@ -90,53 +104,58 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -90,53 +104,58 @@ public class PayWalletServiceImpl implements PayWalletService {
90 return payWallet; 104 return payWallet;
91 } 105 }
92 106
  107 + // TODO @jason:可以做的更抽象一点;pay(bizType, bizId, price);addWalletBalance;这样,如果后续充值,应该也是能复用这个方法的;
93 @Override 108 @Override
94 @Transactional(rollbackFor = Exception.class) 109 @Transactional(rollbackFor = Exception.class)
95 public PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason) { 110 public PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason) {
96 - // 判断退款单是否存在 111 + // 1.1 判断退款单是否存在
97 PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo); 112 PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo);
98 if (payRefund == null) { 113 if (payRefund == null) {
99 throw exception(REFUND_NOT_FOUND); 114 throw exception(REFUND_NOT_FOUND);
100 } 115 }
  116 + // 1.2 校验是否可以退款
101 PayWalletDO payWallet = validatePayWallet(); 117 PayWalletDO payWallet = validatePayWallet();
102 - // 校验是否可以退款  
103 validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), payWallet.getId(), refundPrice); 118 validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), payWallet.getId(), refundPrice);
104 119
  120 + // TODO @jason:不要直接整个更新;而是 new 一个出来更新;然后要考虑并发,要 where 余额 + 金额
105 Integer afterBalance = payWallet.getBalance() + refundPrice; 121 Integer afterBalance = payWallet.getBalance() + refundPrice;
106 payWallet.setBalance(afterBalance); 122 payWallet.setBalance(afterBalance);
107 payWallet.setTotalExpense(payWallet.getTotalExpense() + refundPrice * -1L); 123 payWallet.setTotalExpense(payWallet.getTotalExpense() + refundPrice * -1L);
108 payWalletMapper.updateById(payWallet); 124 payWalletMapper.updateById(payWallet);
109 125
  126 + // 2.2 生成钱包流水
110 String walletNo = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX); 127 String walletNo = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
111 PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId()) 128 PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
112 .setNo(walletNo).setAmount(refundPrice).setBalance(afterBalance).setTransactionTime(LocalDateTime.now()) 129 .setNo(walletNo).setAmount(refundPrice).setBalance(afterBalance).setTransactionTime(LocalDateTime.now())
113 - .setBizId(payRefund.getId()).setBizType(PAYMENT_REFUND.getBizType()) 130 + .setBizId(payRefund.getId()).setBizType(PAYMENT_REFUND.getType())
114 .setDescription(reason); 131 .setDescription(reason);
115 - payWalletTransactionService.addPayWalletTransaction(newWalletTransaction);  
116 - 132 + payWalletTransactionService.createWalletTransaction(newWalletTransaction);
117 return newWalletTransaction; 133 return newWalletTransaction;
118 } 134 }
119 135
120 /** 136 /**
121 * 校验是否能退款 137 * 校验是否能退款
  138 + *
122 * @param refundId 支付退款单 id 139 * @param refundId 支付退款单 id
123 * @param walletPayNo 钱包支付 no 140 * @param walletPayNo 钱包支付 no
124 * @param walletId 钱包 id 141 * @param walletId 钱包 id
125 */ 142 */
  143 + // TODO @jason:不要使用基本类型;
126 private void validateWalletCanRefund(long refundId, String walletPayNo, long walletId, int refundPrice) { 144 private void validateWalletCanRefund(long refundId, String walletPayNo, long walletId, int refundPrice) {
127 // 查询钱包支付交易 145 // 查询钱包支付交易
128 - PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getPayWalletTransactionByNo(walletPayNo); 146 + PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getWalletTransactionByNo(walletPayNo);
129 if (payWalletTransaction == null) { 147 if (payWalletTransaction == null) {
130 throw exception(WALLET_TRANSACTION_NOT_FOUND); 148 throw exception(WALLET_TRANSACTION_NOT_FOUND);
131 } 149 }
132 // 原来的支付金额 150 // 原来的支付金额
133 - int amount = payWalletTransaction.getAmount() * -1; 151 + int amount = payWalletTransaction.getAmount() * -1; // TODO @jason:直接 - payWalletTransaction.getAmount() 即可;
134 if (refundPrice != amount) { 152 if (refundPrice != amount) {
135 throw exception(WALLET_REFUND_AMOUNT_ERROR); 153 throw exception(WALLET_REFUND_AMOUNT_ERROR);
136 } 154 }
137 - PayWalletTransactionDO refundTransaction = payWalletTransactionService.getPayWalletTransaction(walletId, refundId, PAYMENT_REFUND); 155 + PayWalletTransactionDO refundTransaction = payWalletTransactionService.getWalletTransaction(walletId, refundId, PAYMENT_REFUND);
138 if (refundTransaction != null) { 156 if (refundTransaction != null) {
139 throw exception(WALLET_REFUND_EXIST); 157 throw exception(WALLET_REFUND_EXIST);
140 } 158 }
141 } 159 }
  160 +
142 } 161 }
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; @@ -3,19 +3,19 @@ package cn.iocoder.yudao.module.pay.service.wallet;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransactionPageReqVO; 4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransactionPageReqVO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
6 -import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum; 6 +import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
7 7
8 /** 8 /**
9 - * 钱包余额明细 Service 接口 9 + * 钱包余额流水 Service 接口
10 * 10 *
11 * @author jason 11 * @author jason
12 */ 12 */
13 public interface PayWalletTransactionService { 13 public interface PayWalletTransactionService {
14 14
15 /** 15 /**
16 - * 查询钱包余额明细, 分页 16 + * 查询钱包余额流水分页
17 * 17 *
18 - * @param userId 用户 id 18 + * @param userId 用户编号
19 * @param userType 用户类型 19 * @param userType 用户类型
20 * @param pageVO 分页查询参数 20 * @param pageVO 分页查询参数
21 */ 21 */
@@ -23,24 +23,28 @@ public interface PayWalletTransactionService { @@ -23,24 +23,28 @@ public interface PayWalletTransactionService {
23 AppPayWalletTransactionPageReqVO pageVO); 23 AppPayWalletTransactionPageReqVO pageVO);
24 24
25 /** 25 /**
26 - * 新增钱包余额明细  
27 - * @param payWalletTransaction 余额明细 26 + * 新增钱包余额流水
  27 + *
  28 + * @param payWalletTransaction 余额流水
28 * @return id 29 * @return id
29 */ 30 */
30 - Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction); 31 + Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction);
31 32
32 /** 33 /**
33 - * 获取钱包余额明细 根据 no 34 + * 根据 no,获取钱包余流水
  35 + *
34 * @param no 流水号 36 * @param no 流水号
35 */ 37 */
36 - PayWalletTransactionDO getPayWalletTransactionByNo(String no); 38 + PayWalletTransactionDO getWalletTransactionByNo(String no);
37 39
38 /** 40 /**
39 - * 获取钱包  
40 - * @param walletId 钱包 id 41 + * 获取钱包流水
  42 + *
  43 + * @param walletId 钱包编号
41 * @param bizId 业务编号 44 * @param bizId 业务编号
42 - * @param typeEnum 业务类型  
43 - * @return 钱包余额明细 45 + * @param type 业务类型
  46 + * @return 钱包流水
44 */ 47 */
45 - PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum); 48 + PayWalletTransactionDO getWalletTransaction(Long walletId, Long bizId, PayWalletBizTypeEnum type);
  49 +
46 } 50 }
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 @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransact
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
7 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper; 7 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper;
8 -import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum; 8 +import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
9 import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum; 9 import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum;
10 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
11 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
@@ -16,45 +16,47 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -16,45 +16,47 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
16 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.WALLET_NOT_FOUND; 16 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.WALLET_NOT_FOUND;
17 17
18 /** 18 /**
19 - * 钱包余额明细 Service 实现类 19 + * 钱包流水 Service 实现类
20 * 20 *
21 * @author jason 21 * @author jason
22 */ 22 */
23 @Service 23 @Service
24 @Slf4j 24 @Slf4j
25 public class PayWalletTransactionServiceImpl implements PayWalletTransactionService { 25 public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
  26 +
26 @Resource 27 @Resource
27 private PayWalletService payWalletService; 28 private PayWalletService payWalletService;
  29 +
28 @Resource 30 @Resource
29 private PayWalletTransactionMapper payWalletTransactionMapper; 31 private PayWalletTransactionMapper payWalletTransactionMapper;
30 32
31 @Override 33 @Override
32 public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType, 34 public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
33 AppPayWalletTransactionPageReqVO pageVO) { 35 AppPayWalletTransactionPageReqVO pageVO) {
34 - PayWalletDO payWallet = payWalletService.getPayWallet(userId, userType);  
35 - if (payWallet == null) { 36 + PayWalletDO wallet = payWalletService.getPayWallet(userId, userType);
  37 + if (wallet == null) {
36 log.error("[pageWalletTransaction] 用户 {} 钱包不存在", userId); 38 log.error("[pageWalletTransaction] 用户 {} 钱包不存在", userId);
37 throw exception(WALLET_NOT_FOUND); 39 throw exception(WALLET_NOT_FOUND);
38 } 40 }
39 - return payWalletTransactionMapper.selectPageByWalletIdAndQueryType(payWallet.getId(), 41 + // TODO @jason:不用 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()) 哈,直接 pageVO 里面判断值比对就好啦;
  42 + return payWalletTransactionMapper.selectPageByWalletIdAndQueryType(wallet.getId(),
40 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()), pageVO); 43 WalletTransactionQueryTypeEnum.valueOf(pageVO.getType()), pageVO);
41 } 44 }
42 45
43 @Override 46 @Override
44 - public Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction) { 47 + public Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
45 payWalletTransactionMapper.insert(payWalletTransaction); 48 payWalletTransactionMapper.insert(payWalletTransaction);
46 return payWalletTransaction.getId(); 49 return payWalletTransaction.getId();
47 } 50 }
48 51
49 @Override 52 @Override
50 - public PayWalletTransactionDO getPayWalletTransactionByNo(String no) { 53 + public PayWalletTransactionDO getWalletTransactionByNo(String no) {
51 return payWalletTransactionMapper.selectByNo(no); 54 return payWalletTransactionMapper.selectByNo(no);
52 } 55 }
53 56
54 @Override 57 @Override
55 - public PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum) {  
56 - return payWalletTransactionMapper.selectByWalletIdAndBiz(walletId, bizId, typeEnum.getBizType()); 58 + public PayWalletTransactionDO getWalletTransaction(Long walletId, Long bizId, PayWalletBizTypeEnum type) {
  59 + return payWalletTransactionMapper.selectByWalletIdAndBiz(walletId, bizId, type.getType());
57 } 60 }
58 61
59 -  
60 } 62 }