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