Commit a73e078593b05c205c3c068ab0b66e87b8d8e73c

Authored by puhui999
2 parents 25980760 29d61beb

Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

# Conflicts:
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
Showing 94 changed files with 2583 additions and 2314 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java
... ... @@ -12,7 +12,6 @@ import org.springframework.context.annotation.Bean;
12 12 * @author 芋道源码
13 13 */
14 14 @AutoConfiguration
15   -@EnableConfigurationProperties(PayProperties.class)
16 15 public class YudaoPayAutoConfiguration {
17 16  
18 17 @Bean
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java
... ... @@ -79,14 +79,16 @@ public class PayOrderRespDTO {
79 79 /**
80 80 * 创建【WAITING】状态的订单返回
81 81 */
82   - public PayOrderRespDTO(String displayMode, String displayContent,
83   - String outTradeNo, Object rawData) {
84   - this.status = PayOrderStatusRespEnum.WAITING.getStatus();
85   - this.displayMode = displayMode;
86   - this.displayContent = displayContent;
  82 + public static PayOrderRespDTO waitingOf(String displayMode, String displayContent,
  83 + String outTradeNo, Object rawData) {
  84 + PayOrderRespDTO respDTO = new PayOrderRespDTO();
  85 + respDTO.status = PayOrderStatusRespEnum.WAITING.getStatus();
  86 + respDTO.displayMode = displayMode;
  87 + respDTO.displayContent = displayContent;
87 88 // 相对通用的字段
88   - this.outTradeNo = outTradeNo;
89   - this.rawData = rawData;
  89 + respDTO.outTradeNo = outTradeNo;
  90 + respDTO.rawData = rawData;
  91 + return respDTO;
90 92 }
91 93  
92 94 /**
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java
1 1 package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
2 2  
  3 +import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
3 4 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
4 5 import lombok.Data;
5 6  
... ... @@ -44,4 +45,71 @@ public class PayRefundRespDTO {
44 45 */
45 46 private Object rawData;
46 47  
  48 + /**
  49 + * 调用渠道的错误码
  50 + *
  51 + * 注意:这里返回的是业务异常,而是不系统异常。
  52 + * 如果是系统异常,则会抛出 {@link PayException}
  53 + */
  54 + private String channelErrorCode;
  55 + /**
  56 + * 调用渠道报错时,错误信息
  57 + */
  58 + private String channelErrorMsg;
  59 +
  60 + private PayRefundRespDTO() {
  61 + }
  62 +
  63 + /**
  64 + * 创建【WAITING】状态的退款返回
  65 + */
  66 + public static PayRefundRespDTO waitingOf(String channelRefundNo,
  67 + String outRefundNo, Object rawData) {
  68 + PayRefundRespDTO respDTO = new PayRefundRespDTO();
  69 + respDTO.status = PayRefundStatusRespEnum.WAITING.getStatus();
  70 + respDTO.channelRefundNo = channelRefundNo;
  71 + // 相对通用的字段
  72 + respDTO.outRefundNo = outRefundNo;
  73 + respDTO.rawData = rawData;
  74 + return respDTO;
  75 + }
  76 +
  77 + /**
  78 + * 创建【SUCCESS】状态的退款返回
  79 + */
  80 + public static PayRefundRespDTO successOf(String channelRefundNo, LocalDateTime successTime,
  81 + String outRefundNo, Object rawData) {
  82 + PayRefundRespDTO respDTO = new PayRefundRespDTO();
  83 + respDTO.status = PayRefundStatusRespEnum.SUCCESS.getStatus();
  84 + respDTO.channelRefundNo = channelRefundNo;
  85 + respDTO.successTime = successTime;
  86 + // 相对通用的字段
  87 + respDTO.outRefundNo = outRefundNo;
  88 + respDTO.rawData = rawData;
  89 + return respDTO;
  90 + }
  91 +
  92 + /**
  93 + * 创建【FAILURE】状态的退款返回
  94 + */
  95 + public static PayRefundRespDTO failureOf(String outRefundNo, Object rawData) {
  96 + return failureOf(null, null,
  97 + outRefundNo, rawData);
  98 + }
  99 +
  100 + /**
  101 + * 创建【FAILURE】状态的退款返回
  102 + */
  103 + public static PayRefundRespDTO failureOf(String channelErrorCode, String channelErrorMsg,
  104 + String outRefundNo, Object rawData) {
  105 + PayRefundRespDTO respDTO = new PayRefundRespDTO();
  106 + respDTO.status = PayRefundStatusRespEnum.FAILURE.getStatus();
  107 + respDTO.channelErrorCode = channelErrorCode;
  108 + respDTO.channelErrorMsg = channelErrorMsg;
  109 + // 相对通用的字段
  110 + respDTO.outRefundNo = outRefundNo;
  111 + respDTO.rawData = rawData;
  112 + return respDTO;
  113 + }
  114 +
47 115 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -98,7 +98,8 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
98 98 try {
99 99 return doParseOrderNotify(params, body);
100 100 } catch (Throwable ex) {
101   - log.error("[parseOrderNotify][params({}) body({}) 解析失败]", params, body, ex);
  101 + log.error("[parseOrderNotify][客户端({}) params({}) body({}) 解析失败]",
  102 + getId(), params, body, ex);
102 103 throw buildPayException(ex);
103 104 }
104 105 }
... ... @@ -129,6 +130,20 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
129 130  
130 131 protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
131 132  
  133 + @Override
  134 + public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
  135 + try {
  136 + return doParseRefundNotify(params, body);
  137 + } catch (Throwable ex) {
  138 + log.error("[parseRefundNotify][客户端({}) params({}) body({}) 解析失败]",
  139 + getId(), params, body, ex);
  140 + throw buildPayException(ex);
  141 + }
  142 + }
  143 +
  144 + protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body)
  145 + throws Throwable;
  146 +
132 147 // ========== 各种工具方法 ==========
133 148  
134 149 private PayException buildPayException(Throwable ex) {
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
... ... @@ -3,8 +3,10 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 5 import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.map.MapUtil;
6 7 import cn.hutool.core.util.StrUtil;
7 8 import cn.hutool.http.HttpUtil;
  9 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
8 10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
9 11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
10 12 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
... ... @@ -29,7 +31,6 @@ import java.util.Objects;
29 31 import java.util.function.Supplier;
30 32  
31 33 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
32   -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
33 34  
34 35 /**
35 36 * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
... ... @@ -74,10 +75,15 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
74 75 StandardCharsets.UTF_8.name(), config.getSignType());
75 76  
76 77 // 2. 解析订单的状态
  78 + // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂
77 79 String tradeStatus = bodyObj.get("trade_status");
78 80 Integer status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
79   - : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS.getStatus()
  81 + : ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
80 82 : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
  83 + // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功
  84 + if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
  85 + status = PayOrderStatusRespEnum.REFUND.getStatus();
  86 + }
81 87 Assert.notNull(status, (Supplier<Throwable>) () -> {
82 88 throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
83 89 });
... ... @@ -94,7 +100,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
94 100 * @return 退款请求 Response
95 101 */
96 102 @Override
97   - protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  103 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws AlipayApiException {
98 104 // 1.1 构建 AlipayTradeRefundModel 请求
99 105 AlipayTradeRefundModel model = new AlipayTradeRefundModel();
100 106 model.setOutTradeNo(reqDTO.getOutTradeNo());
... ... @@ -104,31 +110,22 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
104 110 // 1.2 构建 AlipayTradePayRequest 请求
105 111 AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
106 112 request.setBizModel(model);
107   - try {
108   - // 2.1 执行请求
109   - AlipayTradeRefundResponse response = client.execute(request);
110   - // 2.2 创建返回结果
111   - PayRefundRespDTO refund = new PayRefundRespDTO()
112   - .setOutRefundNo(reqDTO.getOutRefundNo())
113   - .setRawData(response);
114   - // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
115   - // 另外,支付宝没有退款单号,所以不用设置
116   - if (response.isSuccess()) {
117   - refund.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
118   - .setSuccessTime(LocalDateTimeUtil.of(response.getGmtRefundPay()));
119   - Assert.notNull(refund.getSuccessTime(), "退款成功时间不能为空");
120   - } else {
121   - refund.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus());
122   - }
123   - return refund;
124   - } catch (AlipayApiException e) {
125   - log.error("[doUnifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), e);
126   - return null;
  113 +
  114 + // 2.1 执行请求
  115 + AlipayTradeRefundResponse response = client.execute(request);
  116 + // 2.2 创建返回结果
  117 + // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
  118 + // 另外,支付宝没有退款单号,所以不用设置
  119 + if (response.isSuccess()) {
  120 + return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),
  121 + reqDTO.getOutRefundNo(), response);
  122 + } else {
  123 + return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
127 124 }
128 125 }
129 126  
130 127 @Override
131   - public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
  128 + public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) {
132 129 // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
133 130 // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
134 131 // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
... ... @@ -52,7 +52,7 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
52 52 if (!response.isSuccess()) {
53 53 return buildClosedPayOrderRespDTO(reqDTO, response);
54 54 }
55   - return new PayOrderRespDTO(displayMode, "",
  55 + return PayOrderRespDTO.waitingOf(displayMode, "",
56 56 reqDTO.getOutTradeNo(), response);
57 57 }
58 58  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
... ... @@ -62,7 +62,7 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
62 62 if (!response.isSuccess()) {
63 63 return buildClosedPayOrderRespDTO(reqDTO, response);
64 64 }
65   - return new PayOrderRespDTO(displayMode, "",
  65 + return PayOrderRespDTO.waitingOf(displayMode, "",
66 66 reqDTO.getOutTradeNo(), response);
67 67 }
68 68  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
... ... @@ -63,7 +63,7 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
63 63 if (!response.isSuccess()) {
64 64 return buildClosedPayOrderRespDTO(reqDTO, response);
65 65 }
66   - return new PayOrderRespDTO(displayMode, response.getBody(),
  66 + return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
67 67 reqDTO.getOutTradeNo(), response);
68 68 }
69 69  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
... ... @@ -50,7 +50,7 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
50 50 if (!response.isSuccess()) {
51 51 return buildClosedPayOrderRespDTO(reqDTO, response);
52 52 }
53   - return new PayOrderRespDTO(displayMode, response.getQrCode(),
  53 + return PayOrderRespDTO.waitingOf(displayMode, response.getQrCode(),
54 54 reqDTO.getOutTradeNo(), response);
55 55 }
56 56  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
... ... @@ -52,7 +52,7 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
52 52 if (!response.isSuccess()) {
53 53 return buildClosedPayOrderRespDTO(reqDTO, response);
54 54 }
55   - return new PayOrderRespDTO(displayMode, response.getBody(),
  55 + return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
56 56 reqDTO.getOutTradeNo(), response);
57 57 }
58 58  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
... ... @@ -12,7 +12,6 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
12 12 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
13 13 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
14 14 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
15   -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
16 15 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
17 16 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
18 17 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
... ... @@ -131,6 +130,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
131 130 // 1. 解析回调
132 131 WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
133 132 // 2. 构建结果
  133 + // 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂
134 134 Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
135 135 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
136 136 return new PayOrderRespDTO(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
... ... @@ -142,6 +142,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
142 142 WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null);
143 143 WxPayOrderNotifyV3Result.DecryptNotifyResult result = response.getResult();
144 144 // 2. 构建结果
  145 + // 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂
145 146 Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
146 147 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
147 148 String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
... ... @@ -163,9 +164,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
163 164 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
164 165 }
165 166 } catch (WxPayException e) {
166   - // todo 芋艿:异常的处理;
167   -// throw buildUnifiedOrderException(null, e);
168   - return null;
  167 + String errorCode = getErrorCode(e);
  168 + String errorMessage = getErrorMessage(e);
  169 + return PayRefundRespDTO.failureOf(errorCode, errorMessage,
  170 + reqDTO.getOutTradeNo(), e.getXmlString());
169 171 }
170 172 }
171 173  
... ... @@ -181,17 +183,11 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
181 183 // 2.1 执行请求
182 184 WxPayRefundResult response = client.refundV2(request);
183 185 // 2.2 创建返回结果
184   - PayRefundRespDTO refund = new PayRefundRespDTO()
185   - .setOutRefundNo(reqDTO.getOutRefundNo())
186   - .setRawData(response);
187 186 if (Objects.equals("SUCCESS", response.getResultCode())) {
188   - refund.setStatus(PayRefundStatusRespEnum.WAITING.getStatus())
189   - .setChannelRefundNo(response.getRefundId());
190   - } else {
191   - refund.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
  187 + return PayRefundRespDTO.waitingOf(response.getRefundId(),
  188 + reqDTO.getOutRefundNo(), response);
192 189 }
193   - // TODO 芋艿;异常的处理;
194   - return refund;
  190 + return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
195 191 }
196 192  
197 193 private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
... ... @@ -206,78 +202,51 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
206 202 // 2.1 执行请求
207 203 WxPayRefundV3Result response = client.refundV3(request);
208 204 // 2.2 创建返回结果
209   - PayRefundRespDTO refund = new PayRefundRespDTO()
210   - .setOutRefundNo(reqDTO.getOutRefundNo())
211   - .setRawData(response);
212 205 if (Objects.equals("SUCCESS", response.getStatus())) {
213   - refund.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus())
214   - .setChannelRefundNo(response.getRefundId())
215   - .setSuccessTime(parseDateV3(response.getSuccessTime()));
216   - } else if (Objects.equals("PROCESSING", response.getStatus())) {
217   - refund.setStatus(PayRefundStatusRespEnum.WAITING.getStatus())
218   - .setChannelRefundNo(response.getRefundId());
219   - } else {
220   - refund.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
  206 + return PayRefundRespDTO.successOf(response.getRefundId(), parseDateV3(response.getSuccessTime()),
  207 + reqDTO.getOutRefundNo(), response);
221 208 }
222   - // TODO 芋艿;异常的处理;
223   - return refund;
  209 + if (Objects.equals("PROCESSING", response.getStatus())) {
  210 + return PayRefundRespDTO.waitingOf(response.getRefundId(),
  211 + reqDTO.getOutRefundNo(), response);
  212 + }
  213 + return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
224 214 }
225 215  
226 216 @Override
227   - public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
228   - try {
229   - // 微信支付 v2 回调结果处理
230   - switch (config.getApiVersion()) {
231   - case API_VERSION_V2:
232   - return parseRefundNotifyV2(body);
233   - case WxPayClientConfig.API_VERSION_V3:
234   - return parseRefundNotifyV3(body);
235   - default:
236   - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
237   - }
238   - } catch (WxPayException e) {
239   - log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
240   - throw new RuntimeException(e);
241   - // TODO 芋艿:缺一个异常翻译
  217 + public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) throws WxPayException {
  218 + switch (config.getApiVersion()) {
  219 + case API_VERSION_V2:
  220 + return doParseRefundNotifyV2(body);
  221 + case WxPayClientConfig.API_VERSION_V3:
  222 + return parseRefundNotifyV3(body);
  223 + default:
  224 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
242 225 }
243 226 }
244 227  
245   - @SuppressWarnings("DuplicatedCode")
246   - private PayRefundRespDTO parseRefundNotifyV2(String body) throws WxPayException {
  228 + private PayRefundRespDTO doParseRefundNotifyV2(String body) throws WxPayException {
247 229 // 1. 解析回调
248 230 WxPayRefundNotifyResult response = client.parseRefundNotifyResult(body);
249   - WxPayRefundNotifyResult.ReqInfo responseResult = response.getReqInfo();
  231 + WxPayRefundNotifyResult.ReqInfo result = response.getReqInfo();
250 232 // 2. 构建结果
251   - PayRefundRespDTO notify = new PayRefundRespDTO()
252   - .setChannelRefundNo(responseResult.getRefundId())
253   - .setOutRefundNo(responseResult.getOutRefundNo())
254   - .setRawData(response);
255   - if (Objects.equals("SUCCESS", responseResult.getRefundStatus())) {
256   - notify.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus())
257   - .setSuccessTime(parseDateV2B(responseResult.getSuccessTime()));
258   - } else {
259   - notify.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
  233 + if (Objects.equals("SUCCESS", result.getRefundStatus())) {
  234 + return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV2B(result.getSuccessTime()),
  235 + result.getOutRefundNo(), response);
260 236 }
261   - return notify;
  237 + return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
262 238 }
263 239  
264   - @SuppressWarnings("DuplicatedCode")
265 240 private PayRefundRespDTO parseRefundNotifyV3(String body) throws WxPayException {
266 241 // 1. 解析回调
267 242 WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, null);
268   - WxPayRefundNotifyV3Result.DecryptNotifyResult responseResult = response.getResult();
  243 + WxPayRefundNotifyV3Result.DecryptNotifyResult result = response.getResult();
269 244 // 2. 构建结果
270   - PayRefundRespDTO notify = new PayRefundRespDTO()
271   - .setChannelRefundNo(responseResult.getRefundId())
272   - .setOutRefundNo(responseResult.getOutRefundNo())
273   - .setRawData(response);
274   - if (Objects.equals("SUCCESS", responseResult.getRefundStatus())) {
275   - notify.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus())
276   - .setSuccessTime(parseDateV3(responseResult.getSuccessTime()));
277   - } else {
278   - notify.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
  245 + if (Objects.equals("SUCCESS", result.getRefundStatus())) {
  246 + return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV3(result.getSuccessTime()),
  247 + result.getOutRefundNo(), response);
279 248 }
280   - return notify;
  249 + return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
281 250 }
282 251  
283 252 // ========== 各种工具方法 ==========
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
... ... @@ -67,7 +67,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
67 67 for (int i = 1; i < Byte.MAX_VALUE; i++) {
68 68 try {
69 69 WxPayMicropayResult response = client.micropay(request);
70   - // 支付成功,例如说:1)用户输入了密码;2)
  70 + // 支付成功,例如说:1)用户输入了密码;2)用户免密支付
71 71 return new PayOrderRespDTO(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
72 72 response.getOutTradeNo(), response)
73 73 .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
... ... @@ -48,7 +48,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
48 48 WxPayNativeOrderResult response = client.createOrder(request);
49 49  
50 50 // 转换结果
51   - return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(),
  51 + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(),
52 52 reqDTO.getOutTradeNo(), response);
53 53 }
54 54  
... ... @@ -66,7 +66,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
66 66 String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
67 67  
68 68 // 转换结果
69   - return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response,
  69 + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), response,
70 70 reqDTO.getOutTradeNo(), response);
71 71 }
72 72  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
... ... @@ -58,7 +58,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
58 58 WxPayMpOrderResult response = client.createOrder(request);
59 59  
60 60 // 转换结果
61   - return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
  61 + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
62 62 reqDTO.getOutTradeNo(), response);
63 63 }
64 64  
... ... @@ -77,7 +77,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
77 77 WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request);
78 78  
79 79 // 转换结果
80   - return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
  80 + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
81 81 reqDTO.getOutTradeNo(), response);
82 82 }
83 83  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java
... ... @@ -16,7 +16,8 @@ public enum PayOrderStatusRespEnum {
16 16  
17 17 WAITING(0, "未支付"),
18 18 SUCCESS(10, "支付成功"),
19   - CLOSED(20, "支付关闭"), // 未付款交易超时关闭,或支付完成后全额退款
  19 + REFUND(20, "已退款"),
  20 + CLOSED(30, "支付关闭"),
20 21 ;
21 22  
22 23 private final Integer status;
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java
... ... @@ -14,7 +14,7 @@ import java.util.Objects;
14 14 @AllArgsConstructor
15 15 public enum PayRefundStatusRespEnum {
16 16  
17   - WAITING(0, "未退款"),
  17 + WAITING(0, "等待退款"),
18 18 SUCCESS(10, "退款成功"),
19 19 FAILURE(20, "退款失败");
20 20  
... ... @@ -25,4 +25,8 @@ public enum PayRefundStatusRespEnum {
25 25 return Objects.equals(status, SUCCESS.getStatus());
26 26 }
27 27  
  28 + public static boolean isFailure(Integer status) {
  29 + return Objects.equals(status, FAILURE.getStatus());
  30 + }
  31 +
28 32 }
... ...
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.excel.core.convert;
  2 +
  3 +import com.alibaba.excel.converters.Converter;
  4 +import com.alibaba.excel.enums.CellDataTypeEnum;
  5 +import com.alibaba.excel.metadata.GlobalConfiguration;
  6 +import com.alibaba.excel.metadata.data.WriteCellData;
  7 +import com.alibaba.excel.metadata.property.ExcelContentProperty;
  8 +
  9 +import java.math.BigDecimal;
  10 +import java.math.RoundingMode;
  11 +
  12 +/**
  13 + * 金额转换器
  14 + *
  15 + * 金额单位:分
  16 + *
  17 + * @author 芋道源码
  18 + */
  19 +public class MoneyConvert implements Converter<Integer> {
  20 +
  21 + @Override
  22 + public Class<?> supportJavaTypeKey() {
  23 + throw new UnsupportedOperationException("暂不支持,也不需要");
  24 + }
  25 +
  26 + @Override
  27 + public CellDataTypeEnum supportExcelTypeKey() {
  28 + throw new UnsupportedOperationException("暂不支持,也不需要");
  29 + }
  30 +
  31 + @Override
  32 + public WriteCellData<String> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
  33 + GlobalConfiguration globalConfiguration) {
  34 + BigDecimal result = BigDecimal.valueOf(value)
  35 + .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
  36 + return new WriteCellData<>(result.toString());
  37 + }
  38 +
  39 +}
... ...
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
... ... @@ -55,6 +55,9 @@ public class RandomUtils {
55 55 }
56 56 return RandomUtil.randomInt();
57 57 });
  58 + // LocalDateTime
  59 + PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class,
  60 + (dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime());
58 61 // Boolean
59 62 PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> {
60 63 // 如果是 deleted 的字段,返回非删除
... ... @@ -82,7 +85,8 @@ public class RandomUtils {
82 85 }
83 86  
84 87 public static LocalDateTime randomLocalDateTime() {
85   - return LocalDateTimeUtil.of(randomDate());
  88 + // 设置 Nano 为零的原因,避免 MySQL、H2 存储不到时间戳
  89 + return LocalDateTimeUtil.of(randomDate()).withNano(0);
86 90 }
87 91  
88 92 public static Short randomShort() {
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -63,6 +63,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
63 63 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
64 64 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
65 65 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND;
  66 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND;
66 67 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
67 68  
68 69 /**
... ... @@ -344,7 +345,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
344 345 PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
345 346 if (payOrder == null) {
346 347 log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
347   - throw exception(PAY_ORDER_NOT_FOUND);
  348 + throw exception(cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND);
348 349 }
349 350 // 校验支付单已支付
350 351 if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/DictTypeConstants.java
... ... @@ -7,13 +7,12 @@ package cn.iocoder.yudao.module.pay.enums;
7 7 */
8 8 public interface DictTypeConstants {
9 9  
10   - String CHANNEL_CODE = "pay_channel_code"; // 支付-渠道名
  10 + String CHANNEL_CODE = "pay_channel_code"; // 支付渠道编码
11 11  
12   - String ORDER_STATUS = "pay_order_status"; // 支付-订单-订单状态
13   - String ORDER_NOTIFY_STATUS = "pay_order_notify_status"; // 支付-订单-订单回调商户状态
  12 + String ORDER_STATUS = "pay_order_status"; // 支付渠道
14 13  
15   - String ORDER_REFUND_STATUS = "pay_order_refund_status"; // 支付-订单-订单退款状态
16   - String REFUND_ORDER_STATUS = "pay_refund_order_status"; // 支付-退款订单-退款状态
17   - String REFUND_ORDER_TYPE = "pay_refund_order_type"; // 支付-退款订单-退款类别
  14 + String REFUND_STATUS = "pay_order_status"; // 退款状态
  15 +
  16 + String NOTIFY_STATUS = "pay_notify_status"; // 回调状态
18 17  
19 18 }
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
... ... @@ -10,48 +10,46 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
10 10 public interface ErrorCodeConstants {
11 11  
12 12 // ========== APP 模块 1007000000 ==========
13   - ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
14   - ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
15   - ErrorCode PAY_APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1007000003, "支付应用存在支付订单,无法删除");
16   - ErrorCode PAY_APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1007000004, "支付应用存在退款订单,无法删除");
  13 + ErrorCode APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
  14 + ErrorCode APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
  15 + ErrorCode APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1007000003, "支付应用存在支付订单,无法删除");
  16 + ErrorCode APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1007000004, "支付应用存在退款订单,无法删除");
17 17  
18 18 // ========== CHANNEL 模块 1007001000 ==========
19   - ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
20   - ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用");
21   - ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在");
22   - ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在");
23   - ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道");
  19 + ErrorCode CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
  20 + ErrorCode CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用");
  21 + ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001004, "已存在相同的渠道");
24 22  
25 23 // ========== ORDER 模块 1007002000 ==========
26   - ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在");
27   - ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
28   - ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
29   - ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期");
30   - ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}");
  24 + ErrorCode ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在");
  25 + ErrorCode ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
  26 + ErrorCode ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
  27 + ErrorCode ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期");
  28 + ErrorCode ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}");
  29 + ErrorCode ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1007002005, "支付订单退款失败,原因:状态不是已支付或已退款");
31 30  
32 31 // ========== ORDER 模块(拓展单) 1007003000 ==========
33   - ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
34   - ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
  32 + ErrorCode ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
  33 + ErrorCode ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
35 34  
36 35 // ========== 支付模块(退款) 1007006000 ==========
37   - ErrorCode PAY_REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
38   - ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款");
39   - ErrorCode PAY_REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中");
40   - ErrorCode PAY_REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单");
41   - ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
42   - ErrorCode PAY_REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款");
  36 + ErrorCode REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
  37 + ErrorCode REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中");
  38 + ErrorCode REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单");
  39 + ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
  40 + ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款");
43 41  
44 42 // ========== 示例订单 1007900000 ==========
45   - ErrorCode PAY_DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");
46   - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态");
47   - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1007900002, "示例订单更新支付状态失败,支付单编号不匹配");
48   - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007900003, "示例订单更新支付状态失败,支付单状态不是【支付成功】状态");
49   - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1007900004, "示例订单更新支付状态失败,支付单金额不匹配");
50   - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_NOT_PAID = new ErrorCode(1007900005, "发起退款失败,示例订单未支付");
51   - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUNDED = new ErrorCode(1007900006, "发起退款失败,示例订单已退款");
52   - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007900007, "发起退款失败,退款订单不存在");
53   - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS = new ErrorCode(1007900008, "发起退款失败,退款订单未退款成功");
54   - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007900009, "发起退款失败,退款单编号不匹配");
55   - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007900010, "发起退款失败,退款单金额不匹配");
  43 + ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");
  44 + ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态");
  45 + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1007900002, "示例订单更新支付状态失败,支付单编号不匹配");
  46 + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007900003, "示例订单更新支付状态失败,支付单状态不是【支付成功】状态");
  47 + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1007900004, "示例订单更新支付状态失败,支付单金额不匹配");
  48 + ErrorCode DEMO_ORDER_REFUND_FAIL_NOT_PAID = new ErrorCode(1007900005, "发起退款失败,示例订单未支付");
  49 + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUNDED = new ErrorCode(1007900006, "发起退款失败,示例订单已退款");
  50 + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007900007, "发起退款失败,退款订单不存在");
  51 + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS = new ErrorCode(1007900008, "发起退款失败,退款订单未退款成功");
  52 + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007900009, "发起退款失败,退款单编号不匹配");
  53 + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007900010, "发起退款失败,退款单金额不匹配");
56 54  
57 55 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java renamed to yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java
... ... @@ -12,11 +12,11 @@ import lombok.Getter;
12 12 @AllArgsConstructor
13 13 public enum PayNotifyStatusEnum {
14 14  
15   - WAITING(1, "等待通知"),
16   - SUCCESS(2, "通知成功"),
17   - FAILURE(3, "通知失败"), // 多次尝试,彻底失败
18   - REQUEST_SUCCESS(4, "请求成功,但是结果失败"),
19   - REQUEST_FAILURE(5, "请求失败"),
  15 + WAITING(0, "等待通知"),
  16 + SUCCESS(10, "通知成功"),
  17 + FAILURE(20, "通知失败"), // 多次尝试,彻底失败
  18 + REQUEST_SUCCESS(21, "请求成功,但是结果失败"),
  19 + REQUEST_FAILURE(22, "请求失败"),
20 20  
21 21 ;
22 22  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java renamed to yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java
1 1 package cn.iocoder.yudao.module.pay.enums.order;
2 2  
3 3 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
4 5 import lombok.AllArgsConstructor;
5 6 import lombok.Getter;
6 7  
... ... @@ -17,7 +18,8 @@ public enum PayOrderStatusEnum implements IntArrayValuable {
17 18  
18 19 WAITING(0, "未支付"),
19 20 SUCCESS(10, "支付成功"),
20   - CLOSED(20, "支付关闭"), // 未付款交易超时关闭,或支付完成后全额退款
  21 + REFUND(20, "已退款"),
  22 + CLOSED(30, "支付关闭"), // 注意:全部退款后,还是 REFUND 状态
21 23 ;
22 24  
23 25 private final Integer status;
... ... @@ -39,6 +41,17 @@ public enum PayOrderStatusEnum implements IntArrayValuable {
39 41 }
40 42  
41 43 /**
  44 + * 判断是否支付成功或者已退款
  45 + *
  46 + * @param status 状态
  47 + * @return 是否支付成功或者已退款
  48 + */
  49 + public static boolean isSuccessOrRefund(Integer status) {
  50 + return ObjectUtils.equalsAny(status,
  51 + SUCCESS.getStatus(), REFUND.getStatus());
  52 + }
  53 +
  54 + /**
42 55 * 判断是否支付关闭
43 56 *
44 57 * @param status 状态
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java
... ... @@ -25,4 +25,8 @@ public enum PayRefundStatusEnum {
25 25 return Objects.equals(status, SUCCESS.getStatus());
26 26 }
27 27  
  28 + public static boolean isFailure(Integer status) {
  29 + return Objects.equals(status, FAILURE.getStatus());
  30 + }
  31 +
28 32 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/PayAppController.java
... ... @@ -97,4 +97,12 @@ public class PayAppController {
97 97 return success(PayAppConvert.INSTANCE.convertPage(pageResult, channels));
98 98 }
99 99  
  100 + @GetMapping("/list")
  101 + @Operation(summary = "获得应用列表")
  102 + @PreAuthorize("@ss.hasPermission('pay:merchant:query')")
  103 + public CommonResult<List<PayAppRespVO>> getAppList() {
  104 + List<PayAppDO> appListDO = appService.getAppList();
  105 + return success(PayAppConvert.INSTANCE.convertList(appListDO));
  106 + }
  107 +
100 108 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.notify;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 5 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
4 6 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 7 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
6 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
7 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  10 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
  11 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
  12 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
  13 +import cn.iocoder.yudao.module.pay.convert.notify.PayNotifyTaskConvert;
  14 +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
  15 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
  16 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
  17 +import cn.iocoder.yudao.module.pay.service.app.PayAppService;
  18 +import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
8 19 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
9 20 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
10 21 import io.swagger.v3.oas.annotations.Operation;
  22 +import io.swagger.v3.oas.annotations.Parameter;
11 23 import io.swagger.v3.oas.annotations.tags.Tag;
12 24 import lombok.extern.slf4j.Slf4j;
  25 +import org.springframework.security.access.prepost.PreAuthorize;
13 26 import org.springframework.validation.annotation.Validated;
14 27 import org.springframework.web.bind.annotation.*;
15 28  
16 29 import javax.annotation.Resource;
17 30 import javax.annotation.security.PermitAll;
  31 +import javax.validation.Valid;
  32 +import java.util.List;
18 33 import java.util.Map;
19 34  
20 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21   -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND;
  36 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  37 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  38 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_FOUND;
22 39  
23   -@Tag(name = "管理后台 - 支付通知")
  40 +@Tag(name = "管理后台 - 回调通知")
24 41 @RestController
25 42 @RequestMapping("/pay/notify")
26 43 @Validated
... ... @@ -31,6 +48,10 @@ public class PayNotifyController {
31 48 private PayOrderService orderService;
32 49 @Resource
33 50 private PayRefundService refundService;
  51 + @Resource
  52 + private PayNotifyService notifyService;
  53 + @Resource
  54 + private PayAppService appService;
34 55  
35 56 @Resource
36 57 private PayClientFactory payClientFactory;
... ... @@ -47,7 +68,7 @@ public class PayNotifyController {
47 68 PayClient payClient = payClientFactory.getPayClient(channelId);
48 69 if (payClient == null) {
49 70 log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
50   - throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
  71 + throw exception(CHANNEL_NOT_FOUND);
51 72 }
52 73  
53 74 // 2. 解析通知数据
... ... @@ -68,7 +89,7 @@ public class PayNotifyController {
68 89 PayClient payClient = payClientFactory.getPayClient(channelId);
69 90 if (payClient == null) {
70 91 log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
71   - throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
  92 + throw exception(CHANNEL_NOT_FOUND);
72 93 }
73 94  
74 95 // 2. 解析通知数据
... ... @@ -77,4 +98,29 @@ public class PayNotifyController {
77 98 return "success";
78 99 }
79 100  
  101 + @GetMapping("/get-detail")
  102 + @Operation(summary = "获得回调通知的明细")
  103 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  104 + @PreAuthorize("@ss.hasPermission('pay:notify:query')")
  105 + public CommonResult<PayNotifyTaskDetailRespVO> getNotifyTaskDetail(@RequestParam("id") Long id) {
  106 + PayNotifyTaskDO task = notifyService.getNotifyTask(id);
  107 + if (task == null) {
  108 + return success(null);
  109 + }
  110 + // 拼接返回
  111 + PayAppDO app = appService.getApp(task.getAppId());
  112 + List<PayNotifyLogDO> logs = notifyService.getNotifyLogList(id);
  113 + return success(PayNotifyTaskConvert.INSTANCE.convert(task, app, logs));
  114 + }
  115 +
  116 + @GetMapping("/page")
  117 + @Operation(summary = "获得回调通知分页")
  118 + @PreAuthorize("@ss.hasPermission('pay:notify:query')")
  119 + public CommonResult<PageResult<PayNotifyTaskRespVO>> getNotifyTaskPage(@Valid PayNotifyTaskPageReqVO pageVO) {
  120 + PageResult<PayNotifyTaskDO> pageResult = notifyService.getNotifyTaskPage(pageVO);
  121 + // 拼接返回
  122 + Map<Long, PayAppDO> appMap = appService.getAppMap(convertList(pageResult.getList(), PayNotifyTaskDO::getAppId));
  123 + return success(PayNotifyTaskConvert.INSTANCE.convertPage(pageResult, appMap));
  124 + }
  125 +
80 126 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 回调通知 Base VO,提供给添加、修改、详细的子 VO 使用
  10 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  11 + */
  12 +@Data
  13 +public class PayNotifyTaskBaseVO {
  14 +
  15 + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10636")
  16 + private Long appId;
  17 +
  18 + @Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  19 + private Byte type;
  20 +
  21 + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6722")
  22 + private Long dataId;
  23 +
  24 + @Schema(description = "通知状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  25 + private Byte status;
  26 +
  27 + @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26697")
  28 + private String merchantOrderId;
  29 +
  30 + @Schema(description = "下一次通知时间", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + private LocalDateTime nextNotifyTime;
  32 +
  33 + @Schema(description = "最后一次执行时间", requiredMode = Schema.RequiredMode.REQUIRED)
  34 + private LocalDateTime lastExecuteTime;
  35 +
  36 + @Schema(description = "当前通知次数", requiredMode = Schema.RequiredMode.REQUIRED)
  37 + private Byte notifyTimes;
  38 +
  39 + @Schema(description = "最大可通知次数", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + private Byte maxNotifyTimes;
  41 +
  42 + @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")
  43 + private String notifyUrl;
  44 +
  45 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java 0 → 100644
  1 +
  2 +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo;
  3 +
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.time.LocalDateTime;
  10 +import java.util.List;
  11 +
  12 +@Schema(description = "管理后台 - 回调通知的明细 Response VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class PayNotifyTaskDetailRespVO extends PayNotifyTaskBaseVO {
  17 +
  18 + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380")
  19 + private Long id;
  20 +
  21 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private LocalDateTime createTime;
  23 +
  24 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + private LocalDateTime updateTime;
  26 +
  27 + @Schema(description = "应用名称", example = "wx_pay")
  28 + private String appName;
  29 +
  30 + @Schema(description = "回调日志列表")
  31 + private List<Log> logs;
  32 +
  33 + @Schema(description = "管理后台 - 回调日志")
  34 + @Data
  35 + public static class Log {
  36 +
  37 + @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8848")
  38 + private Long id;
  39 +
  40 + @Schema(description = "通知状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  41 + private Byte status;
  42 +
  43 + @Schema(description = "当前通知次数", requiredMode = Schema.RequiredMode.REQUIRED)
  44 + private Byte notifyTimes;
  45 +
  46 + @Schema(description = "HTTP 响应结果", requiredMode = Schema.RequiredMode.REQUIRED)
  47 + private String response;
  48 +
  49 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  50 + private LocalDateTime createTime;
  51 +
  52 + }
  53 +
  54 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 回调通知分页 Request VO")
  15 +@Data
  16 +@EqualsAndHashCode(callSuper = true)
  17 +@ToString(callSuper = true)
  18 +public class PayNotifyTaskPageReqVO extends PageParam {
  19 +
  20 + @Schema(description = "应用编号", example = "10636")
  21 + private Long appId;
  22 +
  23 + @Schema(description = "通知类型", example = "2")
  24 + private Byte type;
  25 +
  26 + @Schema(description = "数据编号", example = "6722")
  27 + private Long dataId;
  28 +
  29 + @Schema(description = "通知状态", example = "1")
  30 + private Byte status;
  31 +
  32 + @Schema(description = "商户订单编号", example = "26697")
  33 + private String merchantOrderId;
  34 +
  35 + @Schema(description = "创建时间")
  36 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  37 + private LocalDateTime[] createTime;
  38 +
  39 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundRespVO.java renamed to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java 100755 → 100644
1   -package cn.iocoder.yudao.module.pay.controller.admin.refund.vo;
  1 +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4   -import lombok.Data;
5   -import lombok.EqualsAndHashCode;
6   -import lombok.ToString;
7   -
  4 +import lombok.*;
8 5 import java.time.LocalDateTime;
9 6  
10   -@Schema(description = "管理后台 - 退款订单 Response VO")
  7 +@Schema(description = "管理后台 - 回调通知 Response VO")
11 8 @Data
12 9 @EqualsAndHashCode(callSuper = true)
13 10 @ToString(callSuper = true)
14   -public class PayRefundRespVO extends PayRefundBaseVO {
  11 +public class PayNotifyTaskRespVO extends PayNotifyTaskBaseVO {
15 12  
16   - @Schema(description = "支付退款编号", requiredMode = Schema.RequiredMode.REQUIRED)
  13 + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380")
17 14 private Long id;
18 15  
19 16 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
20 17 private LocalDateTime createTime;
21 18  
  19 + @Schema(description = "应用名称", example = "wx_pay")
  20 + private String appName;
  21 +
22 22 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.order;
2 2  
3 3 import cn.hutool.core.collection.CollectionUtil;
4   -import cn.hutool.core.util.ObjectUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 6 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 7 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10   -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
11 8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
12 9 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
13 10 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
14 11 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
15 12 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
16 13 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
17   -import cn.iocoder.yudao.module.pay.service.order.PayOrderExtensionService;
18 14 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
19 15 import io.swagger.v3.oas.annotations.Operation;
20 16 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -32,6 +28,7 @@ import java.util.List;
32 28 import java.util.Map;
33 29  
34 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
35 32 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
36 33 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
37 34  
... ... @@ -42,9 +39,7 @@ import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.E
42 39 public class PayOrderController {
43 40  
44 41 @Resource
45   - private PayOrderService payOrderService;
46   - @Resource
47   - private PayOrderExtensionService orderExtensionService;
  42 + private PayOrderService orderService;
48 43 @Resource
49 44 private PayAppService appService;
50 45  
... ... @@ -53,40 +48,29 @@ public class PayOrderController {
53 48 @Parameter(name = "id", description = "编号", required = true, example = "1024")
54 49 @PreAuthorize("@ss.hasPermission('pay:order:query')")
55 50 public CommonResult<PayOrderRespVO> getOrder(@RequestParam("id") Long id) {
56   - return success(PayOrderConvert.INSTANCE.convert(payOrderService.getOrder(id)));
  51 + return success(PayOrderConvert.INSTANCE.convert(orderService.getOrder(id)));
57 52 }
58 53  
59   - // TODO 芋艿:看看怎么优化下;
60 54 @GetMapping("/get-detail")
61 55 @Operation(summary = "获得支付订单详情")
62 56 @Parameter(name = "id", description = "编号", required = true, example = "1024")
63 57 @PreAuthorize("@ss.hasPermission('pay:order:query')")
64 58 public CommonResult<PayOrderDetailsRespVO> getOrderDetail(@RequestParam("id") Long id) {
65   - PayOrderDO order = payOrderService.getOrder(id);
66   - if (ObjectUtil.isNull(order)) {
67   - return success(new PayOrderDetailsRespVO());
68   - }
69   -
70   - PayAppDO appDO = appService.getApp(order.getAppId());
71   - PayChannelEnum channelEnum = PayChannelEnum.getByCode(order.getChannelCode());
72   -
73   - // TODO @aquan:文案,都是前端 format;
74   - PayOrderDetailsRespVO respVO = PayOrderConvert.INSTANCE.orderDetailConvert(order);
75   - respVO.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
76   - respVO.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
77   -
78   - PayOrderExtensionDO extensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
79   - if (ObjectUtil.isNotNull(extensionDO)) {
80   - respVO.setPayOrderExtension(PayOrderConvert.INSTANCE.orderDetailExtensionConvert(extensionDO));
  59 + PayOrderDO order = orderService.getOrder(id);
  60 + if (order == null) {
  61 + return success(null);
81 62 }
82 63  
83   - return success(respVO);
  64 + // 拼接返回
  65 + PayAppDO app = appService.getApp(order.getAppId());
  66 + PayOrderExtensionDO orderExtension = orderService.getOrderExtension(order.getExtensionId());
  67 + return success(PayOrderConvert.INSTANCE.convert(order, orderExtension, app));
84 68 }
85 69  
86 70 @PostMapping("/submit")
87 71 @Operation(summary = "提交支付订单")
88 72 public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
89   - PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP());
  73 + PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP());
90 74 return success(respVO);
91 75 }
92 76  
... ... @@ -94,62 +78,34 @@ public class PayOrderController {
94 78 @Operation(summary = "获得支付订单分页")
95 79 @PreAuthorize("@ss.hasPermission('pay:order:query')")
96 80 public CommonResult<PageResult<PayOrderPageItemRespVO>> getOrderPage(@Valid PayOrderPageReqVO pageVO) {
97   - PageResult<PayOrderDO> pageResult = payOrderService.getOrderPage(pageVO);
  81 + PageResult<PayOrderDO> pageResult = orderService.getOrderPage(pageVO);
98 82 if (CollectionUtil.isEmpty(pageResult.getList())) {
99 83 return success(new PageResult<>(pageResult.getTotal()));
100 84 }
101 85  
102   - // 处理应用ID数据
103   - Map<Long, PayAppDO> appMap = appService.getAppMap(
104   - CollectionUtils.convertList(pageResult.getList(), PayOrderDO::getAppId));
105   -
106   - List<PayOrderPageItemRespVO> pageList = new ArrayList<>(pageResult.getList().size());
107   - pageResult.getList().forEach(c -> {
108   - PayAppDO appDO = appMap.get(c.getAppId());
109   - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode());
110   -
111   - PayOrderPageItemRespVO orderItem = PayOrderConvert.INSTANCE.pageConvertItemPage(c);
112   - orderItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
113   - orderItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
114   - pageList.add(orderItem);
115   - });
116   - return success(new PageResult<>(pageList, pageResult.getTotal()));
  86 + // 拼接返回
  87 + Map<Long, PayAppDO> appMap = appService.getAppMap(convertList(pageResult.getList(), PayOrderDO::getAppId));
  88 + return success(PayOrderConvert.INSTANCE.convertPage(pageResult, appMap));
117 89 }
118 90  
119 91 @GetMapping("/export-excel")
120   - @Operation(summary = "导出支付订单Excel")
  92 + @Operation(summary = "导出支付订单 Excel")
121 93 @PreAuthorize("@ss.hasPermission('pay:order:export')")
122 94 @OperateLog(type = EXPORT)
123 95 public void exportOrderExcel(@Valid PayOrderExportReqVO exportReqVO,
124 96 HttpServletResponse response) throws IOException {
125   - List<PayOrderDO> list = payOrderService.getOrderList(exportReqVO);
  97 + List<PayOrderDO> list = orderService.getOrderList(exportReqVO);
126 98 if (CollectionUtil.isEmpty(list)) {
127 99 ExcelUtils.write(response, "支付订单.xls", "数据",
128 100 PayOrderExcelVO.class, new ArrayList<>());
  101 + return;
129 102 }
130 103  
131   - // 处理应用ID数据
132   - Map<Long, PayAppDO> appMap = appService.getAppMap(
133   - CollectionUtils.convertList(list, PayOrderDO::getAppId));
134   - // 处理扩展订单数据
135   - Map<Long, PayOrderExtensionDO> orderExtensionMap = orderExtensionService
136   - .getOrderExtensionMap(CollectionUtils.convertList(list, PayOrderDO::getSuccessExtensionId));
137   -
138   - List<PayOrderExcelVO> excelDatum = new ArrayList<>(list.size());
139   - list.forEach(c -> {
140   - PayAppDO appDO = appMap.get(c.getAppId());
141   - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode());
142   - PayOrderExtensionDO orderExtensionDO = orderExtensionMap.get(c.getSuccessExtensionId());
143   -
144   - PayOrderExcelVO excelItem = PayOrderConvert.INSTANCE.excelConvert(c);
145   - excelItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
146   - excelItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
147   - excelItem.setNo(ObjectUtil.isNotNull(orderExtensionDO) ? orderExtensionDO.getNo() : "");
148   - excelDatum.add(excelItem);
149   - });
150   -
  104 + // 拼接返回
  105 + Map<Long, PayAppDO> appMap = appService.getAppMap(convertList(list, PayOrderDO::getAppId));
  106 + List<PayOrderExcelVO> excelList = PayOrderConvert.INSTANCE.convertList(list, appMap);
151 107 // 导出 Excel
152   - ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, excelDatum);
  108 + ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, excelList);
153 109 }
154 110  
155 111 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderBaseVO.java
... ... @@ -17,51 +17,47 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 17 @Data
18 18 public class PayOrderBaseVO {
19 19  
20   - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
21 21 @NotNull(message = "应用编号不能为空")
22 22 private Long appId;
23 23  
24   - @Schema(description = "渠道编号")
  24 + @Schema(description = "渠道编号", example = "2048")
25 25 private Long channelId;
26 26  
27   - @Schema(description = "渠道编码")
  27 + @Schema(description = "渠道编码", example = "wx_app")
28 28 private String channelCode;
29 29  
30   - @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED)
  30 + @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
31 31 @NotNull(message = "商户订单编号不能为空")
32 32 private String merchantOrderId;
33 33  
34   - @Schema(description = "商品标题", requiredMode = Schema.RequiredMode.REQUIRED)
  34 + @Schema(description = "商品标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆")
35 35 @NotNull(message = "商品标题不能为空")
36 36 private String subject;
37 37  
38   - @Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + @Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是土豆")
39 39 @NotNull(message = "商品描述不能为空")
40 40 private String body;
41 41  
42   - @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED)
  42 + @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://127.0.0.1:48080/pay/notify")
43 43 @NotNull(message = "异步通知地址不能为空")
44 44 private String notifyUrl;
45 45  
46   - @Schema(description = "通知商户支付结果的回调状态", requiredMode = Schema.RequiredMode.REQUIRED)
47   - @NotNull(message = "通知商户支付结果的回调状态不能为空")
48   - private Integer notifyStatus;
49   -
50   - @Schema(description = "支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
  46 + @Schema(description = "支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
51 47 @NotNull(message = "支付金额,单位:分不能为空")
52 48 private Long price;
53 49  
54   - @Schema(description = "渠道手续费,单位:百分比")
  50 + @Schema(description = "渠道手续费,单位:百分比", example = "10")
55 51 private Double channelFeeRate;
56 52  
57   - @Schema(description = "渠道手续金额,单位:分")
58   - private Long channelFeePrice;
  53 + @Schema(description = "渠道手续金额,单位:分", example = "100")
  54 + private Integer channelFeePrice;
59 55  
60   - @Schema(description = "支付状态", requiredMode = Schema.RequiredMode.REQUIRED)
  56 + @Schema(description = "支付状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
61 57 @NotNull(message = "支付状态不能为空")
62 58 private Integer status;
63 59  
64   - @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED)
  60 + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
65 61 @NotNull(message = "用户 IP不能为空")
66 62 private String userIp;
67 63  
... ... @@ -74,29 +70,20 @@ public class PayOrderBaseVO {
74 70 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
75 71 private LocalDateTime successTime;
76 72  
77   - @Schema(description = "订单支付通知时间")
78   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
79   - private LocalDateTime notifyTime;
80   -
81   - @Schema(description = "支付成功的订单拓展单编号")
82   - private Long successExtensionId;
83   -
84   - @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED)
85   - @NotNull(message = "退款状态不能为空")
86   - private Integer refundStatus;
  73 + @Schema(description = "支付成功的订单拓展单编号", example = "50")
  74 + private Long extensionId;
87 75  
88   - @Schema(description = "退款次数", requiredMode = Schema.RequiredMode.REQUIRED)
89   - @NotNull(message = "退款次数不能为空")
90   - private Integer refundTimes;
  76 + @Schema(description = "支付订单号", example = "2048888")
  77 + private String no;
91 78  
92   - @Schema(description = "退款总金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED)
  79 + @Schema(description = "退款总金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
93 80 @NotNull(message = "退款总金额,单位:分不能为空")
94 81 private Long refundPrice;
95 82  
96   - @Schema(description = "渠道用户编号")
  83 + @Schema(description = "渠道用户编号", example = "2048")
97 84 private String channelUserId;
98 85  
99   - @Schema(description = "渠道订单号")
  86 + @Schema(description = "渠道订单号", example = "4096")
100 87 private String channelOrderNo;
101 88  
102 89 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java
... ... @@ -13,32 +13,33 @@ import java.time.LocalDateTime;
13 13 @ToString(callSuper = true)
14 14 public class PayOrderDetailsRespVO extends PayOrderBaseVO {
15 15  
16   - @Schema(description = "支付订单编号")
  16 + @Schema(description = "支付订单编号", required = true, example = "1024")
17 17 private Long id;
18 18  
19   - @Schema(description = "应用名称")
  19 + @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
20 20 private String appName;
21 21  
22   - @Schema(description = "渠道编号名称")
23   - private String channelCodeName;
24   -
25   - @Schema(description = "创建时间")
  22 + @Schema(description = "创建时间", required = true)
26 23 private LocalDateTime createTime;
27 24  
  25 + @Schema(description = "更新时间", required = true)
  26 + private LocalDateTime updateTime;
  27 +
28 28 /**
29 29 * 支付订单扩展
30 30 */
31   - private PayOrderExtension payOrderExtension;
  31 + private PayOrderExtension extension;
32 32  
33 33 @Data
34 34 @Schema(description = "支付订单扩展")
35 35 public static class PayOrderExtension {
36 36  
37   - @Schema(description = "支付订单号")
  37 + @Schema(description = "支付订单号", required = true, example = "1024")
38 38 private String no;
39 39  
40 40 @Schema(description = "支付异步通知的内容")
41 41 private String channelNotifyData;
  42 +
42 43 }
43 44  
44 45 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.controller.admin.order.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
4 4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert;
5 6 import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
6 7 import com.alibaba.excel.annotation.ExcelProperty;
7 8 import lombok.Data;
... ... @@ -9,53 +10,44 @@ import lombok.Data;
9 10 import java.time.LocalDateTime;
10 11  
11 12 /**
12   - * 支付订单Excel VO
  13 + * 支付订单 Excel VO
13 14 *
14 15 * @author aquan
15 16 */
16 17 @Data
17 18 public class PayOrderExcelVO {
18 19  
19   - @ExcelProperty("支付订单编号")
  20 + @ExcelProperty("编号")
20 21 private Long id;
21 22  
22   - @ExcelProperty(value = "应用名称")
23   - private String appName;
24   -
25   - @ExcelProperty("商品标题")
26   - private String subject;
  23 + @ExcelProperty("创建时间")
  24 + private LocalDateTime createTime;
27 25  
28   - @ExcelProperty("商户订单编号")
29   - private String merchantOrderId;
  26 + @ExcelProperty(value = "支付金额", converter = MoneyConvert.class)
  27 + private Integer price;
30 28  
31   - @ExcelProperty("渠道订单号")
32   - private String channelOrderNo;
  29 + @ExcelProperty(value = "退款金额", converter = MoneyConvert.class)
  30 + private Integer refundPrice;
33 31  
34   - @ExcelProperty(value = "支付订单号")
35   - private String no;
  32 + @ExcelProperty(value = "手续金额", converter = MoneyConvert.class)
  33 + private Integer channelFeePrice;
36 34  
37   - @ExcelProperty("支付金额,单位:元")
38   - private String price;
  35 + @ExcelProperty("商户单号")
  36 + private String merchantOrderId;
39 37  
40   - @ExcelProperty("渠道手续金额,单位:元")
41   - private String channelFeePrice;
  38 + @ExcelProperty(value = "支付单号")
  39 + private String no;
42 40  
43   - @ExcelProperty("渠道手续费,单位:百分比")
44   - private String channelFeeRate;
  41 + @ExcelProperty("渠道单号")
  42 + private String channelOrderNo;
45 43  
46   - @DictFormat(DictTypeConstants.ORDER_STATUS)
47 44 @ExcelProperty(value = "支付状态", converter = DictConvert.class)
  45 + @DictFormat(DictTypeConstants.ORDER_STATUS)
48 46 private Integer status;
49 47  
50   - @DictFormat(DictTypeConstants.ORDER_NOTIFY_STATUS)
51   - @ExcelProperty(value = "通知商户支付结果的回调状态", converter = DictConvert.class)
52   - private Integer notifyStatus;
53   -
54   - @ExcelProperty("异步通知地址")
55   - private String notifyUrl;
56   -
57   - @ExcelProperty("创建时间")
58   - private LocalDateTime createTime;
  48 + @ExcelProperty(value = "渠道编号名称", converter = DictConvert.class)
  49 + @DictFormat(DictTypeConstants.CHANNEL_CODE)
  50 + private String channelCode;
59 51  
60 52 @ExcelProperty("订单支付成功时间")
61 53 private LocalDateTime successTime;
... ... @@ -63,24 +55,11 @@ public class PayOrderExcelVO {
63 55 @ExcelProperty("订单失效时间")
64 56 private LocalDateTime expireTime;
65 57  
66   - @ExcelProperty("订单支付通知时间")
67   - private LocalDateTime notifyTime;
68   -
69   - @ExcelProperty(value = "渠道编号名称")
70   - private String channelCodeName;
71   -
72   - @ExcelProperty("用户 IP")
73   - private String userIp;
74   -
75   - @DictFormat(DictTypeConstants.ORDER_REFUND_STATUS)
76   - @ExcelProperty(value = "退款状态", converter = DictConvert.class)
77   - private Integer refundStatus;
78   -
79   - @ExcelProperty("退款次数")
80   - private Integer refundTimes;
  58 + @ExcelProperty(value = "应用名称")
  59 + private String appName;
81 60  
82   - @ExcelProperty("退款总金额,单位:元")
83   - private String refundPrice;
  61 + @ExcelProperty("商品标题")
  62 + private String subject;
84 63  
85 64 @ExcelProperty("商品描述")
86 65 private String body;
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExportReqVO.java
... ... @@ -12,75 +12,24 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
12 12 @Data
13 13 public class PayOrderExportReqVO {
14 14  
15   - @Schema(description = "应用编号")
  15 + @Schema(description = "应用编号", example = "1024")
16 16 private Long appId;
17 17  
18   - @Schema(description = "渠道编号")
19   - private Long channelId;
20   -
21   - @Schema(description = "渠道编码")
  18 + @Schema(description = "渠道编码", example = "wx_app")
22 19 private String channelCode;
23 20  
24   - @Schema(description = "商户订单编号")
  21 + @Schema(description = "商户订单编号", example = "4096")
25 22 private String merchantOrderId;
26 23  
27   - @Schema(description = "商品标题")
28   - private String subject;
29   -
30   - @Schema(description = "商品描述")
31   - private String body;
32   -
33   - @Schema(description = "异步通知地址")
34   - private String notifyUrl;
35   -
36   - @Schema(description = "通知商户支付结果的回调状态")
37   - private Integer notifyStatus;
38   -
39   - @Schema(description = "支付金额,单位:分")
40   - private Long price;
41   -
42   - @Schema(description = "渠道手续费,单位:百分比")
43   - private Double channelFeeRate;
  24 + @Schema(description = "渠道编号", example = "1888")
  25 + private String channelOrderNo;
44 26  
45   - @Schema(description = "渠道手续金额,单位:分")
46   - private Long channelFeePrice;
  27 + @Schema(description = "支付单号", example = "2014888")
  28 + private String no;
47 29  
48   - @Schema(description = "支付状态")
  30 + @Schema(description = "支付状态", example = "0")
49 31 private Integer status;
50 32  
51   - @Schema(description = "用户 IP")
52   - private String userIp;
53   -
54   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
55   - @Schema(description = "订单失效时间")
56   - private LocalDateTime[] expireTime;
57   -
58   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
59   - @Schema(description = "开始订单支付成功时间")
60   - private LocalDateTime[] successTime;
61   -
62   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
63   - @Schema(description = "开始订单支付通知时间")
64   - private LocalDateTime[] notifyTime;
65   -
66   - @Schema(description = "支付成功的订单拓展单编号")
67   - private Long successExtensionId;
68   -
69   - @Schema(description = "退款状态")
70   - private Integer refundStatus;
71   -
72   - @Schema(description = "退款次数")
73   - private Integer refundTimes;
74   -
75   - @Schema(description = "退款总金额,单位:分")
76   - private Long refundPrice;
77   -
78   - @Schema(description = "渠道用户编号")
79   - private String channelUserId;
80   -
81   - @Schema(description = "渠道订单号")
82   - private String channelOrderNo;
83   -
84 33 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
85 34 @Schema(description = "创建时间")
86 35 private LocalDateTime[] createTime;
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java
... ... @@ -13,20 +13,13 @@ import java.time.LocalDateTime;
13 13 @ToString(callSuper = true)
14 14 public class PayOrderPageItemRespVO extends PayOrderBaseVO {
15 15  
16   - @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
17 17 private Long id;
18 18  
19 19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
20 20 private LocalDateTime createTime;
21 21  
22   - @Schema(description = "应用名称")
  22 + @Schema(description = "应用名称", example = "wx_pay")
23 23 private String appName;
24 24  
25   - @Schema(description = "渠道名称")
26   - private String channelCodeName;
27   -
28   - @Schema(description = "支付订单号")
29   - private String no;
30   -
31   -
32 25 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageReqVO.java
... ... @@ -17,75 +17,24 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 17 @ToString(callSuper = true)
18 18 public class PayOrderPageReqVO extends PageParam {
19 19  
20   - @Schema(description = "应用编号")
  20 + @Schema(description = "应用编号", example = "1024")
21 21 private Long appId;
22 22  
23   - @Schema(description = "渠道编号")
24   - private Long channelId;
25   -
26   - @Schema(description = "渠道编码")
  23 + @Schema(description = "渠道编码", example = "wx_app")
27 24 private String channelCode;
28 25  
29   - @Schema(description = "商户订单编号")
  26 + @Schema(description = "商户订单编号", example = "4096")
30 27 private String merchantOrderId;
31 28  
32   - @Schema(description = "商品标题")
33   - private String subject;
34   -
35   - @Schema(description = "商品描述")
36   - private String body;
37   -
38   - @Schema(description = "异步通知地址")
39   - private String notifyUrl;
40   -
41   - @Schema(description = "通知商户支付结果的回调状态")
42   - private Integer notifyStatus;
43   -
44   - @Schema(description = "支付金额,单位:分")
45   - private Long price;
46   -
47   - @Schema(description = "渠道手续费,单位:百分比")
48   - private Double channelFeeRate;
  29 + @Schema(description = "渠道编号", example = "1888")
  30 + private String channelOrderNo;
49 31  
50   - @Schema(description = "渠道手续金额,单位:分")
51   - private Long channelFeePrice;
  32 + @Schema(description = "支付单号", example = "2014888")
  33 + private String no;
52 34  
53   - @Schema(description = "支付状态")
  35 + @Schema(description = "支付状态", example = "0")
54 36 private Integer status;
55 37  
56   - @Schema(description = "用户 IP")
57   - private String userIp;
58   -
59   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
60   - @Schema(description = "订单失效时间")
61   - private LocalDateTime[] expireTime;
62   -
63   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
64   - @Schema(description = "订单支付成功时间")
65   - private LocalDateTime[] successTime;
66   -
67   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
68   - @Schema(description = "订单支付通知时间")
69   - private LocalDateTime[] notifyTime;
70   -
71   - @Schema(description = "支付成功的订单拓展单编号")
72   - private Long successExtensionId;
73   -
74   - @Schema(description = "退款状态")
75   - private Integer refundStatus;
76   -
77   - @Schema(description = "退款次数")
78   - private Integer refundTimes;
79   -
80   - @Schema(description = "退款总金额,单位:分")
81   - private Long refundPrice;
82   -
83   - @Schema(description = "渠道用户编号")
84   - private String channelUserId;
85   -
86   - @Schema(description = "渠道订单号")
87   - private String channelOrderNo;
88   -
89 38 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
90 39 @Schema(description = "创建时间")
91 40 private LocalDateTime[] createTime;
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderRespVO.java 100755 → 100644
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.refund;
2 2  
3 3 import cn.hutool.core.collection.CollectionUtil;
4   -import cn.hutool.core.util.ObjectUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  7 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
5 8 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*;
6 9 import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert;
7 10 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
8   -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
9 11 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
10 12 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
11 13 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
12 14 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
13   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
14   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
15   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
16   -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
17   -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
18   -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
19   -import io.swagger.v3.oas.annotations.tags.Tag;
20   -import io.swagger.v3.oas.annotations.Parameter;
21 15 import io.swagger.v3.oas.annotations.Operation;
  16 +import io.swagger.v3.oas.annotations.Parameter;
  17 +import io.swagger.v3.oas.annotations.tags.Tag;
22 18 import org.springframework.security.access.prepost.PreAuthorize;
23 19 import org.springframework.validation.annotation.Validated;
24 20 import org.springframework.web.bind.annotation.GetMapping;
... ... @@ -35,6 +31,7 @@ import java.util.List;
35 31 import java.util.Map;
36 32  
37 33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
38 35 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
39 36  
40 37 @Tag(name = "管理后台 - 退款订单")
... ... @@ -56,20 +53,13 @@ public class PayRefundController {
56 53 @PreAuthorize("@ss.hasPermission('pay:refund:query')")
57 54 public CommonResult<PayRefundDetailsRespVO> getRefund(@RequestParam("id") Long id) {
58 55 PayRefundDO refund = refundService.getRefund(id);
59   - if (ObjectUtil.isNull(refund)) {
  56 + if (refund == null) {
60 57 return success(new PayRefundDetailsRespVO());
61 58 }
62 59  
63   - PayAppDO appDO = appService.getApp(refund.getAppId());
64   - PayChannelEnum channelEnum = PayChannelEnum.getByCode(refund.getChannelCode());
65   - PayOrderDO orderDO = orderService.getOrder(refund.getOrderId());
66   -
67   - PayRefundDetailsRespVO refundDetail = PayRefundConvert.INSTANCE.refundDetailConvert(refund);
68   - refundDetail.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
69   - refundDetail.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
70   - refundDetail.setSubject(orderDO.getSubject());
71   -
72   - return success(refundDetail);
  60 + // 拼接数据
  61 + PayAppDO app = appService.getApp(refund.getAppId());
  62 + return success(PayRefundConvert.INSTANCE.convert(refund, app));
73 63 }
74 64  
75 65 @GetMapping("/page")
... ... @@ -82,21 +72,8 @@ public class PayRefundController {
82 72 }
83 73  
84 74 // 处理应用ID数据
85   - Map<Long, PayAppDO> appMap = appService.getAppMap(
86   - CollectionUtils.convertList(pageResult.getList(), PayRefundDO::getAppId));
87   - List<PayRefundPageItemRespVO> list = new ArrayList<>(pageResult.getList().size());
88   - pageResult.getList().forEach(c -> {
89   - PayAppDO appDO = appMap.get(c.getAppId());
90   - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode());
91   -
92   - PayRefundPageItemRespVO item = PayRefundConvert.INSTANCE.pageItemConvert(c);
93   -
94   - item.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
95   - item.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
96   - list.add(item);
97   - });
98   -
99   - return success(new PageResult<>(list, pageResult.getTotal()));
  75 + Map<Long, PayAppDO> appMap = appService.getAppMap(convertList(pageResult.getList(), PayRefundDO::getAppId));
  76 + return success(PayRefundConvert.INSTANCE.convertPage(pageResult, appMap));
100 77 }
101 78  
102 79 @GetMapping("/export-excel")
... ... @@ -105,36 +82,18 @@ public class PayRefundController {
105 82 @OperateLog(type = EXPORT)
106 83 public void exportRefundExcel(@Valid PayRefundExportReqVO exportReqVO,
107 84 HttpServletResponse response) throws IOException {
108   -
109 85 List<PayRefundDO> list = refundService.getRefundList(exportReqVO);
110 86 if (CollectionUtil.isEmpty(list)) {
111 87 ExcelUtils.write(response, "退款订单.xls", "数据",
112 88 PayRefundExcelVO.class, new ArrayList<>());
  89 + return;
113 90 }
114 91  
115   - // 处理应用ID数据
116   - Map<Long, PayAppDO> appMap = appService.getAppMap(
117   - CollectionUtils.convertList(list, PayRefundDO::getAppId));
118   -
119   - List<PayRefundExcelVO> excelDatum = new ArrayList<>(list.size());
120   - // 处理商品名称数据
121   - Map<Long, PayOrderDO> orderMap = orderService.getOrderSubjectMap(
122   - CollectionUtils.convertList(list, PayRefundDO::getOrderId));
123   -
124   - list.forEach(c -> {
125   - PayAppDO appDO = appMap.get(c.getAppId());
126   - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode());
127   -
128   - PayRefundExcelVO excelItem = PayRefundConvert.INSTANCE.excelConvert(c);
129   - excelItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用");
130   - excelItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道");
131   - excelItem.setSubject(orderMap.get(c.getOrderId()).getSubject());
132   -
133   - excelDatum.add(excelItem);
134   - });
135   -
  92 + // 拼接返回
  93 + Map<Long, PayAppDO> appMap = appService.getAppMap(convertList(list, PayRefundDO::getAppId));
  94 + List<PayRefundExcelVO> excelList = PayRefundConvert.INSTANCE.convertList(list, appMap);
136 95 // 导出 Excel
137   - ExcelUtils.write(response, "退款订单.xls", "数据", PayRefundExcelVO.class, excelDatum);
  96 + ExcelUtils.write(response, "退款订单.xls", "数据", PayRefundExcelVO.class, excelList);
138 97 }
139 98  
140 99 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundBaseVO.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.refund.vo;
  2 +
2 3 import io.swagger.v3.oas.annotations.media.Schema;
3 4 import lombok.Data;
4   -import org.springframework.format.annotation.DateTimeFormat;
5 5  
6   -import javax.validation.constraints.NotNull;
7 6 import java.time.LocalDateTime;
8 7  
9   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
10   -
11 8 /**
12 9 * 退款订单 Base VO,提供给添加、修改、详细的子 VO 使用
13 10 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
... ... @@ -15,91 +12,67 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
15 12 @Data
16 13 public class PayRefundBaseVO {
17 14  
18   - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED)
19   - @NotNull(message = "应用编号不能为空")
  15 + @Schema(description = "外部退款号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110")
  16 + private String no;
  17 +
  18 + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
20 19 private Long appId;
21 20  
22   - @Schema(description = "渠道编号", requiredMode = Schema.RequiredMode.REQUIRED)
23   - @NotNull(message = "渠道编号不能为空")
  21 + @Schema(description = "渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
24 22 private Long channelId;
25 23  
26   - @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED)
27   - @NotNull(message = "渠道编码不能为空")
  24 + @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_app")
28 25 private String channelCode;
29 26  
30   - @Schema(description = "支付订单编号 pay_order 表id", requiredMode = Schema.RequiredMode.REQUIRED)
31   - @NotNull(message = "支付订单编号 pay_order 表id不能为空")
  27 + @Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
32 28 private Long orderId;
33 29  
34   - @Schema(description = "交易订单号 pay_extension 表no 字段", requiredMode = Schema.RequiredMode.REQUIRED)
35   - @NotNull(message = "交易订单号 pay_extension 表no 字段不能为空")
36   - private String tradeNo;
  30 + // ========== 商户相关字段 ==========
37 31  
38   - @Schema(description = "商户订单编号(商户系统生成)", requiredMode = Schema.RequiredMode.REQUIRED)
39   - @NotNull(message = "商户订单编号(商户系统生成)不能为空")
  32 + @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "225")
40 33 private String merchantOrderId;
41 34  
42   - @Schema(description = "商户退款订单号(商户系统生成)", requiredMode = Schema.RequiredMode.REQUIRED)
43   - @NotNull(message = "商户退款订单号(商户系统生成)不能为空")
44   - private String merchantRefundNo;
  35 + @Schema(description = "商户退款订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
  36 + private String merchantRefundId;
45 37  
46   - @Schema(description = "异步通知商户地址", requiredMode = Schema.RequiredMode.REQUIRED)
47   - @NotNull(message = "异步通知商户地址不能为空")
  38 + @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED)
48 39 private String notifyUrl;
49 40  
50   - @Schema(description = "通知商户退款结果的回调状态", requiredMode = Schema.RequiredMode.REQUIRED)
51   - @NotNull(message = "通知商户退款结果的回调状态不能为空")
52   - private Integer notifyStatus;
  41 + // ========== 退款相关字段 ==========
53 42  
54   - @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED)
55   - @NotNull(message = "退款状态不能为空")
  43 + @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
56 44 private Integer status;
57 45  
58   - @Schema(description = "退款类型(部分退款,全部退款)", requiredMode = Schema.RequiredMode.REQUIRED)
59   - @NotNull(message = "退款类型(部分退款,全部退款)不能为空")
60   - private Integer type;
61   -
62   - @Schema(description = "支付金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED)
63   - @NotNull(message = "支付金额,单位分不能为空")
64   - private Long payAmount;
  46 + @Schema(description = "支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  47 + private Long payPrice;
65 48  
66   - @Schema(description = "退款金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED)
67   - @NotNull(message = "退款金额,单位分不能为空")
68   - private Long refundAmount;
  49 + @Schema(description = "退款金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
  50 + private Long refundPrice;
69 51  
70   - @Schema(description = "退款原因", requiredMode = Schema.RequiredMode.REQUIRED)
71   - @NotNull(message = "退款原因不能为空")
  52 + @Schema(description = "退款原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "我要退了")
72 53 private String reason;
73 54  
74   - @Schema(description = "用户 IP")
  55 + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
75 56 private String userIp;
76 57  
77   - @Schema(description = "渠道订单号,pay_order 中的channel_order_no 对应", requiredMode = Schema.RequiredMode.REQUIRED)
78   - @NotNull(message = "渠道订单号,pay_order 中的channel_order_no 对应不能为空")
  58 + // ========== 渠道相关字段 ==========
  59 +
  60 + @Schema(description = "渠道订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "233")
79 61 private String channelOrderNo;
80 62  
81   - @Schema(description = "渠道退款单号,渠道返回")
  63 + @Schema(description = "渠道退款单号", example = "2022")
82 64 private String channelRefundNo;
83 65  
84   - @Schema(description = "渠道调用报错时,错误码")
  66 + @Schema(description = "退款成功时间")
  67 + private LocalDateTime successTime;
  68 +
  69 + @Schema(description = "调用渠道的错误码")
85 70 private String channelErrorCode;
86 71  
87   - @Schema(description = "渠道调用报错时,错误信息")
  72 + @Schema(description = "调用渠道的错误提示")
88 73 private String channelErrorMsg;
89 74  
90 75 @Schema(description = "支付渠道的额外参数")
91   - private String channelExtras;
92   -
93   - @Schema(description = "退款失效时间")
94   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
95   - private LocalDateTime expireTime;
96   -
97   - @Schema(description = "退款成功时间")
98   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
99   - private LocalDateTime successTime;
100   -
101   - @Schema(description = "退款通知时间")
102   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
103   - private LocalDateTime notifyTime;
  76 + private String channelNotifyData;
104 77  
105 78 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundCreateReqVO.java deleted
1   -package cn.iocoder.yudao.module.pay.controller.admin.refund.vo;
2   -import io.swagger.v3.oas.annotations.media.Schema;
3   -import lombok.*;
4   -
5   -@Schema(description = "管理后台 - 退款订单创建 Request VO")
6   -@Data
7   -@EqualsAndHashCode(callSuper = true)
8   -@ToString(callSuper = true)
9   -public class PayRefundCreateReqVO extends PayRefundBaseVO {
10   -
11   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java
... ... @@ -5,7 +5,6 @@ import lombok.Data;
5 5 import lombok.EqualsAndHashCode;
6 6 import lombok.ToString;
7 7  
8   -import javax.validation.constraints.NotNull;
9 8 import java.time.LocalDateTime;
10 9  
11 10 @Schema(description = "管理后台 - 退款订单详情 Response VO")
... ... @@ -17,14 +16,11 @@ public class PayRefundDetailsRespVO extends PayRefundBaseVO {
17 16 @Schema(description = "支付退款编号", requiredMode = Schema.RequiredMode.REQUIRED)
18 17 private Long id;
19 18  
20   - @Schema(description = "应用名称")
  19 + @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是芋艿")
21 20 private String appName;
22 21  
23   - @Schema(description = "渠道编号名称")
24   - private String channelCodeName;
25   -
26   - @NotNull(message = "商品标题不能为空")
27   - private String subject;
  22 + @Schema(description = "支付订单", requiredMode = Schema.RequiredMode.REQUIRED)
  23 + private Order order;
28 24  
29 25 @Schema(description = "创建时间")
30 26 private LocalDateTime createTime;
... ... @@ -32,4 +28,13 @@ public class PayRefundDetailsRespVO extends PayRefundBaseVO {
32 28 @Schema(description = "更新时间")
33 29 private LocalDateTime updateTime;
34 30  
  31 + @Schema(description = "管理后台 - 支付订单")
  32 + @Data
  33 + public static class Order {
  34 +
  35 + @Schema(description = "商品标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆")
  36 + private String subject;
  37 +
  38 + }
  39 +
35 40 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.controller.admin.refund.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
4 4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert;
5 6 import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
6 7 import com.alibaba.excel.annotation.ExcelProperty;
7 8 import lombok.Data;
... ... @@ -19,64 +20,42 @@ public class PayRefundExcelVO {
19 20 @ExcelProperty("支付退款编号")
20 21 private Long id;
21 22  
22   - @ExcelProperty("商品名称")
23   - private String subject;
  23 + @ExcelProperty("创建时间")
  24 + private LocalDateTime createTime;
24 25  
25   - @ExcelProperty(value = "应用名称")
26   - private String appName;
  26 + @ExcelProperty(value = "支付金额", converter = MoneyConvert.class)
  27 + private Integer payPrice;
27 28  
28   - @ExcelProperty(value = "渠道编号名称")
29   - private String channelCodeName;
  29 + @ExcelProperty(value = "退款金额", converter = MoneyConvert.class)
  30 + private Integer refundPrice;
30 31  
31   - @ExcelProperty("交易订单号")
32   - private String tradeNo;
  32 + @ExcelProperty("商户退款单号")
  33 + private String merchantRefundId;
  34 + @ExcelProperty("退款单号")
  35 + private String no;
  36 + @ExcelProperty("渠道退款单号")
  37 + private String channelRefundNo;
33 38  
34   - @ExcelProperty("商户订单编号")
  39 + @ExcelProperty("商户支付单号")
35 40 private String merchantOrderId;
  41 + @ExcelProperty("渠道支付单号")
  42 + private String channelOrderNo;
36 43  
37   - @ExcelProperty("商户退款订单号")
38   - private String merchantRefundNo;
39   -
40   - @ExcelProperty("异步通知商户地址")
41   - private String notifyUrl;
42   -
43   - @DictFormat(DictTypeConstants.ORDER_NOTIFY_STATUS)
44   - @ExcelProperty(value = "商户退款结果回调状态", converter = DictConvert.class)
45   - private Integer notifyStatus;
46   -
47   - @DictFormat(DictTypeConstants.REFUND_ORDER_STATUS)
48 44 @ExcelProperty(value = "退款状态", converter = DictConvert.class)
  45 + @DictFormat(DictTypeConstants.REFUND_STATUS)
49 46 private Integer status;
50 47  
51   - @DictFormat(DictTypeConstants.REFUND_ORDER_TYPE)
52   - @ExcelProperty(value = "退款类型", converter = DictConvert.class)
53   - private Integer type;
  48 + @ExcelProperty(value = "退款渠道", converter = DictConvert.class)
  49 + @DictFormat(DictTypeConstants.CHANNEL_CODE)
  50 + private String channelCode;
54 51  
55   - @ExcelProperty("支付金额,单位:元")
56   - private String payPrice;
  52 + @ExcelProperty("成功时间")
  53 + private LocalDateTime successTime;
57 54  
58   - @ExcelProperty("退款金额,单位:元")
59   - private String refundPrice;
  55 + @ExcelProperty(value = "支付应用")
  56 + private String appName;
60 57  
61 58 @ExcelProperty("退款原因")
62 59 private String reason;
63 60  
64   - @ExcelProperty("用户付款 IP")
65   - private String userIp;
66   -
67   - @ExcelProperty("渠道订单号")
68   - private String channelOrderNo;
69   -
70   - @ExcelProperty("渠道退款单号")
71   - private String channelRefundNo;
72   -
73   - @ExcelProperty("创建时间")
74   - private LocalDateTime createTime;
75   -
76   - @ExcelProperty("退款成功时间")
77   - private LocalDateTime successTime;
78   -
79   - @ExcelProperty("退款失效时间")
80   - private LocalDateTime expireTime;
81   -
82 61 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExportReqVO.java
... ... @@ -12,77 +12,26 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
12 12 @Data
13 13 public class PayRefundExportReqVO {
14 14  
15   - @Schema(description = "应用编号")
  15 + @Schema(description = "应用编号", example = "1024")
16 16 private Long appId;
17 17  
18   - @Schema(description = "渠道编号")
19   - private Long channelId;
20   -
21   - @Schema(description = "渠道编码")
  18 + @Schema(description = "渠道编码", example = "wx_app")
22 19 private String channelCode;
23 20  
24   - @Schema(description = "支付订单编号 pay_order 表id")
25   - private Long orderId;
26   -
27   - @Schema(description = "交易订单号 pay_extension 表no 字段")
28   - private String tradeNo;
29   -
30   - @Schema(description = "商户订单编号(商户系统生成)")
  21 + @Schema(description = "商户支付单号", example = "10")
31 22 private String merchantOrderId;
32 23  
33   - @Schema(description = "商户退款订单号(商户系统生成)")
34   - private String merchantRefundNo;
35   -
36   - @Schema(description = "异步通知商户地址")
37   - private String notifyUrl;
38   -
39   - @Schema(description = "通知商户退款结果的回调状态")
40   - private Integer notifyStatus;
41   -
42   - @Schema(description = "退款状态")
43   - private Integer status;
44   -
45   - @Schema(description = "退款类型(部分退款,全部退款)")
46   - private Integer type;
47   -
48   - @Schema(description = "支付金额,单位分")
49   - private Long payPrice;
50   -
51   - @Schema(description = "退款金额,单位分")
52   - private Long refundPrice;
53   -
54   - @Schema(description = "退款原因")
55   - private String reason;
  24 + @Schema(description = "商户退款单号", example = "20")
  25 + private String merchantRefundId;
56 26  
57   - @Schema(description = "用户 IP")
58   - private String userIp;
59   -
60   - @Schema(description = "渠道订单号,pay_order 中的channel_order_no 对应")
  27 + @Schema(description = "渠道支付单号", example = "30")
61 28 private String channelOrderNo;
62 29  
63   - @Schema(description = "渠道退款单号,渠道返回")
  30 + @Schema(description = "渠道退款单号", example = "40")
64 31 private String channelRefundNo;
65 32  
66   - @Schema(description = "渠道调用报错时,错误码")
67   - private String channelErrorCode;
68   -
69   - @Schema(description = "渠道调用报错时,错误信息")
70   - private String channelErrorMsg;
71   -
72   - @Schema(description = "支付渠道的额外参数")
73   - private String channelExtras;
74   -
75   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
76   - @Schema(description = "退款失效时间")
77   - private LocalDateTime[] expireTime;
78   -
79   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
80   - @Schema(description = "退款成功时间")
81   - private LocalDateTime[] successTime;
82   -
83   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
84   - @Schema(description = "退款通知时间")
85   - private LocalDateTime[] notifyTime;
  33 + @Schema(description = "退款状态", example = "0")
  34 + private Integer status;
86 35  
87 36 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
88 37 @Schema(description = "创建时间")
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java
... ... @@ -13,15 +13,12 @@ import java.time.LocalDateTime;
13 13 @ToString(callSuper = true)
14 14 public class PayRefundPageItemRespVO extends PayRefundBaseVO {
15 15  
16   - @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
17 17 private Long id;
18 18  
19   - @Schema(description = "应用名称")
  19 + @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是芋艿")
20 20 private String appName;
21 21  
22   - @Schema(description = "渠道名称")
23   - private String channelCodeName;
24   -
25 22 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
26 23 private LocalDateTime createTime;
27 24  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageReqVO.java
... ... @@ -17,77 +17,26 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 17 @ToString(callSuper = true)
18 18 public class PayRefundPageReqVO extends PageParam {
19 19  
20   - @Schema(description = "应用编号")
  20 + @Schema(description = "应用编号", example = "1024")
21 21 private Long appId;
22 22  
23   - @Schema(description = "渠道编号")
24   - private Long channelId;
25   -
26   - @Schema(description = "渠道编码")
  23 + @Schema(description = "渠道编码", example = "wx_app")
27 24 private String channelCode;
28 25  
29   - @Schema(description = "支付订单编号 pay_order 表id")
30   - private Long orderId;
31   -
32   - @Schema(description = "交易订单号 pay_extension 表no 字段")
33   - private String tradeNo;
34   -
35   - @Schema(description = "商户订单编号(商户系统生成)")
  26 + @Schema(description = "商户支付单号", example = "10")
36 27 private String merchantOrderId;
37 28  
38   - @Schema(description = "商户退款订单号(商户系统生成)")
39   - private String merchantRefundNo;
40   -
41   - @Schema(description = "异步通知商户地址")
42   - private String notifyUrl;
43   -
44   - @Schema(description = "通知商户退款结果的回调状态")
45   - private Integer notifyStatus;
46   -
47   - @Schema(description = "退款状态")
48   - private Integer status;
49   -
50   - @Schema(description = "退款类型(部分退款,全部退款)")
51   - private Integer type;
52   -
53   - @Schema(description = "支付金额,单位分")
54   - private Long payPrice;
55   -
56   - @Schema(description = "退款金额,单位分")
57   - private Long refundPrice;
58   -
59   - @Schema(description = "退款原因")
60   - private String reason;
  29 + @Schema(description = "商户退款单号", example = "20")
  30 + private String merchantRefundId;
61 31  
62   - @Schema(description = "用户 IP")
63   - private String userIp;
64   -
65   - @Schema(description = "渠道订单号,pay_order 中的channel_order_no 对应")
  32 + @Schema(description = "渠道支付单号", example = "30")
66 33 private String channelOrderNo;
67 34  
68   - @Schema(description = "渠道退款单号,渠道返回")
  35 + @Schema(description = "渠道退款单号", example = "40")
69 36 private String channelRefundNo;
70 37  
71   - @Schema(description = "渠道调用报错时,错误码")
72   - private String channelErrorCode;
73   -
74   - @Schema(description = "渠道调用报错时,错误信息")
75   - private String channelErrorMsg;
76   -
77   - @Schema(description = "支付渠道的额外参数")
78   - private String channelExtras;
79   -
80   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
81   - @Schema(description = "退款失效时间")
82   - private LocalDateTime[] expireTime;
83   -
84   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
85   - @Schema(description = "退款成功时间")
86   - private LocalDateTime[] successTime;
87   -
88   - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
89   - @Schema(description = "退款通知时间")
90   - private LocalDateTime[] notifyTime;
  38 + @Schema(description = "退款状态", example = "0")
  39 + private Integer status;
91 40  
92 41 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
93 42 @Schema(description = "创建时间")
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundUpdateReqVO.java deleted
1   -package cn.iocoder.yudao.module.pay.controller.admin.refund.vo;
2   -import io.swagger.v3.oas.annotations.media.Schema;
3   -import lombok.*;
4   -import javax.validation.constraints.*;
5   -
6   -@Schema(description = "管理后台 - 退款订单更新 Request VO")
7   -@Data
8   -@EqualsAndHashCode(callSuper = true)
9   -@ToString(callSuper = true)
10   -public class PayRefundUpdateReqVO extends PayRefundBaseVO {
11   -
12   - @Schema(description = "支付退款编号", requiredMode = Schema.RequiredMode.REQUIRED)
13   - @NotNull(message = "支付退款编号不能为空")
14   - private Long id;
15   -
16   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/notify/PayNotifyTaskConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.convert.notify;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  5 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
  6 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
  7 +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
  8 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
  9 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
  10 +import org.mapstruct.Mapper;
  11 +import org.mapstruct.factory.Mappers;
  12 +
  13 +import java.util.List;
  14 +import java.util.Map;
  15 +
  16 +/**
  17 + * 支付通知 Convert
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Mapper
  22 +public interface PayNotifyTaskConvert {
  23 +
  24 + PayNotifyTaskConvert INSTANCE = Mappers.getMapper(PayNotifyTaskConvert.class);
  25 +
  26 + PayNotifyTaskRespVO convert(PayNotifyTaskDO bean);
  27 +
  28 + default PageResult<PayNotifyTaskRespVO> convertPage(PageResult<PayNotifyTaskDO> page, Map<Long, PayAppDO> appMap){
  29 + PageResult<PayNotifyTaskRespVO> result = convertPage(page);
  30 + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName())));
  31 + return result;
  32 + }
  33 + PageResult<PayNotifyTaskRespVO> convertPage(PageResult<PayNotifyTaskDO> page);
  34 +
  35 + default PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, PayAppDO app, List<PayNotifyLogDO> logs) {
  36 + PayNotifyTaskDetailRespVO respVO = convert(task, logs);
  37 + if (app != null) {
  38 + respVO.setAppName(app.getName());
  39 + }
  40 + return respVO;
  41 + }
  42 + PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, List<PayNotifyLogDO> logs);
  43 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java
1 1 package cn.iocoder.yudao.module.pay.convert.order;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
4 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
5 7 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
6 8 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
7 9 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
8 10 import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO;
  11 +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
9 12 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
10 13 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
11 14 import org.mapstruct.Mapper;
12 15 import org.mapstruct.Mapping;
13 16 import org.mapstruct.factory.Mappers;
14 17  
15   -import java.math.BigDecimal;
16   -import java.math.RoundingMode;
17 18 import java.util.List;
  19 +import java.util.Map;
18 20  
19 21 /**
20 22 * 支付订单 Convert
... ... @@ -30,60 +32,32 @@ public interface PayOrderConvert {
30 32  
31 33 PayOrderRespDTO convert2(PayOrderDO order);
32 34  
33   - PayOrderDetailsRespVO orderDetailConvert(PayOrderDO bean);
34   -
35   - PayOrderDetailsRespVO.PayOrderExtension orderDetailExtensionConvert(PayOrderExtensionDO bean);
36   -
37   - List<PayOrderRespVO> convertList(List<PayOrderDO> list);
38   -
39   - PageResult<PayOrderRespVO> convertPage(PageResult<PayOrderDO> page);
40   -
41   - /**
42   - * 订单 DO 转自定义分页对象
43   - *
44   - * @param bean 订单DO
45   - * @return 分页对象
46   - */
47   - PayOrderPageItemRespVO pageConvertItemPage(PayOrderDO bean);
48   -
49   - // TODO 芋艿:优化下 convert 逻辑
50   - default PayOrderExcelVO excelConvert(PayOrderDO bean) {
51   - if (bean == null) {
52   - return null;
  35 + default PayOrderDetailsRespVO convert(PayOrderDO order, PayOrderExtensionDO orderExtension, PayAppDO app) {
  36 + PayOrderDetailsRespVO respVO = convertDetail(order);
  37 + respVO.setExtension(convert(orderExtension));
  38 + if (app != null) {
  39 + respVO.setAppName(app.getName());
53 40 }
  41 + return respVO;
  42 + }
  43 + PayOrderDetailsRespVO convertDetail(PayOrderDO bean);
  44 + PayOrderDetailsRespVO.PayOrderExtension convert(PayOrderExtensionDO bean);
54 45  
55   - PayOrderExcelVO payOrderExcelVO = new PayOrderExcelVO();
56   -
57   - payOrderExcelVO.setId(bean.getId());
58   - payOrderExcelVO.setSubject(bean.getSubject());
59   - payOrderExcelVO.setMerchantOrderId(bean.getMerchantOrderId());
60   - payOrderExcelVO.setChannelOrderNo(bean.getChannelOrderNo());
61   - payOrderExcelVO.setStatus(bean.getStatus());
62   - payOrderExcelVO.setNotifyStatus(bean.getNotifyStatus());
63   - payOrderExcelVO.setNotifyUrl(bean.getNotifyUrl());
64   - payOrderExcelVO.setCreateTime(bean.getCreateTime());
65   - payOrderExcelVO.setSuccessTime(bean.getSuccessTime());
66   - payOrderExcelVO.setExpireTime(bean.getExpireTime());
67   - payOrderExcelVO.setNotifyTime(bean.getNotifyTime());
68   - payOrderExcelVO.setUserIp(bean.getUserIp());
69   - payOrderExcelVO.setRefundStatus(bean.getRefundStatus());
70   - payOrderExcelVO.setRefundTimes(bean.getRefundTimes());
71   - payOrderExcelVO.setBody(bean.getBody());
72   -
73   - BigDecimal multiple = new BigDecimal(100);
74   -
75   - payOrderExcelVO.setPrice(BigDecimal.valueOf(bean.getPrice())
76   - .divide(multiple, 2, RoundingMode.HALF_UP).toString());
77   -
78   - payOrderExcelVO.setChannelFeePrice(BigDecimal.valueOf(bean.getChannelFeePrice())
79   - .divide(multiple, 2, RoundingMode.HALF_UP).toString());
80   - payOrderExcelVO.setChannelFeeRate(java.math.BigDecimal.valueOf(bean.getChannelFeeRate())
81   - .multiply(multiple).toString());
82   - payOrderExcelVO.setRefundPrice(BigDecimal.valueOf(bean.getRefundPrice())
83   - .divide(multiple, 2, RoundingMode.HALF_UP).toString());
84   -
85   - return payOrderExcelVO;
  46 + default PageResult<PayOrderPageItemRespVO> convertPage(PageResult<PayOrderDO> page, Map<Long, PayAppDO> appMap) {
  47 + PageResult<PayOrderPageItemRespVO> result = convertPage(page);
  48 + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName())));
  49 + return result;
  50 + }
  51 + PageResult<PayOrderPageItemRespVO> convertPage(PageResult<PayOrderDO> page);
  52 +
  53 + default List<PayOrderExcelVO> convertList(List<PayOrderDO> list, Map<Long, PayAppDO> appMap) {
  54 + return CollectionUtils.convertList(list, order -> {
  55 + PayOrderExcelVO excelVO = convertExcel(order);
  56 + MapUtils.findAndThen(appMap, order.getAppId(), app -> excelVO.setAppName(app.getName()));
  57 + return excelVO;
  58 + });
86 59 }
  60 + PayOrderExcelVO convertExcel(PayOrderDO bean);
87 61  
88 62 PayOrderDO convert(PayOrderCreateReqDTO bean);
89 63  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java
1 1 package cn.iocoder.yudao.module.pay.convert.refund;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
4 6 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
5 7 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
6   -import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*;
  8 +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundDetailsRespVO;
  9 +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExcelVO;
  10 +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageItemRespVO;
  11 +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
  12 +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
7 13 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
8 14 import org.mapstruct.Mapper;
9 15 import org.mapstruct.factory.Mappers;
10 16  
11   -import java.math.BigDecimal;
12   -import java.math.RoundingMode;
13 17 import java.util.List;
  18 +import java.util.Map;
14 19  
15 20 @Mapper
16 21 public interface PayRefundConvert {
17 22  
18 23 PayRefundConvert INSTANCE = Mappers.getMapper(PayRefundConvert.class);
19 24  
20   - PayRefundDO convert(PayRefundCreateReqVO bean);
21 25  
22   - PayRefundDO convert(PayRefundUpdateReqVO bean);
23   -
24   - PayRefundRespVO convert(PayRefundDO bean);
25   -
26   - /**
27   - * 退款订单 DO 转 退款详情订单 VO
28   - *
29   - * @param bean 退款订单 DO
30   - * @return 退款详情订单 VO
31   - */
32   - PayRefundDetailsRespVO refundDetailConvert(PayRefundDO bean);
33   -
34   - /**
35   - * 退款订单DO 转 分页退款条目VO
36   - *
37   - * @param bean 退款订单DO
38   - * @return 分页退款条目VO
39   - */
40   - PayRefundPageItemRespVO pageItemConvert(PayRefundDO bean);
41   -
42   - List<PayRefundRespVO> convertList(List<PayRefundDO> list);
43   -
44   - PageResult<PayRefundRespVO> convertPage(PageResult<PayRefundDO> page);
45   -
46   - /**
47   - * 退款订单DO 转 导出excel VO
48   - *
49   - * @param bean 退款订单DO
50   - * @return 导出 excel VO
51   - */
52   - default PayRefundExcelVO excelConvert(PayRefundDO bean) {
53   - if (bean == null) {
54   - return null;
  26 + default PayRefundDetailsRespVO convert(PayRefundDO refund, PayAppDO app) {
  27 + PayRefundDetailsRespVO respVO = convert(refund);
  28 + if (app != null) {
  29 + respVO.setAppName(app.getName());
55 30 }
  31 + return respVO;
  32 + }
  33 + PayRefundDetailsRespVO convert(PayRefundDO bean);
  34 + PayRefundDetailsRespVO.Order convert(PayOrderDO bean);
56 35  
57   - PayRefundExcelVO payRefundExcelVO = new PayRefundExcelVO();
58   -
59   - payRefundExcelVO.setId(bean.getId());
60   - payRefundExcelVO.setTradeNo(bean.getNo());
61   - payRefundExcelVO.setMerchantOrderId(bean.getMerchantOrderId());
62   - // TODO 芋艿:晚点在改
63   -// payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo());
64   - payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl());
65   - payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus());
66   - payRefundExcelVO.setStatus(bean.getStatus());
67   - payRefundExcelVO.setReason(bean.getReason());
68   - payRefundExcelVO.setUserIp(bean.getUserIp());
69   - payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo());
70   - payRefundExcelVO.setChannelRefundNo(bean.getChannelRefundNo());
71   - payRefundExcelVO.setSuccessTime(bean.getSuccessTime());
72   - payRefundExcelVO.setCreateTime(bean.getCreateTime());
73   -
74   - BigDecimal multiple = new BigDecimal(100);
75   - payRefundExcelVO.setPayPrice(BigDecimal.valueOf(bean.getPayPrice())
76   - .divide(multiple, 2, RoundingMode.HALF_UP).toString());
77   - payRefundExcelVO.setRefundPrice(BigDecimal.valueOf(bean.getRefundPrice())
78   - .divide(multiple, 2, RoundingMode.HALF_UP).toString());
79   -
80   - return payRefundExcelVO;
  36 + default PageResult<PayRefundPageItemRespVO> convertPage(PageResult<PayRefundDO> page, Map<Long, PayAppDO> appMap) {
  37 + PageResult<PayRefundPageItemRespVO> result = convertPage(page);
  38 + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName())));
  39 + return result;
81 40 }
  41 + PageResult<PayRefundPageItemRespVO> convertPage(PageResult<PayRefundDO> page);
82 42  
83 43 PayRefundDO convert(PayRefundCreateReqDTO bean);
84 44  
85 45 PayRefundRespDTO convert02(PayRefundDO bean);
86 46  
  47 + default List<PayRefundExcelVO> convertList(List<PayRefundDO> list, Map<Long, PayAppDO> appMap) {
  48 + return CollectionUtils.convertList(list, order -> {
  49 + PayRefundExcelVO excelVO = convertExcel(order);
  50 + MapUtils.findAndThen(appMap, order.getAppId(), app -> excelVO.setAppName(app.getName()));
  51 + return excelVO;
  52 + });
  53 + }
  54 + PayRefundExcelVO convertExcel(PayRefundDO bean);
  55 +
87 56 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java
... ... @@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
15 15 import java.time.LocalDateTime;
16 16  
17 17 /**
18   - * 商户支付、退款等的通知
  18 + * 支付通知
19 19 * 在支付系统收到支付渠道的支付、退款的结果后,需要不断的通知到业务系统,直到成功。
20 20 *
21 21 * @author 芋道源码
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java
... ... @@ -4,9 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
5 5 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
6 6 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
7   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
8 7 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
9   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
10 8 import com.baomidou.mybatisplus.annotation.KeySequence;
11 9 import com.baomidou.mybatisplus.annotation.TableName;
12 10 import lombok.*;
... ... @@ -71,12 +69,6 @@ public class PayOrderDO extends BaseDO {
71 69 * 异步通知地址
72 70 */
73 71 private String notifyUrl;
74   - /**
75   - * 通知商户支付结果的回调状态
76   - *
77   - * 枚举 {@link PayOrderNotifyStatusEnum}
78   - */
79   - private Integer notifyStatus;
80 72  
81 73 // ========== 订单相关字段 ==========
82 74  
... ... @@ -93,7 +85,7 @@ public class PayOrderDO extends BaseDO {
93 85 /**
94 86 * 渠道手续金额,单位:分
95 87 */
96   - private Long channelFeePrice;
  88 + private Integer channelFeePrice;
97 89 /**
98 90 * 支付状态
99 91 *
... ... @@ -113,27 +105,19 @@ public class PayOrderDO extends BaseDO {
113 105 */
114 106 private LocalDateTime successTime;
115 107 /**
116   - * 订单支付通知时间,即支付渠道的通知时间
117   - */
118   - private LocalDateTime notifyTime;
119   - /**
120 108 * 支付成功的订单拓展单编号
121 109 *
122   - * 关联 {@link PayOrderDO#getId()}
  110 + * 关联 {@link PayOrderExtensionDO#getId()}
123 111 */
124   - private Long successExtensionId;
125   -
126   - // ========== 退款相关字段 ==========
  112 + private Long extensionId;
127 113 /**
128   - * 退款状态
  114 + * 支付成功的外部订单号
129 115 *
130   - * 枚举 {@link PayOrderRefundStatusEnum}
131   - */
132   - private Integer refundStatus;
133   - /**
134   - * 退款次数
  116 + * 关联 {@link PayOrderExtensionDO#getNo()}
135 117 */
136   - private Integer refundTimes;
  118 + private String no;
  119 +
  120 + // ========== 退款相关字段 ==========
137 121 /**
138 122 * 退款总金额,单位:分
139 123 */
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java
... ... @@ -15,6 +15,7 @@ import java.util.Map;
15 15 /**
16 16 * 支付订单拓展 DO
17 17 *
  18 + * 每次调用支付渠道,都会生成一条对应记录
18 19 *
19 20 * @author 芋道源码
20 21 */
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
... ... @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 6 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
7 7 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
8 8 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
9   -import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
10 9 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
11 10 import com.baomidou.mybatisplus.annotation.KeySequence;
12 11 import com.baomidou.mybatisplus.annotation.TableId;
... ... @@ -89,12 +88,6 @@ public class PayRefundDO extends BaseDO {
89 88 * 异步通知地址
90 89 */
91 90 private String notifyUrl;
92   - /**
93   - * 通知商户退款结果的回调状态
94   - *
95   - * 枚举 {@link PayNotifyStatusEnum}
96   - */
97   - private Integer notifyStatus;
98 91  
99 92 // ========== 退款相关字段 ==========
100 93 /**
... ... @@ -147,7 +140,7 @@ public class PayRefundDO extends BaseDO {
147 140 */
148 141 private String channelErrorCode;
149 142 /**
150   - * 调用渠道报错时,错误信息
  143 + * 调用渠道的错误提示
151 144 */
152 145 private String channelErrorMsg;
153 146  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyLogMapper.java
... ... @@ -4,6 +4,13 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import org.apache.ibatis.annotations.Mapper;
6 6  
  7 +import java.util.List;
  8 +
7 9 @Mapper
8 10 public interface PayNotifyLogMapper extends BaseMapperX<PayNotifyLogDO> {
  11 +
  12 + default List<PayNotifyLogDO> selectListByTaskId(Long taskId) {
  13 + return selectList(PayNotifyLogDO::getTaskId, taskId);
  14 + }
  15 +
9 16 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyTaskMapper.java
1 1 package cn.iocoder.yudao.module.pay.dal.mysql.notify;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
3 7 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
4 8 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
5   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  9 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
7 10 import org.apache.ibatis.annotations.Mapper;
8 11  
9 12 import java.time.LocalDateTime;
... ... @@ -21,10 +24,21 @@ public interface PayNotifyTaskMapper extends BaseMapperX&lt;PayNotifyTaskDO&gt; {
21 24 * @return PayTransactionNotifyTaskDO 数组
22 25 */
23 26 default List<PayNotifyTaskDO> selectListByNotify() {
24   - return selectList(new QueryWrapper<PayNotifyTaskDO>()
25   - .in("status", PayNotifyStatusEnum.WAITING.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(),
26   - PayNotifyStatusEnum.REQUEST_FAILURE.getStatus())
27   - .le("next_notify_time", LocalDateTime.now()));
  27 + return selectList(new LambdaQueryWrapper<PayNotifyTaskDO>()
  28 + .in(PayNotifyTaskDO::getStatus, PayNotifyStatusEnum.WAITING.getStatus(),
  29 + PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus())
  30 + .le(PayNotifyTaskDO::getNextNotifyTime, LocalDateTime.now()));
  31 + }
  32 +
  33 + default PageResult<PayNotifyTaskDO> selectPage(PayNotifyTaskPageReqVO reqVO) {
  34 + return selectPage(reqVO, new LambdaQueryWrapperX<PayNotifyTaskDO>()
  35 + .eqIfPresent(PayNotifyTaskDO::getAppId, reqVO.getAppId())
  36 + .eqIfPresent(PayNotifyTaskDO::getType, reqVO.getType())
  37 + .eqIfPresent(PayNotifyTaskDO::getDataId, reqVO.getDataId())
  38 + .eqIfPresent(PayNotifyTaskDO::getStatus, reqVO.getStatus())
  39 + .eqIfPresent(PayNotifyTaskDO::getMerchantOrderId, reqVO.getMerchantOrderId())
  40 + .betweenIfPresent(PayNotifyTaskDO::getCreateTime, reqVO.getCreateTime())
  41 + .orderByDesc(PayNotifyTaskDO::getId));
28 42 }
29 43  
30 44 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java
1 1 package cn.iocoder.yudao.module.pay.dal.mysql.order;
2 2  
3   -import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
4   -import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
5 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7   -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
  7 +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
8 8 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
9 9 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
10   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
11 10 import org.apache.ibatis.annotations.Mapper;
12 11  
13   -import java.util.Collection;
14 12 import java.util.List;
15 13  
16 14 @Mapper
17 15 public interface PayOrderMapper extends BaseMapperX<PayOrderDO> {
18 16  
19 17 default PageResult<PayOrderDO> selectPage(PayOrderPageReqVO reqVO) {
20   - return selectPage(reqVO, new QueryWrapperX<PayOrderDO>()
21   - .eqIfPresent("app_id", reqVO.getAppId())
22   - .eqIfPresent("channel_id", reqVO.getChannelId())
23   - .eqIfPresent("channel_code", reqVO.getChannelCode())
24   - .likeIfPresent("merchant_order_id", reqVO.getMerchantOrderId())
25   - .eqIfPresent("notify_status", reqVO.getNotifyStatus())
26   - .eqIfPresent("status", reqVO.getStatus())
27   - .eqIfPresent("refund_status", reqVO.getRefundStatus())
28   - .likeIfPresent("channel_order_no", reqVO.getChannelOrderNo())
29   - .betweenIfPresent("create_time", reqVO.getCreateTime())
30   - .orderByDesc("id"));
  18 + return selectPage(reqVO, new LambdaQueryWrapperX<PayOrderDO>()
  19 + .eqIfPresent(PayOrderDO::getAppId, reqVO.getAppId())
  20 + .eqIfPresent(PayOrderDO::getChannelCode, reqVO.getChannelCode())
  21 + .likeIfPresent(PayOrderDO::getMerchantOrderId, reqVO.getMerchantOrderId())
  22 + .likeIfPresent(PayOrderDO::getChannelOrderNo, reqVO.getChannelOrderNo())
  23 + .likeIfPresent(PayOrderDO::getNo, reqVO.getNo())
  24 + .eqIfPresent(PayOrderDO::getStatus, reqVO.getStatus())
  25 + .betweenIfPresent(PayOrderDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(PayOrderDO::getId));
31 27 }
32 28  
33 29 default List<PayOrderDO> selectList(PayOrderExportReqVO reqVO) {
34   - return selectList(new QueryWrapperX<PayOrderDO>()
35   - .eqIfPresent("app_id", reqVO.getAppId())
36   - .eqIfPresent("channel_id", reqVO.getChannelId())
37   - .eqIfPresent("channel_code", reqVO.getChannelCode())
38   - .likeIfPresent("merchant_order_id", reqVO.getMerchantOrderId())
39   - .eqIfPresent("notify_status", reqVO.getNotifyStatus())
40   - .eqIfPresent("status", reqVO.getStatus())
41   - .eqIfPresent("refund_status", reqVO.getRefundStatus())
42   - .likeIfPresent("channel_order_no", reqVO.getChannelOrderNo())
43   - .betweenIfPresent("create_time", reqVO.getCreateTime())
44   - .orderByDesc("id"));
45   - }
46   -
47   - default List<PayOrderDO> findByIdListQueryOrderSubject(Collection<Long> idList) {
48   - return selectList(new LambdaQueryWrapper<PayOrderDO>()
49   - .select(PayOrderDO::getId, PayOrderDO::getSubject)
50   - .in(PayOrderDO::getId, idList));
  30 + return selectList(new LambdaQueryWrapperX<PayOrderDO>()
  31 + .eqIfPresent(PayOrderDO::getAppId, reqVO.getAppId())
  32 + .eqIfPresent(PayOrderDO::getChannelCode, reqVO.getChannelCode())
  33 + .likeIfPresent(PayOrderDO::getMerchantOrderId, reqVO.getMerchantOrderId())
  34 + .likeIfPresent(PayOrderDO::getChannelOrderNo, reqVO.getChannelOrderNo())
  35 + .likeIfPresent(PayOrderDO::getNo, reqVO.getNo())
  36 + .eqIfPresent(PayOrderDO::getStatus, reqVO.getStatus())
  37 + .betweenIfPresent(PayOrderDO::getCreateTime, reqVO.getCreateTime())
  38 + .orderByDesc(PayOrderDO::getId));
51 39 }
52 40  
53 41 default Long selectCountByAppId(Long appId) {
... ... @@ -55,13 +43,13 @@ public interface PayOrderMapper extends BaseMapperX&lt;PayOrderDO&gt; {
55 43 }
56 44  
57 45 default PayOrderDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) {
58   - return selectOne(new QueryWrapper<PayOrderDO>().eq("app_id", appId)
59   - .eq("merchant_order_id", merchantOrderId));
  46 + return selectOne(PayOrderDO::getAppId, appId,
  47 + PayOrderDO::getMerchantOrderId, merchantOrderId);
60 48 }
61 49  
62 50 default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) {
63   - return update(update, new QueryWrapper<PayOrderDO>()
64   - .eq("id", id).eq("status", status));
  51 + return update(update, new LambdaQueryWrapper<PayOrderDO>()
  52 + .eq(PayOrderDO::getId, id).eq(PayOrderDO::getStatus, status));
65 53 }
66 54  
67 55 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.dal.mysql.refund;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
7 6 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
8 7 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
9 8 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
... ... @@ -44,27 +43,29 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; {
44 43 }
45 44  
46 45 default PageResult<PayRefundDO> selectPage(PayRefundPageReqVO reqVO) {
47   - return selectPage(reqVO, new QueryWrapperX<PayRefundDO>()
48   - .eqIfPresent("app_id", reqVO.getAppId())
49   - .eqIfPresent("channel_code", reqVO.getChannelCode())
50   - .likeIfPresent("merchant_refund_no", reqVO.getMerchantRefundNo())
51   - .eqIfPresent("type", reqVO.getType())
52   - .eqIfPresent("status", reqVO.getStatus())
53   - .eqIfPresent("notify_status", reqVO.getNotifyStatus())
54   - .betweenIfPresent("create_time", reqVO.getCreateTime())
55   - .orderByDesc("id"));
  46 + return selectPage(reqVO, new LambdaQueryWrapperX<PayRefundDO>()
  47 + .eqIfPresent(PayRefundDO::getAppId, reqVO.getAppId())
  48 + .eqIfPresent(PayRefundDO::getChannelCode, reqVO.getChannelCode())
  49 + .likeIfPresent(PayRefundDO::getMerchantOrderId, reqVO.getMerchantOrderId())
  50 + .likeIfPresent(PayRefundDO::getMerchantRefundId, reqVO.getMerchantRefundId())
  51 + .likeIfPresent(PayRefundDO::getChannelOrderNo, reqVO.getChannelOrderNo())
  52 + .likeIfPresent(PayRefundDO::getChannelRefundNo, reqVO.getChannelRefundNo())
  53 + .eqIfPresent(PayRefundDO::getStatus, reqVO.getStatus())
  54 + .betweenIfPresent(PayRefundDO::getCreateTime, reqVO.getCreateTime())
  55 + .orderByDesc(PayRefundDO::getId));
56 56 }
57 57  
58 58 default List<PayRefundDO> selectList(PayRefundExportReqVO reqVO) {
59   - return selectList(new QueryWrapperX<PayRefundDO>()
60   - .eqIfPresent("app_id", reqVO.getAppId())
61   - .eqIfPresent("channel_code", reqVO.getChannelCode())
62   - .likeIfPresent("merchant_refund_no", reqVO.getMerchantRefundNo())
63   - .eqIfPresent("type", reqVO.getType())
64   - .eqIfPresent("status", reqVO.getStatus())
65   - .eqIfPresent("notify_status", reqVO.getNotifyStatus())
66   - .betweenIfPresent("create_time", reqVO.getCreateTime())
67   - .orderByDesc("id"));
  59 + return selectList(new LambdaQueryWrapperX<PayRefundDO>()
  60 + .eqIfPresent(PayRefundDO::getAppId, reqVO.getAppId())
  61 + .eqIfPresent(PayRefundDO::getChannelCode, reqVO.getChannelCode())
  62 + .likeIfPresent(PayRefundDO::getMerchantOrderId, reqVO.getMerchantOrderId())
  63 + .likeIfPresent(PayRefundDO::getMerchantRefundId, reqVO.getMerchantRefundId())
  64 + .likeIfPresent(PayRefundDO::getChannelOrderNo, reqVO.getChannelOrderNo())
  65 + .likeIfPresent(PayRefundDO::getChannelRefundNo, reqVO.getChannelRefundNo())
  66 + .eqIfPresent(PayRefundDO::getStatus, reqVO.getStatus())
  67 + .betweenIfPresent(PayRefundDO::getCreateTime, reqVO.getCreateTime())
  68 + .orderByDesc(PayRefundDO::getId));
68 69 }
69 70  
70 71 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java
... ... @@ -14,4 +14,12 @@ public interface RedisKeyConstants {
14 14 "pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类
15 15 RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构
16 16  
  17 + /**
  18 + * 支付序号的缓存
  19 + *
  20 + * KEY 格式:pay_no:{prefix}
  21 + * VALUE 数据格式:编号自增
  22 + */
  23 + String PAY_NO = "pay_no";
  24 +
17 25 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/no/PayNoRedisDAO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.dal.redis.no;
  2 +
  3 +import cn.hutool.core.date.DatePattern;import cn.hutool.core.date.DateUtil;import org.springframework.data.redis.core.StringRedisTemplate;
  4 +import org.springframework.stereotype.Repository;
  5 +
  6 +import javax.annotation.Resource;import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 支付序号的 Redis DAO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Repository
  14 +public class PayNoRedisDAO {
  15 +
  16 + @Resource
  17 + private StringRedisTemplate stringRedisTemplate;
  18 +
  19 + /**
  20 + * 生成序号
  21 + *
  22 + * @param prefix 前缀
  23 + * @return 序号
  24 + */
  25 + public String generate(String prefix) {
  26 + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN);
  27 + Long no = stringRedisTemplate.opsForValue().increment(noPrefix);
  28 + return noPrefix + no;
  29 + }
  30 +
  31 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderNotifyStatusEnum.java deleted
1   -package cn.iocoder.yudao.module.pay.enums.order;
2   -
3   -import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -/**
8   - * 支付订单的通知状态枚举
9   - *
10   - * @author 芋道源码
11   - */
12   -@Getter
13   -@AllArgsConstructor
14   -public enum PayOrderNotifyStatusEnum implements IntArrayValuable {
15   -
16   - NO(0, "未通知"),
17   - SUCCESS(10, "通知成功"),
18   - FAILURE(20, "通知失败")
19   - ;
20   -
21   - private final Integer status;
22   - private final String name;
23   -
24   - @Override
25   - public int[] array() {
26   - return new int[0];
27   - }
28   -
29   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java deleted
1   -package cn.iocoder.yudao.module.pay.enums.order;
2   -
3   -import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -/**
8   - * 支付订单的退款状态枚举
9   - *
10   - * @author 芋道源码
11   - */
12   -@Getter
13   -@AllArgsConstructor
14   -public enum PayOrderRefundStatusEnum implements IntArrayValuable {
15   -
16   - NO(0, "未退款"),
17   - PART(10, "部分退款"),
18   - ALL(20, "全部退款")
19   - ;
20   -
21   - private final Integer status;
22   - private final String name;
23   -
24   - @Override
25   - public int[] array() {
26   - return new int[0];
27   - }
28   -
29   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.framework.pay.config;
  2 +
  3 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
  4 +import org.springframework.context.annotation.Configuration;
  5 +
  6 +@Configuration(proxyBeanMethods = false)
  7 +@EnableConfigurationProperties(PayProperties.class)
  8 +public class PayConfiguration {
  9 +}
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java renamed to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java
1   -package cn.iocoder.yudao.framework.pay.config;
  1 +package cn.iocoder.yudao.module.pay.framework.pay.config;
2 2  
3 3 import lombok.Data;
4 4 import org.hibernate.validator.constraints.URL;
... ... @@ -12,6 +12,9 @@ import javax.validation.constraints.NotEmpty;
12 12 @Data
13 13 public class PayProperties {
14 14  
  15 + private static final String ORDER_NO_PREFIX = "P";
  16 + private static final String REFUND_NO_PREFIX = "R";
  17 +
15 18 /**
16 19 * 支付回调地址
17 20 *
... ... @@ -34,4 +37,16 @@ public class PayProperties {
34 37 @URL(message = "支付回调地址的格式必须是 URL")
35 38 private String refundNotifyUrl;
36 39  
  40 + /**
  41 + * 支付订单 no 的前缀
  42 + */
  43 + @NotEmpty(message = "支付订单 no 的前缀不能为空")
  44 + private String orderNoPrefix = ORDER_NO_PREFIX;
  45 +
  46 + /**
  47 + * 退款订单 no 的前缀
  48 + */
  49 + @NotEmpty(message = "退款订单 no 的前缀不能为空")
  50 + private String refundNoPrefix = REFUND_NO_PREFIX;
  51 +
37 52 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/package-info.java 0 → 100644
  1 +/**
  2 + * 占位,无实际作用
  3 + */
  4 +package cn.iocoder.yudao.module.pay.framework.pay.core;
0 5 \ No newline at end of file
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppService.java
... ... @@ -14,29 +14,29 @@ import java.util.List;
14 14 import java.util.Map;
15 15  
16 16 /**
17   - * 支付应用信息 Service 接口
  17 + * 支付应用 Service 接口
18 18 *
19 19 * @author 芋艿
20 20 */
21 21 public interface PayAppService {
22 22  
23 23 /**
24   - * 创建支付应用信息
  24 + * 创建支付应用
25 25 *
26   - * @param createReqVO 创建信息
  26 + * @param createReqVO 创建
27 27 * @return 编号
28 28 */
29 29 Long createApp(@Valid PayAppCreateReqVO createReqVO);
30 30  
31 31 /**
32   - * 更新支付应用信息
  32 + * 更新支付应用
33 33 *
34   - * @param updateReqVO 更新信息
  34 + * @param updateReqVO 更新
35 35 */
36 36 void updateApp(@Valid PayAppUpdateReqVO updateReqVO);
37 37  
38 38 /**
39   - * 修改应用信息状态
  39 + * 修改应用状态
40 40 *
41 41 * @param id 应用编号
42 42 * @param status 状态
... ... @@ -44,33 +44,40 @@ public interface PayAppService {
44 44 void updateAppStatus(Long id, Integer status);
45 45  
46 46 /**
47   - * 删除支付应用信息
  47 + * 删除支付应用
48 48 *
49 49 * @param id 编号
50 50 */
51 51 void deleteApp(Long id);
52 52  
53 53 /**
54   - * 获得支付应用信息
  54 + * 获得支付应用
55 55 *
56 56 * @param id 编号
57   - * @return 支付应用信息
  57 + * @return 支付应用
58 58 */
59 59 PayAppDO getApp(Long id);
60 60  
61 61 /**
62   - * 获得支付应用信息列表
  62 + * 获得支付应用列表
63 63 *
64 64 * @param ids 编号
65   - * @return 支付应用信息列表
  65 + * @return 支付应用列表
66 66 */
67 67 List<PayAppDO> getAppList(Collection<Long> ids);
68 68  
69 69 /**
70   - * 获得支付应用信息分页
  70 + * 获得支付应用列表
  71 + *
  72 + * @return 支付应用列表
  73 + */
  74 + List<PayAppDO> getAppList();
  75 +
  76 + /**
  77 + * 获得支付应用分页
71 78 *
72 79 * @param pageReqVO 分页查询
73   - * @return 支付应用信息分页
  80 + * @return 支付应用分页
74 81 */
75 82 PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO);
76 83  
... ... @@ -91,7 +98,7 @@ public interface PayAppService {
91 98 * 如果不合法,抛出 {@link ServiceException} 业务异常
92 99 *
93 100 * @param id 应用编号
94   - * @return 应用信息
  101 + * @return 应用
95 102 */
96 103 PayAppDO validPayApp(Long id);
97 104  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java
1 1 package cn.iocoder.yudao.module.pay.service.app;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4   -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO;
7 6 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO;
... ... @@ -9,10 +8,7 @@ import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppUpdateReqVO;
9 8 import cn.iocoder.yudao.module.pay.convert.app.PayAppConvert;
10 9 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
11 10 import cn.iocoder.yudao.module.pay.dal.mysql.app.PayAppMapper;
12   -import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
13 11 import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
14   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
15   -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
16 12 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
17 13 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
18 14 import org.springframework.context.annotation.Lazy;
... ... @@ -77,10 +73,10 @@ public class PayAppServiceImpl implements PayAppService {
77 73 validateAppExists(id);
78 74 // 校验关联数据是否存在
79 75 if (orderService.getOrderCountByAppId(id) > 0) {
80   - throw exception(PAY_APP_EXIST_ORDER_CANT_DELETE);
  76 + throw exception(APP_EXIST_ORDER_CANT_DELETE);
81 77 }
82 78 if (refundService.getRefundCountByAppId(id) > 0) {
83   - throw exception(PAY_APP_EXIST_REFUND_CANT_DELETE);
  79 + throw exception(APP_EXIST_REFUND_CANT_DELETE);
84 80 }
85 81  
86 82 // 删除
... ... @@ -89,7 +85,7 @@ public class PayAppServiceImpl implements PayAppService {
89 85  
90 86 private void validateAppExists(Long id) {
91 87 if (appMapper.selectById(id) == null) {
92   - throw exception(PAY_APP_NOT_FOUND);
  88 + throw exception(APP_NOT_FOUND);
93 89 }
94 90 }
95 91  
... ... @@ -104,6 +100,11 @@ public class PayAppServiceImpl implements PayAppService {
104 100 }
105 101  
106 102 @Override
  103 + public List<PayAppDO> getAppList() {
  104 + return appMapper.selectList();
  105 + }
  106 +
  107 + @Override
107 108 public PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO) {
108 109 return appMapper.selectPage(pageReqVO);
109 110 }
... ... @@ -113,11 +114,11 @@ public class PayAppServiceImpl implements PayAppService {
113 114 PayAppDO app = appMapper.selectById(id);
114 115 // 校验是否存在
115 116 if (app == null) {
116   - throw exception(ErrorCodeConstants.PAY_APP_NOT_FOUND);
  117 + throw exception(ErrorCodeConstants.APP_NOT_FOUND);
117 118 }
118 119 // 校验是否禁用
119 120 if (CommonStatusEnum.DISABLE.getStatus().equals(app.getStatus())) {
120   - throw exception(ErrorCodeConstants.PAY_APP_IS_DISABLE);
  121 + throw exception(ErrorCodeConstants.APP_IS_DISABLE);
121 122 }
122 123 return app;
123 124 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
... ... @@ -32,8 +32,7 @@ import java.util.List;
32 32 import java.util.concurrent.TimeUnit;
33 33  
34 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
35   -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR;
36   -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS;
  35 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
37 36  
38 37 /**
39 38 * 支付渠道 Service 实现类
... ... @@ -142,7 +141,7 @@ public class PayChannelServiceImpl implements PayChannelService {
142 141 // 解析配置
143 142 Class<? extends PayClientConfig> payClass = PayChannelEnum.getByCode(code).getConfigClass();
144 143 if (ObjectUtil.isNull(payClass)) {
145   - throw exception(CHANNEL_NOT_EXISTS);
  144 + throw exception(CHANNEL_NOT_FOUND);
146 145 }
147 146 PayClientConfig config = JsonUtils.parseObject2(configStr, payClass);
148 147 Assert.notNull(config);
... ... @@ -167,7 +166,7 @@ public class PayChannelServiceImpl implements PayChannelService {
167 166 private PayChannelDO validateChannelExists(Long id) {
168 167 PayChannelDO channel = channelMapper.selectById(id);
169 168 if (channel == null) {
170   - throw exception(CHANNEL_NOT_EXISTS);
  169 + throw exception(CHANNEL_NOT_FOUND);
171 170 }
172 171 return channel;
173 172 }
... ... @@ -203,10 +202,10 @@ public class PayChannelServiceImpl implements PayChannelService {
203 202  
204 203 private void validPayChannel(PayChannelDO channel) {
205 204 if (channel == null) {
206   - throw exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND);
  205 + throw exception(CHANNEL_NOT_FOUND);
207 206 }
208 207 if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) {
209   - throw exception(ErrorCodeConstants.PAY_CHANNEL_IS_DISABLE);
  208 + throw exception(CHANNEL_IS_DISABLE);
210 209 }
211 210 }
212 211  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
... ... @@ -119,7 +119,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
119 119 new PayDemoOrderDO().setPayStatus(true).setPayTime(LocalDateTime.now())
120 120 .setPayChannelCode(payOrder.getChannelCode()));
121 121 if (updateCount == 0) {
122   - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
  122 + throw exception(DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
123 123 }
124 124 }
125 125  
... ... @@ -137,44 +137,44 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
137 137 // 1.1 校验订单是否存在
138 138 PayDemoOrderDO order = payDemoOrderMapper.selectById(id);
139 139 if (order == null) {
140   - throw exception(PAY_DEMO_ORDER_NOT_FOUND);
  140 + throw exception(DEMO_ORDER_NOT_FOUND);
141 141 }
142 142 // 1.2 校验订单未支付
143 143 if (order.getPayStatus()) {
144 144 log.error("[validateDemoOrderCanPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]",
145 145 id, toJsonString(order));
146   - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
  146 + throw exception(DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
147 147 }
148 148 // 1.3 校验支付订单匹配
149 149 if (notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
150 150 log.error("[validateDemoOrderCanPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]",
151 151 id, payOrderId, toJsonString(order));
152   - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
  152 + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
153 153 }
154 154  
155 155 // 2.1 校验支付单是否存在
156 156 PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
157 157 if (payOrder == null) {
158 158 log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId);
159   - throw exception(PAY_ORDER_NOT_FOUND);
  159 + throw exception(ORDER_NOT_FOUND);
160 160 }
161 161 // 2.2 校验支付单已支付
162 162 if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
163 163 log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]",
164 164 id, payOrderId, toJsonString(payOrder));
165   - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
  165 + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
166 166 }
167 167 // 2.3 校验支付金额一致
168 168 if (notEqual(payOrder.getPrice(), order.getPrice())) {
169 169 log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]",
170 170 id, payOrderId, toJsonString(order), toJsonString(payOrder));
171   - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
  171 + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
172 172 }
173 173 // 2.4 校验支付订单匹配(二次)
174 174 if (notEqual(payOrder.getMerchantOrderId(), id.toString())) {
175 175 log.error("[validateDemoOrderCanPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]",
176 176 id, payOrderId, toJsonString(payOrder));
177   - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
  177 + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
178 178 }
179 179 return payOrder;
180 180 }
... ... @@ -203,15 +203,15 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
203 203 // 校验订单是否存在
204 204 PayDemoOrderDO order = payDemoOrderMapper.selectById(id);
205 205 if (order == null) {
206   - throw exception(PAY_DEMO_ORDER_NOT_FOUND);
  206 + throw exception(DEMO_ORDER_NOT_FOUND);
207 207 }
208 208 // 校验订单是否支付
209 209 if (!order.getPayStatus()) {
210   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_NOT_PAID);
  210 + throw exception(DEMO_ORDER_REFUND_FAIL_NOT_PAID);
211 211 }
212 212 // 校验订单是否已退款
213 213 if (order.getPayRefundId() != null) {
214   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUNDED);
  214 + throw exception(DEMO_ORDER_REFUND_FAIL_REFUNDED);
215 215 }
216 216 return order;
217 217 }
... ... @@ -229,35 +229,35 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
229 229 // 1.1 校验示例订单
230 230 PayDemoOrderDO order = payDemoOrderMapper.selectById(id);
231 231 if (order == null) {
232   - throw exception(PAY_DEMO_ORDER_NOT_FOUND);
  232 + throw exception(DEMO_ORDER_NOT_FOUND);
233 233 }
234 234 // 1.2 校验退款订单匹配
235 235 if (Objects.equals(order.getPayOrderId(), payRefundId)) {
236 236 log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!order 数据是:{}]",
237 237 id, payRefundId, toJsonString(order));
238   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
  238 + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
239 239 }
240 240  
241 241 // 2.1 校验退款订单
242 242 PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
243 243 if (payRefund == null) {
244   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND);
  244 + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND);
245 245 }
246 246 // 2.2
247 247 if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
248   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS);
  248 + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS);
249 249 }
250 250 // 2.3 校验退款金额一致
251 251 if (notEqual(payRefund.getRefundPrice(), order.getPrice())) {
252 252 log.error("[validateDemoOrderCanRefunded][order({}) payRefund({}) 退款金额不匹配,请进行处理!order 数据是:{},payRefund 数据是:{}]",
253 253 id, payRefundId, toJsonString(order), toJsonString(payRefund));
254   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH);
  254 + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH);
255 255 }
256 256 // 2.4 校验退款订单匹配(二次)
257 257 if (notEqual(payRefund.getMerchantOrderId(), id.toString())) {
258 258 log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]",
259 259 id, payRefundId, toJsonString(payRefund));
260   - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
  260 + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
261 261 }
262 262 return payRefund;
263 263 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyService.java
1 1 package cn.iocoder.yudao.module.pay.service.notify;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
  5 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
  6 +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
3 7 import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
4 8  
5 9 import javax.validation.Valid;
  10 +import java.util.List;
6 11  
7 12 /**
8   - * 支付通知 Service 接口
  13 + * 回调通知 Service 接口
9 14 *
10 15 * @author 芋道源码
11 16 */
12 17 public interface PayNotifyService {
13 18  
14 19 /**
15   - * 创建支付通知任务
  20 + * 创建回调通知任务
16 21 *
17 22 * @param reqDTO 任务信息
18 23 */
19 24 void createPayNotifyTask(@Valid PayNotifyTaskCreateReqDTO reqDTO);
20 25  
21 26 /**
22   - * 执行支付通知
  27 + * 执行回调通知
23 28 *
24 29 * 注意,该方法提供给定时任务调用。目前是 yudao-server 进行调用
25 30 * @return 通知数量
26 31 */
27 32 int executeNotify() throws InterruptedException;
28 33  
  34 + /**
  35 + * 获得回调通知
  36 + *
  37 + * @param id 编号
  38 + * @return 回调通知
  39 + */
  40 + PayNotifyTaskDO getNotifyTask(Long id);
  41 +
  42 + /**
  43 + * 获得回调通知分页
  44 + *
  45 + * @param pageReqVO 分页查询
  46 + * @return 回调通知分页
  47 + */
  48 + PageResult<PayNotifyTaskDO> getNotifyTaskPage(PayNotifyTaskPageReqVO pageReqVO);
  49 +
  50 + /**
  51 + * 获得回调日志列表
  52 + *
  53 + * @param taskId 任务编号
  54 + * @return 日志列表
  55 + */
  56 + List<PayNotifyLogDO> getNotifyLogList(Long taskId);
  57 +
29 58 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
... ... @@ -6,11 +6,13 @@ import cn.hutool.core.util.ObjectUtil;
6 6 import cn.hutool.http.HttpResponse;
7 7 import cn.hutool.http.HttpUtil;
8 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  9 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 10 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
10 11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
11 12 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 13 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
13 14 import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
  15 +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
14 16 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
15 17 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
16 18 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
... ... @@ -72,15 +74,15 @@ public class PayNotifyServiceImpl implements PayNotifyService {
72 74 private PayRefundService refundService;
73 75  
74 76 @Resource
75   - private PayNotifyTaskMapper payNotifyTaskMapper;
  77 + private PayNotifyTaskMapper notifyTaskMapper;
76 78 @Resource
77   - private PayNotifyLogMapper payNotifyLogMapper;
  79 + private PayNotifyLogMapper notifyLogMapper;
78 80  
79 81 @Resource(name = NOTIFY_THREAD_POOL_TASK_EXECUTOR)
80 82 private ThreadPoolTaskExecutor threadPoolTaskExecutor;
81 83  
82 84 @Resource
83   - private PayNotifyLockRedisDAO payNotifyLockCoreRedisDAO;
  85 + private PayNotifyLockRedisDAO notifyLockCoreRedisDAO;
84 86  
85 87 @Resource
86 88 @Lazy // 循环依赖(自己依赖自己),避免报错
... ... @@ -105,7 +107,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
105 107 }
106 108  
107 109 // 执行插入
108   - payNotifyTaskMapper.insert(task);
  110 + notifyTaskMapper.insert(task);
109 111  
110 112 // 必须在事务提交后,在发起任务,否则 PayNotifyTaskDO 还没入库,就提前回调接入的业务
111 113 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
... ... @@ -119,7 +121,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
119 121 @Override
120 122 public int executeNotify() throws InterruptedException {
121 123 // 获得需要通知的任务
122   - List<PayNotifyTaskDO> tasks = payNotifyTaskMapper.selectListByNotify();
  124 + List<PayNotifyTaskDO> tasks = notifyTaskMapper.selectListByNotify();
123 125 if (CollUtil.isEmpty(tasks)) {
124 126 return 0;
125 127 }
... ... @@ -164,11 +166,11 @@ public class PayNotifyServiceImpl implements PayNotifyService {
164 166 */
165 167 public void executeNotify(PayNotifyTaskDO task) {
166 168 // 分布式锁,避免并发问题
167   - payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> {
  169 + notifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> {
168 170 // 校验,当前任务是否已经被通知过
169 171 // 虽然已经通过分布式加锁,但是可能同时满足通知的条件,然后都去获得锁。此时,第一个执行完后,第二个还是能拿到锁,然后会再执行一次。
170 172 // 因此,此处我们通过第 notifyTimes 通知次数是否匹配来判断
171   - PayNotifyTaskDO dbTask = payNotifyTaskMapper.selectById(task.getId());
  173 + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId());
172 174 if (ObjectUtil.notEqual(task.getNotifyTimes(), dbTask.getNotifyTimes())) {
173 175 log.warn("[executeNotifySync][task({}) 任务被忽略,原因是它的通知不是第 ({}) 次,可能是因为并发执行了]",
174 176 JsonUtils.toJsonString(task), dbTask.getNotifyTimes());
... ... @@ -197,7 +199,7 @@ public class PayNotifyServiceImpl implements PayNotifyService {
197 199 // 记录 PayNotifyLog 日志
198 200 String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) :
199 201 JsonUtils.toJsonString(invokeResult);
200   - payNotifyLogMapper.insert(PayNotifyLogDO.builder().taskId(task.getId())
  202 + notifyLogMapper.insert(PayNotifyLogDO.builder().taskId(task.getId())
201 203 .notifyTimes(task.getNotifyTimes() + 1).status(newStatus).response(response).build());
202 204 }
203 205  
... ... @@ -250,22 +252,37 @@ public class PayNotifyServiceImpl implements PayNotifyService {
250 252 // 情况一:调用成功
251 253 if (invokeResult != null && invokeResult.isSuccess()) {
252 254 updateTask.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus());
253   - payNotifyTaskMapper.updateById(updateTask);
  255 + notifyTaskMapper.updateById(updateTask);
254 256 return updateTask.getStatus();
255 257 }
256 258 // 情况二:调用失败、调用异常
257 259 // 2.1 超过最大回调次数
258 260 if (updateTask.getNotifyTimes() >= PayNotifyTaskDO.NOTIFY_FREQUENCY.length) {
259 261 updateTask.setStatus(PayNotifyStatusEnum.FAILURE.getStatus());
260   - payNotifyTaskMapper.updateById(updateTask);
  262 + notifyTaskMapper.updateById(updateTask);
261 263 return updateTask.getStatus();
262 264 }
263 265 // 2.2 未超过最大回调次数
264 266 updateTask.setNextNotifyTime(addTime(Duration.ofSeconds(PayNotifyTaskDO.NOTIFY_FREQUENCY[updateTask.getNotifyTimes()])));
265 267 updateTask.setStatus(invokeException != null ? PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()
266 268 : PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus());
267   - payNotifyTaskMapper.updateById(updateTask);
  269 + notifyTaskMapper.updateById(updateTask);
268 270 return updateTask.getStatus();
269 271 }
270 272  
  273 + @Override
  274 + public PayNotifyTaskDO getNotifyTask(Long id) {
  275 + return notifyTaskMapper.selectById(id);
  276 + }
  277 +
  278 + @Override
  279 + public PageResult<PayNotifyTaskDO> getNotifyTaskPage(PayNotifyTaskPageReqVO pageReqVO) {
  280 + return notifyTaskMapper.selectPage(pageReqVO);
  281 + }
  282 +
  283 + @Override
  284 + public List<PayNotifyLogDO> getNotifyLogList(Long taskId) {
  285 + return notifyLogMapper.selectListByTaskId(taskId);
  286 + }
  287 +
271 288 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java deleted
1   -package cn.iocoder.yudao.module.pay.service.order;
2   -
3   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4   -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
5   -
6   -import java.util.Collection;
7   -import java.util.List;
8   -import java.util.Map;
9   -
10   -// TODO 芋艿:合并到 PayOrder 里;
11   -/**
12   - * 支付订单 Service 接口
13   - *
14   - * @author aquan
15   - */
16   -public interface PayOrderExtensionService {
17   -
18   - /**
19   - * 获得支付订单
20   - *
21   - * @param id 编号
22   - * @return 支付订单
23   - */
24   - PayOrderExtensionDO getOrderExtension(Long id);
25   -
26   - /**
27   - * 获得支付订单
28   - * 列表
29   - *
30   - * @param ids 编号
31   - * @return 支付订单
32   - * 列表
33   - */
34   - List<PayOrderExtensionDO> getOrderExtensionList(Collection<Long> ids);
35   -
36   - /**
37   - * 根据订单成功的 扩展订单ID 查询所有的扩展订单转 成 map 返回
38   - *
39   - * @param successExtensionIdList 订单 ID 集合
40   - * @return 订单扩展 map 集合
41   - */
42   - default Map<Long, PayOrderExtensionDO> getOrderExtensionMap(Collection<Long> successExtensionIdList) {
43   - List<PayOrderExtensionDO> list = getOrderExtensionList(successExtensionIdList);
44   - return CollectionUtils.convertMap(list, PayOrderExtensionDO::getId);
45   - }
46   -
47   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.pay.service.order;
2   -
3   -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
4   -import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper;
5   -import org.springframework.stereotype.Service;
6   -import org.springframework.validation.annotation.Validated;
7   -
8   -import javax.annotation.Resource;
9   -import java.util.Collection;
10   -import java.util.List;
11   -
12   -/**
13   - * 支付订单 Service 实现类
14   - *
15   - * @author aquan
16   - */
17   -@Service
18   -@Validated
19   -public class PayOrderExtensionServiceImpl implements PayOrderExtensionService {
20   -
21   - @Resource
22   - private PayOrderExtensionMapper orderExtensionMapper;
23   -
24   - @Override
25   - public PayOrderExtensionDO getOrderExtension(Long id) {
26   - return orderExtensionMapper.selectById(id);
27   - }
28   -
29   - @Override
30   - public List<PayOrderExtensionDO> getOrderExtensionList(Collection<Long> ids) {
31   - return orderExtensionMapper.selectBatchIds(ids);
32   - }
33   -
34   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
1 1 package cn.iocoder.yudao.module.pay.service.order;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 5 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
7 6 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
... ... @@ -9,12 +8,11 @@ import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
9 8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitReqVO;
10 9 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitRespVO;
11 10 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  11 +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
12 12  
13 13 import javax.validation.Valid;
14 14 import javax.validation.constraints.NotEmpty;
15   -import java.util.Collection;
16 15 import java.util.List;
17   -import java.util.Map;
18 16  
19 17 /**
20 18 * 支付订单 Service 接口
... ... @@ -49,45 +47,22 @@ public interface PayOrderService {
49 47 Long getOrderCountByAppId(Long appId);
50 48  
51 49 /**
52   - * 获得支付订单
53   - * 分页
  50 + * 获得支付订单分页
54 51 *
55 52 * @param pageReqVO 分页查询
56   - * @return 支付订单
57   - * 分页
  53 + * @return 支付订单分页
58 54 */
59 55 PageResult<PayOrderDO> getOrderPage(PayOrderPageReqVO pageReqVO);
60 56  
61 57 /**
62   - * 获得支付订单
63   - * 列表, 用于 Excel 导出
  58 + * 获得支付订单列表, 用于 Excel 导出
64 59 *
65 60 * @param exportReqVO 查询条件
66   - * @return 支付订单
67   - * 列表
  61 + * @return 支付订单列表
68 62 */
69 63 List<PayOrderDO> getOrderList(PayOrderExportReqVO exportReqVO);
70 64  
71 65 /**
72   - * 根据 ID 集合获取只包含商品名称的订单集合
73   - *
74   - * @param idList 订单 ID 集合
75   - * @return 只包含商品名称的订单集合
76   - */
77   - List<PayOrderDO> getOrderSubjectList(Collection<Long> idList);
78   -
79   - /**
80   - * 根据订单 ID 集合获取订单商品名称Map集合
81   - *
82   - * @param ids 订单 ID 集合
83   - * @return 订单商品 map 集合
84   - */
85   - default Map<Long, PayOrderDO> getOrderSubjectMap(Collection<Long> ids) {
86   - List<PayOrderDO> list = getOrderSubjectList(ids);
87   - return CollectionUtils.convertMap(list, PayOrderDO::getId);
88   - }
89   -
90   - /**
91 66 * 创建支付单
92 67 *
93 68 * @param reqDTO 创建请求
... ... @@ -122,4 +97,12 @@ public interface PayOrderService {
122 97 */
123 98 void updateOrderRefundPrice(Long id, Integer incrRefundPrice);
124 99  
  100 + /**
  101 + * 获得支付订单
  102 + *
  103 + * @param id 编号
  104 + * @return 支付订单
  105 + */
  106 + PayOrderExtensionDO getOrderExtension(Long id);
  107 +
125 108 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
1 1 package cn.iocoder.yudao.module.pay.service.order;
2 2  
3   -import cn.hutool.core.date.DateUtil;
4 3 import cn.hutool.core.lang.Pair;
5 4 import cn.hutool.core.util.ObjectUtil;
6   -import cn.hutool.core.util.RandomUtil;
7 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
8 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 8 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
10   -import cn.iocoder.yudao.framework.pay.config.PayProperties;
11 9 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
12 10 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
13 11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
... ... @@ -26,22 +24,21 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
26 24 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
27 25 import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper;
28 26 import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper;
  27 +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
29 28 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
30   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
31   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
32 29 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
  30 +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
33 31 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
34 32 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
35 33 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
36 34 import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
  35 +import cn.iocoder.yudao.module.pay.util.MoneyUtils;
37 36 import lombok.extern.slf4j.Slf4j;
38 37 import org.springframework.stereotype.Service;
39 38 import org.springframework.transaction.annotation.Transactional;
40 39 import org.springframework.validation.annotation.Validated;
41 40  
42 41 import javax.annotation.Resource;
43   -import java.time.LocalDateTime;
44   -import java.util.Collection;
45 42 import java.util.List;
46 43 import java.util.Objects;
47 44  
... ... @@ -69,6 +66,8 @@ public class PayOrderServiceImpl implements PayOrderService {
69 66 private PayOrderMapper orderMapper;
70 67 @Resource
71 68 private PayOrderExtensionMapper orderExtensionMapper;
  69 + @Resource
  70 + private PayNoRedisDAO noRedisDAO;
72 71  
73 72 @Resource
74 73 private PayAppService appService;
... ... @@ -102,12 +101,6 @@ public class PayOrderServiceImpl implements PayOrderService {
102 101 return orderMapper.selectList(exportReqVO);
103 102 }
104 103  
105   - // TODO @艿艿:需要优化。不确定这个方法的作用
106   - @Override
107   - public List<PayOrderDO> getOrderSubjectList(Collection<Long> idList) {
108   - return orderMapper.findByIdListQueryOrderSubject(idList);
109   - }
110   -
111 104 @Override
112 105 public Long createOrder(PayOrderCreateReqDTO reqDTO) {
113 106 // 校验 App
... ... @@ -125,11 +118,11 @@ public class PayOrderServiceImpl implements PayOrderService {
125 118 // 创建支付交易单
126 119 order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId())
127 120 // 商户相关字段
128   - .setNotifyUrl(app.getOrderNotifyUrl()).setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus())
  121 + .setNotifyUrl(app.getOrderNotifyUrl())
129 122 // 订单相关字段
130 123 .setStatus(PayOrderStatusEnum.WAITING.getStatus())
131 124 // 退款相关字段
132   - .setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0);
  125 + .setRefundPrice(0);
133 126 orderMapper.insert(order);
134 127 return order.getId();
135 128 }
... ... @@ -143,8 +136,9 @@ public class PayOrderServiceImpl implements PayOrderService {
143 136 PayClient client = payClientFactory.getPayClient(channel.getId());
144 137  
145 138 // 2. 插入 PayOrderExtensionDO
  139 + String no = noRedisDAO.generate(payProperties.getOrderNoPrefix());
146 140 PayOrderExtensionDO orderExtension = PayOrderConvert.INSTANCE.convert(reqVO, userIp)
147   - .setOrderId(order.getId()).setNo(generateOrderExtensionNo())
  141 + .setOrderId(order.getId()).setNo(no)
148 142 .setChannelId(channel.getId()).setChannelCode(channel.getCode())
149 143 .setStatus(PayOrderStatusEnum.WAITING.getStatus());
150 144 orderExtensionMapper.insert(orderExtension);
... ... @@ -162,10 +156,10 @@ public class PayOrderServiceImpl implements PayOrderService {
162 156  
163 157 // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功
164 158 if (unifiedOrderResp != null) {
165   - notifyPayOrder(channel, unifiedOrderResp);
  159 + getSelf().notifyOrder(channel, unifiedOrderResp);
166 160 // 如有渠道错误码,则抛出业务异常,提示用户
167 161 if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) {
168   - throw exception(PAY_ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(),
  162 + throw exception(ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(),
169 163 unifiedOrderResp.getChannelErrorMsg());
170 164 }
171 165 // 此处需要读取最新的状态
... ... @@ -177,13 +171,13 @@ public class PayOrderServiceImpl implements PayOrderService {
177 171 private PayOrderDO validateOrderCanSubmit(Long id) {
178 172 PayOrderDO order = orderMapper.selectById(id);
179 173 if (order == null) { // 是否存在
180   - throw exception(PAY_ORDER_NOT_FOUND);
  174 + throw exception(ORDER_NOT_FOUND);
181 175 }
182 176 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
183   - throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
  177 + throw exception(ORDER_STATUS_IS_NOT_WAITING);
184 178 }
185 179 if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期
186   - throw exception(PAY_ORDER_IS_EXPIRED);
  180 + throw exception(ORDER_IS_EXPIRED);
187 181 }
188 182 return order;
189 183 }
... ... @@ -196,7 +190,7 @@ public class PayOrderServiceImpl implements PayOrderService {
196 190 PayClient client = payClientFactory.getPayClient(channel.getId());
197 191 if (client == null) {
198 192 log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
199   - throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
  193 + throw exception(CHANNEL_NOT_FOUND);
200 194 }
201 195 return channel;
202 196 }
... ... @@ -211,61 +205,22 @@ public class PayOrderServiceImpl implements PayOrderService {
211 205 return payProperties.getOrderNotifyUrl() + "/" + channel.getId();
212 206 }
213 207  
214   - private String generateOrderExtensionNo() {
215   -// wx
216   -// 2014
217   -// 10
218   -// 27
219   -// 20
220   -// 09
221   -// 39
222   -// 5522657
223   -// a690389285100
224   - // 目前的算法
225   - // 时间序列,年月日时分秒 14 位
226   - // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整
227   - return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列
228   - RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒
229   - ;
230   - }
231   -
232 208 @Override
233   - @Transactional(rollbackFor = Exception.class)
234 209 public void notifyOrder(Long channelId, PayOrderRespDTO notify) {
235 210 // 校验支付渠道是否有效
236 211 PayChannelDO channel = channelService.validPayChannel(channelId);
237 212 // 更新支付订单为已支付
238   - TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify));
  213 + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyOrder(channel, notify));
239 214 }
240 215  
241   - @Override
242   - public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) {
243   - PayOrderDO order = orderMapper.selectById(id);
244   - if (order == null) {
245   - throw exception(PAY_ORDER_NOT_FOUND);
246   - }
247   - if (!PayOrderStatusEnum.isSuccess(order.getStatus())) {
248   - throw exception(PAY_REFUND_PRICE_EXCEED);
249   - }
250   - if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) {
251   - throw exception(PAY_REFUND_PRICE_EXCEED);
252   - }
253   -
254   - // 更新订单
255   - PayOrderDO updateObj = new PayOrderDO()
256   - .setRefundPrice(order.getRefundPrice() + incrRefundPrice)
257   - .setRefundTimes(order.getRefundTimes() + 1);
258   - if (Objects.equals(updateObj.getRefundPrice(), order.getPrice())) {
259   - updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus())
260   - .setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus());
261   - } else {
262   - updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus())
263   - .setRefundStatus(PayOrderRefundStatusEnum.PART.getStatus());
264   - }
265   - orderMapper.updateByIdAndStatus(id, PayOrderStatusEnum.SUCCESS.getStatus(), updateObj);
266   - }
267   -
268   - private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) {
  216 + /**
  217 + * 通知并更新订单的支付结果
  218 + *
  219 + * @param channel 支付渠道
  220 + * @param notify 通知
  221 + */
  222 + @Transactional(rollbackFor = Exception.class) // 注意,如果是方法内调用该方法,需要通过 getSelf().notifyPayOrder(channel, notify) 调用,否则事务不生效
  223 + public void notifyOrder(PayChannelDO channel, PayOrderRespDTO notify) {
269 224 // 情况一:支付成功的回调
270 225 if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) {
271 226 notifyOrderSuccess(channel, notify);
... ... @@ -275,20 +230,22 @@ public class PayOrderServiceImpl implements PayOrderService {
275 230 if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) {
276 231 notifyOrderClosed(channel, notify);
277 232 }
  233 + // 情况三:WAITING:无需处理
  234 + // 情况四:REFUND:通过退款回调处理
278 235 }
279 236  
280 237 private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
281 238 // 1. 更新 PayOrderExtensionDO 支付成功
282   - PayOrderExtensionDO orderExtension = updateOrderExtensionSuccess(notify);
  239 + PayOrderExtensionDO orderExtension = updateOrderSuccess(notify);
283 240 // 2. 更新 PayOrderDO 支付成功
284   - Pair<Boolean, PayOrderDO> order = updateOrderExtensionSuccess(channel, orderExtension, notify);
285   - if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调
  241 + Boolean paid = updateOrderSuccess(channel, orderExtension, notify);
  242 + if (paid) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调
286 243 return;
287 244 }
288 245  
289 246 // 3. 插入支付通知记录
290 247 notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
291   - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getValue().getId()).build());
  248 + .type(PayNotifyTypeEnum.ORDER.getType()).dataId(orderExtension.getOrderId()).build());
292 249 }
293 250  
294 251 /**
... ... @@ -297,25 +254,25 @@ public class PayOrderServiceImpl implements PayOrderService {
297 254 * @param notify 通知
298 255 * @return PayOrderExtensionDO 对象
299 256 */
300   - private PayOrderExtensionDO updateOrderExtensionSuccess(PayOrderRespDTO notify) {
  257 + private PayOrderExtensionDO updateOrderSuccess(PayOrderRespDTO notify) {
301 258 // 1. 查询 PayOrderExtensionDO
302 259 PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
303 260 if (orderExtension == null) {
304   - throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
  261 + throw exception(ORDER_EXTENSION_NOT_FOUND);
305 262 }
306 263 if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
307 264 log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新]", orderExtension.getId());
308 265 return orderExtension;
309 266 }
310 267 if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
311   - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  268 + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
312 269 }
313 270  
314 271 // 2. 更新 PayOrderExtensionDO
315 272 int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(),
316 273 PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build());
317 274 if (updateCounts == 0) { // 校验状态,必须是待支付
318   - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  275 + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
319 276 }
320 277 log.info("[updateOrderExtensionSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
321 278 return orderExtension;
... ... @@ -327,37 +284,37 @@ public class PayOrderServiceImpl implements PayOrderService {
327 284 * @param channel 支付渠道
328 285 * @param orderExtension 支付拓展单
329 286 * @param notify 通知回调
330   - * @return key:是否之前已经成功回调
331   - * value:PayOrderDO 对象
  287 + * @return 是否之前已经成功回调
332 288 */
333   - private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
334   - PayOrderRespDTO notify) {
  289 + private Boolean updateOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
  290 + PayOrderRespDTO notify) {
335 291 // 1. 判断 PayOrderDO 是否处于待支付
336 292 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
337 293 if (order == null) {
338   - throw exception(PAY_ORDER_NOT_FOUND);
  294 + throw exception(ORDER_NOT_FOUND);
339 295 }
340 296 if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新
341   - && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) {
  297 + && Objects.equals(order.getExtensionId(), orderExtension.getId())) {
342 298 log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新]", order.getId());
343   - return Pair.of(true, order);
  299 + return true;
344 300 }
345 301 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
346   - throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
  302 + throw exception(ORDER_STATUS_IS_NOT_WAITING);
347 303 }
348 304  
349 305 // 2. 更新 PayOrderDO
350 306 int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
351 307 PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus())
352 308 .channelId(channel.getId()).channelCode(channel.getCode())
353   - .successTime(notify.getSuccessTime()).successExtensionId(orderExtension.getId())
  309 + .successTime(notify.getSuccessTime()).extensionId(orderExtension.getId()).no(orderExtension.getNo())
354 310 .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId())
355   - .notifyTime(LocalDateTime.now()).build());
  311 + .channelFeeRate(channel.getFeeRate()).channelFeePrice(MoneyUtils.calculateRatePrice(order.getPrice(), channel.getFeeRate()))
  312 + .build());
356 313 if (updateCounts == 0) { // 校验状态,必须是待支付
357   - throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
  314 + throw exception(ORDER_STATUS_IS_NOT_WAITING);
358 315 }
359 316 log.info("[updateOrderExtensionSuccess][支付订单({}) 更新为已支付]", order.getId());
360   - return Pair.of(false, order);
  317 + return false;
361 318 }
362 319  
363 320 private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) {
... ... @@ -368,7 +325,7 @@ public class PayOrderServiceImpl implements PayOrderService {
368 325 // 1. 查询 PayOrderExtensionDO
369 326 PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
370 327 if (orderExtension == null) {
371   - throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
  328 + throw exception(ORDER_EXTENSION_NOT_FOUND);
372 329 }
373 330 if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { // 如果已经是关闭,直接返回,不用重复更新
374 331 log.info("[updateOrderExtensionClosed][支付拓展单({}) 已经是支付关闭,无需更新]", orderExtension.getId());
... ... @@ -380,7 +337,7 @@ public class PayOrderServiceImpl implements PayOrderService {
380 337 return;
381 338 }
382 339 if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
383   - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  340 + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
384 341 }
385 342  
386 343 // 2. 更新 PayOrderExtensionDO
... ... @@ -388,9 +345,46 @@ public class PayOrderServiceImpl implements PayOrderService {
388 345 PayOrderExtensionDO.builder().status(PayOrderStatusEnum.CLOSED.getStatus()).channelNotifyData(toJsonString(notify))
389 346 .channelErrorCode(notify.getChannelErrorCode()).channelErrorMsg(notify.getChannelErrorMsg()).build());
390 347 if (updateCounts == 0) { // 校验状态,必须是待支付
391   - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  348 + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
392 349 }
393 350 log.info("[updateOrderExtensionClosed][支付拓展单({}) 更新为支付关闭]", orderExtension.getId());
394 351 }
395 352  
  353 + @Override
  354 + public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) {
  355 + PayOrderDO order = orderMapper.selectById(id);
  356 + if (order == null) {
  357 + throw exception(ORDER_NOT_FOUND);
  358 + }
  359 + if (!PayOrderStatusEnum.isSuccessOrRefund(order.getStatus())) {
  360 + throw exception(ORDER_REFUND_FAIL_STATUS_ERROR);
  361 + }
  362 + if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) {
  363 + throw exception(REFUND_PRICE_EXCEED);
  364 + }
  365 +
  366 + // 更新订单
  367 + PayOrderDO updateObj = new PayOrderDO()
  368 + .setRefundPrice(order.getRefundPrice() + incrRefundPrice)
  369 + .setStatus(PayOrderStatusEnum.REFUND.getStatus());
  370 + int updateCount = orderMapper.updateByIdAndStatus(id, order.getStatus(), updateObj);
  371 + if (updateCount == 0) {
  372 + throw exception(ORDER_REFUND_FAIL_STATUS_ERROR);
  373 + }
  374 + }
  375 +
  376 + @Override
  377 + public PayOrderExtensionDO getOrderExtension(Long id) {
  378 + return orderExtensionMapper.selectById(id);
  379 + }
  380 +
  381 + /**
  382 + * 获得自身的代理对象,解决 AOP 生效问题
  383 + *
  384 + * @return 自己
  385 + */
  386 + private PayOrderServiceImpl getSelf() {
  387 + return SpringUtil.getBean(getClass());
  388 + }
  389 +
396 390 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.service.refund;
3 3 import cn.hutool.core.date.DateUtil;
4 4 import cn.hutool.core.util.RandomUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6   -import cn.iocoder.yudao.framework.pay.config.PayProperties;
7 6 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
8 7 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
9 8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
... ... @@ -21,16 +20,14 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
21 20 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
22 21 import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
23 22 import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
24   -import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
25 23 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
26   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
27 24 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
28 25 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
  26 +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
29 27 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
30 28 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
31 29 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
32 30 import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
33   -import cn.iocoder.yudao.module.pay.service.order.PayOrderExtensionService;
34 31 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
35 32 import lombok.extern.slf4j.Slf4j;
36 33 import org.springframework.stereotype.Service;
... ... @@ -43,6 +40,7 @@ import java.util.List;
43 40  
44 41 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
45 42 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
  43 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_FOUND;
46 44  
47 45 /**
48 46 * 退款订单 Service 实现类
... ... @@ -66,8 +64,6 @@ public class PayRefundServiceImpl implements PayRefundService {
66 64 @Resource
67 65 private PayOrderService orderService;
68 66 @Resource
69   - private PayOrderExtensionService orderExtensionService;
70   - @Resource
71 67 private PayAppService appService;
72 68 @Resource
73 69 private PayChannelService channelService;
... ... @@ -106,13 +102,13 @@ public class PayRefundServiceImpl implements PayRefundService {
106 102 PayClient client = payClientFactory.getPayClient(channel.getId());
107 103 if (client == null) {
108 104 log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
109   - throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
  105 + throw exception(CHANNEL_NOT_FOUND);
110 106 }
111 107 // 1.4 校验退款订单是否已经存在
112 108 PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId(
113 109 app.getId(), reqDTO.getMerchantRefundId());
114 110 if (refund != null) {
115   - throw exception(ErrorCodeConstants.PAY_REFUND_EXISTS);
  111 + throw exception(ErrorCodeConstants.REFUND_EXISTS);
116 112 }
117 113  
118 114 // 2.1 插入退款单
... ... @@ -120,7 +116,7 @@ public class PayRefundServiceImpl implements PayRefundService {
120 116 .setNo(generateRefundNo()).setOrderId(order.getId())
121 117 .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode())
122 118 // 商户相关的字段
123   - .setNotifyUrl(app.getRefundNotifyUrl()).setNotifyStatus(PayNotifyStatusEnum.WAITING.getStatus())
  119 + .setNotifyUrl(app.getRefundNotifyUrl())
124 120 // 渠道相关字段
125 121 .setChannelOrderNo(order.getChannelOrderNo())
126 122 // 退款相关字段
... ... @@ -128,7 +124,7 @@ public class PayRefundServiceImpl implements PayRefundService {
128 124 .setPayPrice(order.getPrice()).setRefundPrice(reqDTO.getPrice());
129 125 refundMapper.insert(refund);
130 126 // 2.2 向渠道发起退款申请
131   - PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
  127 + PayOrderExtensionDO orderExtension = orderService.getOrderExtension(order.getExtensionId());
132 128 PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO()
133 129 .setPayPrice(order.getPrice())
134 130 .setRefundPrice(reqDTO.getPrice())
... ... @@ -136,7 +132,7 @@ public class PayRefundServiceImpl implements PayRefundService {
136 132 .setOutRefundNo(refund.getNo())
137 133 .setNotifyUrl(genChannelRefundNotifyUrl(channel))
138 134 .setReason(reqDTO.getReason());
139   - PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // TODO 增加一个 channelErrorCode、channelErrorMsg 字段
  135 + PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO);
140 136 // 2.3 处理退款返回
141 137 notifyRefund(channel, refundRespDTO);
142 138  
... ... @@ -153,25 +149,21 @@ public class PayRefundServiceImpl implements PayRefundService {
153 149 private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) {
154 150 PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId());
155 151 if (order == null) {
156   - throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
  152 + throw exception(ErrorCodeConstants.ORDER_NOT_FOUND);
157 153 }
158 154 // 校验状态,必须是支付状态
159 155 if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) {
160   - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS);
  156 + throw exception(ErrorCodeConstants.ORDER_STATUS_IS_NOT_SUCCESS);
161 157 }
162 158  
163   - // 是否已经全额退款
164   - if (PayOrderRefundStatusEnum.ALL.getStatus().equals(order.getRefundStatus())) {
165   - throw exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED);
166   - }
167 159 // 校验金额 退款金额不能大于原定的金额
168 160 if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){
169   - throw exception(ErrorCodeConstants.PAY_REFUND_PRICE_EXCEED);
  161 + throw exception(ErrorCodeConstants.REFUND_PRICE_EXCEED);
170 162 }
171 163 // 是否有退款中的订单
172 164 if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(),
173 165 PayRefundStatusEnum.WAITING.getStatus()) > 0) {
174   - throw exception(ErrorCodeConstants.PAY_REFUND_HAS_REFUNDING);
  166 + throw exception(ErrorCodeConstants.REFUND_HAS_REFUNDING);
175 167 }
176 168 return order;
177 169 }
... ... @@ -205,7 +197,6 @@ public class PayRefundServiceImpl implements PayRefundService {
205 197 }
206 198  
207 199 @Override
208   - @Transactional(rollbackFor = Exception.class)
209 200 public void notifyRefund(Long channelId, PayRefundRespDTO notify) {
210 201 // 校验支付渠道是否有效
211 202 channelService.validPayChannel(channelId);
... ... @@ -217,28 +208,33 @@ public class PayRefundServiceImpl implements PayRefundService {
217 208 TenantUtils.execute(channel.getTenantId(), () -> notifyRefund(channel, notify));
218 209 }
219 210  
  211 + // TODO 芋艿:事务问题
220 212 private void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) {
  213 + // 情况一:退款成功
221 214 if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) {
222 215 notifyRefundSuccess(channel, notify);
223   - } else {
  216 + return;
  217 + }
  218 + // 情况二:退款失败
  219 + if (PayRefundStatusRespEnum.isFailure(notify.getStatus())) {
224 220 notifyRefundFailure(channel, notify);
225 221 }
226 222 }
227 223  
228   - private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) {
  224 + public void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) {
229 225 // 1.1 查询 PayRefundDO
230 226 PayRefundDO refund = refundMapper.selectByAppIdAndNo(
231 227 channel.getAppId(), notify.getOutRefundNo());
232 228 if (refund == null) {
233   - throw exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND);
  229 + throw exception(ErrorCodeConstants.REFUND_NOT_FOUND);
234 230 }
235 231 if (PayRefundStatusEnum.isSuccess(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
  232 + log.info("[notifyRefundSuccess][退款订单({}) 已经是退款成功,无需更新]", refund.getId());
236 233 return;
237 234 }
238 235 if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) {
239   - throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING);
  236 + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING);
240 237 }
241   -
242 238 // 1.2 更新 PayRefundDO
243 239 PayRefundDO updateRefundObj = new PayRefundDO()
244 240 .setSuccessTime(notify.getSuccessTime())
... ... @@ -247,19 +243,48 @@ public class PayRefundServiceImpl implements PayRefundService {
247 243 .setChannelNotifyData(toJsonString(notify));
248 244 int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj);
249 245 if (updateCounts == 0) { // 校验状态,必须是等待状态
250   - throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING);
  246 + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING);
251 247 }
  248 + log.info("[notifyRefundSuccess][退款订单({}) 更新为退款成功]", refund.getId());
252 249  
253 250 // 2. 更新订单
254 251 orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice());
255 252  
256   - // 3. 插入退款通知记录
  253 + // 3. 插入退款通知记录 TODO 芋艿:退款成功
257 254 notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
258 255 .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build());
259 256 }
260 257  
261   - private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) {
262   - // TODO 芋艿:未实现
  258 + @Transactional(rollbackFor = Exception.class)
  259 + public void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) {
  260 + // 1.1 查询 PayRefundDO
  261 + PayRefundDO refund = refundMapper.selectByAppIdAndNo(
  262 + channel.getAppId(), notify.getOutRefundNo());
  263 + if (refund == null) {
  264 + throw exception(ErrorCodeConstants.REFUND_NOT_FOUND);
  265 + }
  266 + if (PayRefundStatusEnum.isFailure(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
  267 + log.info("[notifyRefundSuccess][退款订单({}) 已经是退款关闭,无需更新]", refund.getId());
  268 + return;
  269 + }
  270 + if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) {
  271 + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING);
  272 + }
  273 + // 1.2 更新 PayRefundDO
  274 + PayRefundDO updateRefundObj = new PayRefundDO()
  275 + .setChannelRefundNo(notify.getChannelRefundNo())
  276 + .setStatus(PayRefundStatusEnum.FAILURE.getStatus())
  277 + .setChannelNotifyData(toJsonString(notify))
  278 + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg());
  279 + int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj);
  280 + if (updateCounts == 0) { // 校验状态,必须是等待状态
  281 + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING);
  282 + }
  283 + log.info("[notifyRefundFailure][退款订单({}) 更新为退款失败]", refund.getId());
  284 +
  285 + // 2. 插入退款通知记录 TODO 芋艿:退款失败
  286 + notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
  287 + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build());
263 288 }
264 289  
265 290 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/util/MoneyUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.util;
  2 +
  3 +import java.math.BigDecimal;
  4 +import java.math.RoundingMode;
  5 +
  6 +/**
  7 + * 金额工具类
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public class MoneyUtils {
  12 +
  13 + /**
  14 + * 计算百分比金额,四舍五入
  15 + *
  16 + * @param price 金额
  17 + * @param rate 百分比,例如说 56.77% 则传入 56.77
  18 + * @return 百分比金额
  19 + */
  20 + public static Integer calculateRatePrice(Integer price, Double rate) {
  21 + return new BigDecimal(price)
  22 + .multiply(BigDecimal.valueOf(rate)) // 乘以
  23 + .setScale(0, RoundingMode.HALF_UP) // 四舍五入
  24 + .intValue();
  25 + }
  26 +
  27 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java
... ... @@ -90,7 +90,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
90 90 PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o ->
91 91 o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
92 92 // 调用, 并断言异常
93   - assertServiceException(() -> appService.updateApp(reqVO), PAY_APP_NOT_FOUND);
  93 + assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_FOUND);
94 94 }
95 95  
96 96 @Test
... ... @@ -130,7 +130,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
130 130 Long id = randomLongId();
131 131  
132 132 // 调用, 并断言异常
133   - assertServiceException(() -> appService.deleteApp(id), PAY_APP_NOT_FOUND);
  133 + assertServiceException(() -> appService.deleteApp(id), APP_NOT_FOUND);
134 134 }
135 135  
136 136 @Test
... ... @@ -144,7 +144,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
144 144 when(orderService.getOrderCountByAppId(eq(id))).thenReturn(10L);
145 145  
146 146 // 调用, 并断言异常
147   - assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_ORDER_CANT_DELETE);
  147 + assertServiceException(() -> appService.deleteApp(id), APP_EXIST_ORDER_CANT_DELETE);
148 148 }
149 149  
150 150 @Test
... ... @@ -158,7 +158,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
158 158 when(refundService.getRefundCountByAppId(eq(id))).thenReturn(10L);
159 159  
160 160 // 调用, 并断言异常
161   - assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_REFUND_CANT_DELETE);
  161 + assertServiceException(() -> appService.deleteApp(id), APP_EXIST_REFUND_CANT_DELETE);
162 162 }
163 163  
164 164 @Test
... ... @@ -239,7 +239,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
239 239  
240 240 @Test
241 241 public void testValidPayApp_notFound() {
242   - assertServiceException(() -> appService.validPayApp(randomLongId()), PAY_APP_NOT_FOUND);
  242 + assertServiceException(() -> appService.validPayApp(randomLongId()), APP_NOT_FOUND);
243 243 }
244 244  
245 245 @Test
... ... @@ -252,7 +252,7 @@ public class PayAppServiceTest extends BaseDbUnitTest {
252 252 Long id = dbApp.getId();
253 253  
254 254 // 调用,并断言异常
255   - assertServiceException(() -> appService.validPayApp(id), PAY_APP_IS_DISABLE);
  255 + assertServiceException(() -> appService.validPayApp(id), APP_IS_DISABLE);
256 256 }
257 257  
258 258 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java
... ... @@ -26,8 +26,7 @@ import java.util.List;
26 26 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
27 27 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
28 28 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
29   -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR;
30   -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS;
  29 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
31 30 import static org.junit.jupiter.api.Assertions.*;
32 31  
33 32 @Import({PayChannelServiceImpl.class})
... ... @@ -52,6 +51,40 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
52 51 }
53 52  
54 53 @Test
  54 + public void testInitLocalCache() {
  55 + // mock 数据
  56 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
  57 + o -> o.setConfig(randomWxPayClientConfig()));
  58 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  59 +
  60 + // 调用
  61 + channelService.initLocalCache();
  62 + // 校验缓存
  63 + assertEquals(1, channelService.getChannelCache().size());
  64 + assertEquals(dbChannel, channelService.getChannelCache().get(0));
  65 + }
  66 +
  67 + @Test
  68 + public void testRefreshLocalCache() {
  69 + // mock 数据 01
  70 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
  71 + o -> o.setConfig(randomWxPayClientConfig()));
  72 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  73 + channelService.initLocalCache();
  74 + // mock 数据 02
  75 + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class,
  76 + o -> o.setConfig(randomWxPayClientConfig()));
  77 + channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
  78 +
  79 + // 调用
  80 + channelService.refreshLocalCache();
  81 + // 校验缓存
  82 + assertEquals(2, channelService.getChannelCache().size());
  83 + assertEquals(dbChannel, channelService.getChannelCache().get(0));
  84 + assertEquals(dbChannel02, channelService.getChannelCache().get(1));
  85 + }
  86 +
  87 + @Test
55 88 public void testCreateChannel_success() {
56 89 // 准备参数
57 90 WxPayClientConfig config = randomWxPayClientConfig();
... ... @@ -125,7 +158,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
125 158 });
126 159  
127 160 // 调用, 并断言异常
128   - assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS);
  161 + assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_FOUND);
129 162 }
130 163  
131 164 @Test
... ... @@ -153,7 +186,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
153 186 Long id = randomLongId();
154 187  
155 188 // 调用, 并断言异常
156   - assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS);
  189 + assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_FOUND);
157 190 }
158 191  
159 192 @Test
... ... @@ -214,6 +247,101 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
214 247 assertPojoEquals(channel, dbChannel);
215 248 }
216 249  
  250 + @Test
  251 + public void testValidPayChannel_notExists() {
  252 + // 准备参数
  253 + Long id = randomLongId();
  254 +
  255 + // 调用, 并断言异常
  256 + assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_NOT_FOUND);
  257 + }
  258 +
  259 + @Test
  260 + public void testValidPayChannel_isDisable() {
  261 + // mock 数据
  262 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
  263 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  264 + o.setConfig(randomAlipayPayClientConfig());
  265 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  266 + });
  267 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  268 + // 准备参数
  269 + Long id = dbChannel.getId();
  270 +
  271 + // 调用, 并断言异常
  272 + assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_IS_DISABLE);
  273 + }
  274 +
  275 + @Test
  276 + public void testValidPayChannel_success() {
  277 + // mock 数据
  278 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
  279 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  280 + o.setConfig(randomAlipayPayClientConfig());
  281 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  282 + });
  283 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  284 + // 准备参数
  285 + Long id = dbChannel.getId();
  286 +
  287 + // 调用
  288 + PayChannelDO channel = channelService.validPayChannel(id);
  289 + // 断言异常
  290 + assertPojoEquals(channel, dbChannel);
  291 + }
  292 +
  293 + @Test
  294 + public void testValidPayChannel_appIdAndCode() {
  295 + // mock 数据
  296 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
  297 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  298 + o.setConfig(randomAlipayPayClientConfig());
  299 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  300 + });
  301 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  302 + // 准备参数
  303 + Long appId = dbChannel.getAppId();
  304 + String code = dbChannel.getCode();
  305 +
  306 + // 调用
  307 + PayChannelDO channel = channelService.validPayChannel(appId, code);
  308 + // 断言异常
  309 + assertPojoEquals(channel, dbChannel);
  310 + }
  311 +
  312 + @Test
  313 + public void testGetEnableChannelList() {
  314 + // 准备参数
  315 + Long appId = randomLongId();
  316 + // mock 数据 01(enable 不匹配)
  317 + PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> {
  318 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  319 + o.setConfig(randomAlipayPayClientConfig());
  320 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  321 + });
  322 + channelMapper.insert(dbChannel01);// @Sql: 先插入出一条存在的数据
  323 + // mock 数据 02(appId 不匹配)
  324 + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> {
  325 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  326 + o.setConfig(randomAlipayPayClientConfig());
  327 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  328 + });
  329 + channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
  330 + // mock 数据 03
  331 + PayChannelDO dbChannel03 = randomPojo(PayChannelDO.class, o -> {
  332 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  333 + o.setConfig(randomAlipayPayClientConfig());
  334 + o.setAppId(appId);
  335 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  336 + });
  337 + channelMapper.insert(dbChannel03);// @Sql: 先插入出一条存在的数据
  338 +
  339 + // 调用
  340 + List<PayChannelDO> channel = channelService.getEnableChannelList(appId);
  341 + // 断言异常
  342 + assertPojoEquals(channel, dbChannel03);
  343 + }
  344 +
217 345 public WxPayClientConfig randomWxPayClientConfig() {
218 346 return new WxPayClientConfig()
219 347 .setAppId(randomString())
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java
1 1 package cn.iocoder.yudao.module.pay.service.order;
2 2  
3   -import cn.hutool.core.date.DateUtil;
4   -import cn.hutool.core.util.RandomUtil;
  3 +import cn.hutool.extra.spring.SpringUtil;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6   -import cn.iocoder.yudao.framework.pay.config.PayProperties;
  5 +import cn.iocoder.yudao.framework.pay.core.client.PayClient;
7 6 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
8 8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
9   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  9 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
  10 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
  11 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
  12 +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
10 13 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
11 14 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
  15 +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitReqVO;
  16 +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitRespVO;
  17 +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
  18 +import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
12 19 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  20 +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
  21 +import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper;
13 22 import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper;
14   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
  23 +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
  24 +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
15 25 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
16   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
  26 +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
17 27 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
18 28 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
19 29 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
  30 +import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
  31 +import org.junit.jupiter.api.BeforeEach;
20 32 import org.junit.jupiter.api.Test;
  33 +import org.mockito.ArgumentMatcher;
  34 +import org.mockito.MockedStatic;
21 35 import org.springframework.boot.test.mock.mockito.MockBean;
22 36 import org.springframework.context.annotation.Import;
23 37  
24 38 import javax.annotation.Resource;
25   -import java.time.LocalDateTime;
  39 +import java.time.Duration;
26 40 import java.util.List;
27 41  
  42 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  43 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
28 44 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
29 45 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  46 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
30 47 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  48 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
  49 +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
  50 +import static org.assertj.core.api.Assertions.assertThat;
31 51 import static org.junit.jupiter.api.Assertions.assertEquals;
  52 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  53 +import static org.mockito.ArgumentMatchers.*;
  54 +import static org.mockito.Mockito.*;
32 55  
33 56 /**
34 57 * {@link PayOrderServiceImpl} 的单元测试类
35 58 *
36 59 * @author 芋艿
37 60 */
38   -@Import({PayOrderServiceImpl.class})
39   -public class PayOrderServiceTest extends BaseDbUnitTest {
  61 +@Import({PayOrderServiceImpl.class, PayNoRedisDAO.class})
  62 +public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
40 63  
41 64 @Resource
42 65 private PayOrderServiceImpl orderService;
43 66  
44 67 @Resource
45 68 private PayOrderMapper orderMapper;
  69 + @Resource
  70 + private PayOrderExtensionMapper orderExtensionMapper;
46 71  
47 72 @MockBean
48 73 private PayClientFactory payClientFactory;
... ... @@ -55,140 +80,92 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
55 80 @MockBean
56 81 private PayNotifyService notifyService;
57 82  
58   - public String generateNo() {
59   - return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + RandomUtil.randomInt(100000, 999999);
  83 + @BeforeEach
  84 + public void setUp() {
  85 + when(properties.getOrderNotifyUrl()).thenReturn("http://127.0.0.1");
60 86 }
61 87  
62 88 @Test
63 89 public void testGetOrderPage() {
64   -
65   - String merchantOrderId = generateNo();
66   - String channelOrderId = generateNo();
67   -
68 90 // mock 数据
69 91 PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到
70 92 o.setAppId(1L);
71   - o.setChannelId(1L);
72 93 o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
73   - o.setMerchantOrderId(merchantOrderId);
74   - o.setSubject("灿灿子的炸弹猫");
75   - o.setBody("斌斌子送给灿灿子的炸弹猫");
76   - o.setNotifyUrl("https://hc.com/lbh");
77   - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
78   - o.setPrice(10000);
79   - o.setChannelFeeRate(0.01);
80   - o.setChannelFeePrice(1L);
  94 + o.setMerchantOrderId("110");
  95 + o.setChannelOrderNo("220");
  96 + o.setNo("330");
81 97 o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
82   - o.setUserIp("127.0.0.1");
83   - o.setCreateTime(LocalDateTime.of(2018, 1, 1, 10, 1, 0));
84   - o.setExpireTime(LocalDateTime.of(2018, 1, 1, 10, 30, 0));
85   - o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2));
86   - o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15));
87   - o.setSuccessExtensionId(1L);
88   - o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
89   - o.setRefundTimes(0);
90   - o.setRefundPrice(0);
91   - o.setChannelUserId("1008611");
92   - o.setChannelOrderNo(channelOrderId);
93   - o.setUpdateTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15));
  98 + o.setCreateTime(buildTime(2018, 1, 15));
94 99 });
95 100 orderMapper.insert(dbOrder);
96 101 // 测试 appId 不匹配
97 102 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setAppId(2L)));
98   - // 测试 channelId 不匹配
99   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelId(2L)));
100 103 // 测试 channelCode 不匹配
101 104 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
102 105 // 测试 merchantOrderId 不匹配
103   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(generateNo())));
104   - // 测试 notifyStatus 不匹配
105   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
  106 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(randomString())));
  107 + // 测试 channelOrderNo 不匹配
  108 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelOrderNo(randomString())));
  109 + // 测试 no 不匹配
  110 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNo(randomString())));
106 111 // 测试 status 不匹配
107 112 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())));
108   - // 测试 refundStatus 不匹配
109   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus())));
110 113 // 测试 createTime 不匹配
111   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10,
112   - 1))));
  114 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(buildTime(2019, 1, 1))));
113 115 // 准备参数
114 116 PayOrderPageReqVO reqVO = new PayOrderPageReqVO();
115 117 reqVO.setAppId(1L);
116   - reqVO.setChannelId(1L);
117 118 reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
118   - reqVO.setMerchantOrderId(merchantOrderId);
119   - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
  119 + reqVO.setMerchantOrderId("11");
  120 + reqVO.setChannelOrderNo("22");
  121 + reqVO.setNo("33");
120 122 reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
121   - reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
122   - reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)}));
  123 + reqVO.setCreateTime(buildBetweenTime(2018, 1, 10, 2018, 1, 30));
  124 +
123 125 // 调用
124 126 PageResult<PayOrderDO> pageResult = orderService.getOrderPage(reqVO);
125 127 // 断言
126 128 assertEquals(1, pageResult.getTotal());
127 129 assertEquals(1, pageResult.getList().size());
128 130 assertPojoEquals(dbOrder, pageResult.getList().get(0));
129   - // assertEquals(0, dbOrder.getUpdateTime().compareTo(pageResult.getList().get(0).getUpdateTime()));
130 131 }
131 132  
132 133 @Test
133 134 public void testGetOrderList() {
134 135 // mock 数据
135   - String merchantOrderId = generateNo();
136   - String channelOrderId = generateNo();
137 136 PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到
138 137 o.setAppId(1L);
139   - o.setChannelId(1L);
140 138 o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
141   - o.setMerchantOrderId(merchantOrderId);
142   - o.setSubject("灿灿子的炸弹猫");
143   - o.setBody("斌斌子送给灿灿子的炸弹猫");
144   - o.setNotifyUrl("https://hc.com/lbh");
145   - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
146   - o.setPrice(10000);
147   - o.setChannelFeeRate(0.01);
148   - o.setChannelFeePrice(1L);
  139 + o.setMerchantOrderId("110");
  140 + o.setChannelOrderNo("220");
  141 + o.setNo("330");
149 142 o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
150   - o.setUserIp("127.0.0.1");
151   - o.setCreateTime(LocalDateTime.of(2018, 1, 1, 10, 1, 0));
152   - o.setExpireTime(LocalDateTime.of(2018, 1, 1, 10, 30, 0));
153   - o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2));
154   - o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15));
155   - o.setSuccessExtensionId(1L);
156   - o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
157   - o.setRefundTimes(0);
158   - o.setRefundPrice(0);
159   - o.setChannelUserId("1008611");
160   - o.setChannelOrderNo(channelOrderId);
161   - o.setUpdateTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15));
162   -
  143 + o.setCreateTime(buildTime(2018, 1, 15));
163 144 });
164 145 orderMapper.insert(dbOrder);
165 146 // 测试 appId 不匹配
166 147 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setAppId(2L)));
167   - // 测试 channelId 不匹配
168   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelId(2L)));
169 148 // 测试 channelCode 不匹配
170 149 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
171 150 // 测试 merchantOrderId 不匹配
172   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(generateNo())));
173   - // 测试 notifyStatus 不匹配
174   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
  151 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(randomString())));
  152 + // 测试 channelOrderNo 不匹配
  153 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelOrderNo(randomString())));
  154 + // 测试 no 不匹配
  155 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNo(randomString())));
175 156 // 测试 status 不匹配
176 157 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())));
177   - // 测试 refundStatus 不匹配
178   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus())));
179 158 // 测试 createTime 不匹配
180   - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10,
181   - 1))));
  159 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(buildTime(2019, 1, 1))));
182 160 // 准备参数
183 161 PayOrderExportReqVO reqVO = new PayOrderExportReqVO();
184 162 reqVO.setAppId(1L);
185   - reqVO.setChannelId(1L);
186 163 reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
187   - reqVO.setMerchantOrderId(merchantOrderId);
188   - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
  164 + reqVO.setMerchantOrderId("11");
  165 + reqVO.setChannelOrderNo("22");
  166 + reqVO.setNo("33");
189 167 reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
190   - reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
191   - reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)}));
  168 + reqVO.setCreateTime(buildBetweenTime(2018, 1, 10, 2018, 1, 30));
192 169  
193 170 // 调用
194 171 List<PayOrderDO> list = orderService.getOrderList(reqVO);
... ... @@ -197,4 +174,393 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
197 174 assertPojoEquals(dbOrder, list.get(0));
198 175 }
199 176  
  177 + @Test
  178 + public void testCreateOrder_success() {
  179 + // mock 参数
  180 + PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class,
  181 + o -> o.setAppId(1L).setMerchantOrderId("10")
  182 + .setSubject(randomString()).setBody(randomString()));
  183 + // mock 方法
  184 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L).setOrderNotifyUrl("http://127.0.0.1"));
  185 + when(appService.validPayApp(eq(reqDTO.getAppId()))).thenReturn(app);
  186 +
  187 + // 调用
  188 + Long orderId = orderService.createOrder(reqDTO);
  189 + // 断言
  190 + PayOrderDO order = orderMapper.selectById(orderId);
  191 + assertPojoEquals(order, reqDTO);
  192 + assertEquals(order.getAppId(), 1L);
  193 + assertEquals(order.getNotifyUrl(), "http://127.0.0.1");
  194 + assertEquals(order.getStatus(), PayOrderStatusEnum.WAITING.getStatus());
  195 + assertEquals(order.getRefundPrice(), 0);
  196 + }
  197 +
  198 + @Test
  199 + public void testCreateOrder_exists() {
  200 + // mock 参数
  201 + PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class,
  202 + o -> o.setAppId(1L).setMerchantOrderId("10"));
  203 + // mock 数据
  204 + PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> o.setAppId(1L).setMerchantOrderId("10"));
  205 + orderMapper.insert(dbOrder);
  206 +
  207 + // 调用
  208 + Long orderId = orderService.createOrder(reqDTO);
  209 + // 断言
  210 + PayOrderDO order = orderMapper.selectById(orderId);
  211 + assertPojoEquals(dbOrder, order);
  212 + }
  213 +
  214 + @Test
  215 + public void testSubmitOrder_notFound() {
  216 + // 准备参数
  217 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class);
  218 + String userIp = randomString();
  219 +
  220 + // 调用, 并断言异常
  221 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), ORDER_NOT_FOUND);
  222 + }
  223 +
  224 + @Test
  225 + public void testSubmitOrder_notWaiting() {
  226 + // mock 数据(order)
  227 + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()));
  228 + orderMapper.insert(order);
  229 + // 准备参数
  230 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()));
  231 + String userIp = randomString();
  232 +
  233 + // 调用, 并断言异常
  234 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), ORDER_STATUS_IS_NOT_WAITING);
  235 + }
  236 +
  237 + @Test
  238 + public void testSubmitOrder_expired() {
  239 + // mock 数据(order)
  240 + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())
  241 + .setExpireTime(addTime(Duration.ofDays(-1))));
  242 + orderMapper.insert(order);
  243 + // 准备参数
  244 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()));
  245 + String userIp = randomString();
  246 +
  247 + // 调用, 并断言异常
  248 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), ORDER_IS_EXPIRED);
  249 + }
  250 +
  251 + @Test
  252 + public void testSubmitOrder_channelNotFound() {
  253 + // mock 数据(order)
  254 + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())
  255 + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1))));
  256 + orderMapper.insert(order);
  257 + // 准备参数
  258 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())
  259 + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
  260 + String userIp = randomString();
  261 + // mock 方法(app)
  262 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
  263 + when(appService.validPayApp(eq(1L))).thenReturn(app);
  264 + // mock 方法(channel)
  265 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setCode(PayChannelEnum.ALIPAY_APP.getCode()));
  266 + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode())))
  267 + .thenReturn(channel);
  268 +
  269 + // 调用, 并断言异常
  270 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), CHANNEL_NOT_FOUND);
  271 + }
  272 +
  273 + @Test // 调用 unifiedOrder 接口,返回存在渠道错误
  274 + public void testSubmitOrder_channelError() {
  275 + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class);
  276 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  277 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class)))
  278 + .thenReturn(payOrderServiceImpl);
  279 +
  280 + // mock 数据(order)
  281 + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())
  282 + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1))));
  283 + orderMapper.insert(order);
  284 + // 准备参数
  285 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())
  286 + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
  287 + String userIp = randomString();
  288 + // mock 方法(app)
  289 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
  290 + when(appService.validPayApp(eq(1L))).thenReturn(app);
  291 + // mock 方法(channel)
  292 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
  293 + .setCode(PayChannelEnum.ALIPAY_APP.getCode()));
  294 + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode())))
  295 + .thenReturn(channel);
  296 + // mock 方法(client)
  297 + PayClient client = mock(PayClient.class);
  298 + when(payClientFactory.getPayClient(eq(10L))).thenReturn(client);
  299 + // mock 方法()
  300 + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o ->
  301 + o.setChannelErrorCode("001").setChannelErrorMsg("模拟异常"));
  302 + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> {
  303 + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo());
  304 + assertThat(payOrderUnifiedReqDTO)
  305 + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime")
  306 + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10",
  307 + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime());
  308 + return true;
  309 + }))).thenReturn(unifiedOrderResp);
  310 +
  311 + // 调用,并断言异常
  312 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp),
  313 + ORDER_SUBMIT_CHANNEL_ERROR, "001", "模拟异常");
  314 + // 断言,数据记录(PayOrderExtensionDO)
  315 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null);
  316 + assertNotNull(orderExtension);
  317 + assertThat(orderExtension).extracting("no", "orderId").isNotNull();
  318 + assertThat(orderExtension)
  319 + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras",
  320 + "channelErrorCode", "channelErrorMsg", "channelNotifyData")
  321 + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp,
  322 + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(),
  323 + null, null, null);
  324 + }
  325 + }
  326 +
  327 + @Test
  328 + public void testSubmitOrder_success() {
  329 + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class);
  330 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  331 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class)))
  332 + .thenReturn(payOrderServiceImpl);
  333 +
  334 + // mock 数据(order)
  335 + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())
  336 + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1))));
  337 + orderMapper.insert(order);
  338 + // 准备参数
  339 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())
  340 + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
  341 + String userIp = randomString();
  342 + // mock 方法(app)
  343 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
  344 + when(appService.validPayApp(eq(1L))).thenReturn(app);
  345 + // mock 方法(channel)
  346 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
  347 + .setCode(PayChannelEnum.ALIPAY_APP.getCode()));
  348 + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode())))
  349 + .thenReturn(channel);
  350 + // mock 方法(client)
  351 + PayClient client = mock(PayClient.class);
  352 + when(payClientFactory.getPayClient(eq(10L))).thenReturn(client);
  353 + // mock 方法()
  354 + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> o.setChannelErrorCode(null).setChannelErrorMsg(null)
  355 + .setDisplayMode(PayOrderDisplayModeEnum.URL.getMode()).setDisplayContent("tudou"));
  356 + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> {
  357 + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo());
  358 + assertThat(payOrderUnifiedReqDTO)
  359 + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime")
  360 + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10",
  361 + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime());
  362 + return true;
  363 + }))).thenReturn(unifiedOrderResp);
  364 +
  365 + // 调用
  366 + PayOrderSubmitRespVO result = orderService.submitOrder(reqVO, userIp);
  367 + // 断言,数据记录(PayOrderExtensionDO)
  368 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null);
  369 + assertNotNull(orderExtension);
  370 + assertThat(orderExtension).extracting("no", "orderId").isNotNull();
  371 + assertThat(orderExtension)
  372 + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras",
  373 + "channelErrorCode", "channelErrorMsg", "channelNotifyData")
  374 + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp,
  375 + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(),
  376 + null, null, null);
  377 + // 断言,返回(PayOrderSubmitRespVO)
  378 + assertThat(result)
  379 + .extracting("status", "displayMode", "displayContent")
  380 + .containsExactly(PayOrderStatusEnum.WAITING.getStatus(), PayOrderDisplayModeEnum.URL.getMode(), "tudou");
  381 + // 断言,调用
  382 + verify(payOrderServiceImpl).notifyOrder(same(channel), same(unifiedOrderResp));
  383 + }
  384 + }
  385 +
  386 + @Test
  387 + public void testNotifyOrder_channelId() {
  388 + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class);
  389 + try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
  390 + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class)))
  391 + .thenReturn(payOrderServiceImpl);
  392 + // 准备参数
  393 + Long channelId = 10L;
  394 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class);
  395 + // mock 方法(channel)
  396 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
  397 + when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
  398 +
  399 + // 调用
  400 + orderService.notifyOrder(channelId, notify);
  401 + // 断言
  402 + verify(payOrderServiceImpl).notifyOrder(same(channel), same(notify));
  403 + }
  404 + }
  405 +
  406 + @Test
  407 + public void testNotifyOrderSuccess_orderExtension_notFound() {
  408 + // 准备参数
  409 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
  410 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class,
  411 + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()));
  412 +
  413 + // 调用,并断言异常
  414 + assertServiceException(() -> orderService.notifyOrder(channel, notify),
  415 + ORDER_EXTENSION_NOT_FOUND);
  416 + }
  417 +
  418 + @Test
  419 + public void testNotifyOrderSuccess_orderExtension_closed() {
  420 + // mock 数据(PayOrderExtensionDO)
  421 + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class,
  422 + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())
  423 + .setNo("P110"));
  424 + orderExtensionMapper.insert(orderExtension);
  425 + // 准备参数
  426 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
  427 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class,
  428 + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
  429 + .setOutTradeNo("P110"));
  430 +
  431 + // 调用,并断言异常
  432 + assertServiceException(() -> orderService.notifyOrder(channel, notify),
  433 + ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  434 + }
  435 +
  436 + @Test
  437 + public void testNotifyOrderSuccess_order_notFound() {
  438 + // mock 数据(PayOrderExtensionDO)
  439 + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class,
  440 + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())
  441 + .setNo("P110"));
  442 + orderExtensionMapper.insert(orderExtension);
  443 + // 准备参数
  444 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
  445 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class,
  446 + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
  447 + .setOutTradeNo("P110"));
  448 +
  449 + // 调用,并断言异常
  450 + assertServiceException(() -> orderService.notifyOrder(channel, notify),
  451 + ORDER_NOT_FOUND);
  452 + // 断言 PayOrderExtensionDO :数据更新被回滚
  453 + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null));
  454 + }
  455 +
  456 + @Test
  457 + public void testNotifyOrderSuccess_order_closed() {
  458 + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.CLOSED.getStatus());
  459 + }
  460 +
  461 + @Test
  462 + public void testNotifyOrderSuccess_order_refund() {
  463 + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.REFUND.getStatus());
  464 + }
  465 +
  466 + private void testNotifyOrderSuccess_order_closedOrRefund(Integer status) {
  467 + // mock 数据(PayOrderDO)
  468 + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status));
  469 + orderMapper.insert(order);
  470 + // mock 数据(PayOrderExtensionDO)
  471 + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class,
  472 + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())
  473 + .setNo("P110")
  474 + .setOrderId(order.getId()));
  475 + orderExtensionMapper.insert(orderExtension);
  476 + // 准备参数
  477 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
  478 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class,
  479 + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
  480 + .setOutTradeNo("P110"));
  481 +
  482 + // 调用,并断言异常
  483 + assertServiceException(() -> orderService.notifyOrder(channel, notify),
  484 + ORDER_STATUS_IS_NOT_WAITING);
  485 + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS
  486 + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null));
  487 + }
  488 +
  489 + @Test
  490 + public void testNotifyOrderSuccess_order_paid() {
  491 + // mock 数据(PayOrderDO)
  492 + PayOrderDO order = randomPojo(PayOrderDO.class,
  493 + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()));
  494 + orderMapper.insert(order);
  495 + // mock 数据(PayOrderExtensionDO)
  496 + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class,
  497 + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())
  498 + .setNo("P110")
  499 + .setOrderId(order.getId()));
  500 + orderExtensionMapper.insert(orderExtension);
  501 + // 重要:需要将 order 的 extensionId 更新下
  502 + order.setExtensionId(orderExtension.getId());
  503 + orderMapper.updateById(order);
  504 + // 准备参数
  505 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
  506 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class,
  507 + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
  508 + .setOutTradeNo("P110"));
  509 +
  510 + // 调用,并断言异常
  511 + orderService.notifyOrder(channel, notify);
  512 + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS
  513 + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null));
  514 + // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS
  515 + assertPojoEquals(order, orderMapper.selectOne(null));
  516 + // 断言,调用
  517 + verify(notifyService, never()).createPayNotifyTask(any(PayNotifyTaskCreateReqDTO.class));
  518 + }
  519 +
  520 + @Test
  521 + public void testNotifyOrderSuccess_order_waiting() {
  522 + // mock 数据(PayOrderDO)
  523 + PayOrderDO order = randomPojo(PayOrderDO.class,
  524 + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())
  525 + .setPrice(10));
  526 + orderMapper.insert(order);
  527 + // mock 数据(PayOrderExtensionDO)
  528 + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class,
  529 + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())
  530 + .setNo("P110")
  531 + .setOrderId(order.getId()));
  532 + orderExtensionMapper.insert(orderExtension);
  533 + // 准备参数
  534 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
  535 + .setFeeRate(0.1D));
  536 + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class,
  537 + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
  538 + .setOutTradeNo("P110"));
  539 +
  540 + // 调用,并断言异常
  541 + orderService.notifyOrder(channel, notify);
  542 + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS
  543 + orderExtension.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())
  544 + .setChannelNotifyData(toJsonString(notify));
  545 + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null),
  546 + "updateTime", "updater");
  547 + // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS
  548 + order.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())
  549 + .setChannelId(10L).setChannelCode(channel.getCode())
  550 + .setSuccessTime(notify.getSuccessTime()).setExtensionId(orderExtension.getId()).setNo(orderExtension.getNo())
  551 + .setChannelOrderNo(notify.getChannelOrderNo()).setChannelUserId(notify.getChannelUserId())
  552 + .setChannelFeeRate(0.1D).setChannelFeePrice(1);
  553 + assertPojoEquals(order, orderMapper.selectOne(null),
  554 + "updateTime", "updater");
  555 + // 断言,调用
  556 + verify(notifyService).createPayNotifyTask(argThat(new ArgumentMatcher<PayNotifyTaskCreateReqDTO>() {
  557 + @Override
  558 + public boolean matches(PayNotifyTaskCreateReqDTO reqDTO) {
  559 + assertEquals(reqDTO.getType(), PayNotifyTypeEnum.ORDER.getType());
  560 + assertEquals(reqDTO.getDataId(), orderExtension.getOrderId());
  561 + return true;
  562 + }
  563 + }));
  564 + }
  565 +
200 566 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
1 1 package cn.iocoder.yudao.module.pay.service.refund;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.pay.config.PayProperties;
5 4 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
6 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
  6 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
7 7 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
8 8 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
9 9 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
10 10 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
11 11 import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
12   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
  12 +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
13 13 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
14   -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
  14 +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
15 15 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
16 16 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
17 17 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
18   -import cn.iocoder.yudao.module.pay.service.order.PayOrderExtensionService;
19 18 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
20 19 import org.junit.jupiter.api.Test;
21 20 import org.springframework.boot.test.mock.mockito.MockBean;
... ... @@ -31,7 +30,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
31 30 import static org.junit.jupiter.api.Assertions.assertEquals;
32 31  
33 32 @Import(PayRefundServiceImpl.class)
34   -public class PayRefundServiceTest extends BaseDbUnitTest {
  33 +public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
35 34  
36 35 @Resource
37 36 private PayRefundServiceImpl refundService;
... ... @@ -46,8 +45,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
46 45 @MockBean
47 46 private PayOrderService orderService;
48 47 @MockBean
49   - private PayOrderExtensionService orderExtensionService;
50   - @MockBean
51 48 private PayAppService appService;
52 49 @MockBean
53 50 private PayChannelService channelService;
... ... @@ -66,8 +63,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
66 63 o.setMerchantOrderId("MOT0000001");
67 64 o.setMerchantRefundId("MRF0000001");
68 65 o.setNotifyUrl("https://www.cancanzi.com");
69   - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
70   - o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
  66 + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
71 67 o.setPayPrice(100);
72 68 o.setRefundPrice(500);
73 69 o.setReason("就是想退款了,你有意见吗");
... ... @@ -87,11 +83,8 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
87 83 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
88 84 // 测试 merchantRefundNo 不匹配
89 85 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112")));
90   - // 测试 notifyStatus 不匹配
91   - refundMapper.insert(
92   - cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
93 86 // 测试 status 不匹配
94   - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus())));
  87 + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())));
95 88 // 测试 createTime 不匹配
96 89 refundMapper.insert(cloneIgnoreId(dbRefund, o ->
97 90 o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10))));
... ... @@ -99,8 +92,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
99 92 PayRefundPageReqVO reqVO = new PayRefundPageReqVO();
100 93 reqVO.setAppId(1L);
101 94 reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
102   - reqVO.setMerchantRefundNo("MRF0000001");
103   - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
  95 + reqVO.setMerchantRefundId("MRF0000001");
104 96 reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
105 97 reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)}));
106 98  
... ... @@ -124,8 +116,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
124 116 o.setMerchantOrderId("MOT0000001");
125 117 o.setMerchantRefundId("MRF0000001");
126 118 o.setNotifyUrl("https://www.cancanzi.com");
127   - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
128   - o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
  119 + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
129 120 o.setPayPrice(100);
130 121 o.setRefundPrice(500);
131 122 o.setReason("就是想退款了,你有意见吗");
... ... @@ -145,11 +136,8 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
145 136 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
146 137 // 测试 merchantRefundNo 不匹配
147 138 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112")));
148   - // 测试 notifyStatus 不匹配
149   - refundMapper.insert(
150   - cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
151 139 // 测试 status 不匹配
152   - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus())));
  140 + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())));
153 141 // 测试 createTime 不匹配
154 142 refundMapper.insert(cloneIgnoreId(dbRefund, o ->
155 143 o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10))));
... ... @@ -158,10 +146,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
158 146 PayRefundExportReqVO reqVO = new PayRefundExportReqVO();
159 147 reqVO.setAppId(1L);
160 148 reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
161   - reqVO.setMerchantRefundNo("MRF0000001");
162   - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
163 149 reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
164   - reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus());
165 150 reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)}));
166 151  
167 152 // 调用
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql
1 1 DELETE FROM pay_app;
2 2 DELETE FROM pay_channel;
3 3 DELETE FROM pay_order;
  4 +DELETE FROM pay_order_extension;
4 5 DELETE FROM pay_refund;
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
... ... @@ -39,19 +39,17 @@ CREATE TABLE IF NOT EXISTS `pay_order` (
39 39 `subject` varchar(32) NOT NULL,
40 40 `body` varchar(128) NOT NULL,
41 41 `notify_url` varchar(1024) NOT NULL,
42   - `notify_status` tinyint(4) NOT NULL,
43   - `amount` bigint(20) NOT NULL,
  42 + `price` bigint(20) NOT NULL,
44 43 `channel_fee_rate` double DEFAULT 0,
45   - `channel_fee_amount` bigint(20) DEFAULT 0,
  44 + `channel_fee_price` bigint(20) DEFAULT 0,
46 45 `status` tinyint(4) NOT NULL,
47 46 `user_ip` varchar(50) NOT NULL,
48   - `expire_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  47 + `expire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
49 48 `success_time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
50 49 `notify_time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
51   - `success_extension_id` bigint(20) DEFAULT NULL COMMENT '支付成功的订单拓展单编号',
52   - `refund_status` tinyint(4) NOT NULL,
53   - `refund_times` tinyint(4) NOT NULL,
54   - `refund_amount` bigint(20) NOT NULL,
  50 + `extension_id` bigint(20) DEFAULT NULL,
  51 + `no` varchar(64) NULL,
  52 + `refund_price` bigint(20) NOT NULL,
55 53 `channel_user_id` varchar(255) DEFAULT NULL,
56 54 `channel_order_no` varchar(64) DEFAULT NULL,
57 55 `creator` varchar(64) DEFAULT '',
... ... @@ -62,6 +60,26 @@ CREATE TABLE IF NOT EXISTS `pay_order` (
62 60 PRIMARY KEY ("id")
63 61 ) COMMENT = '支付订单';
64 62  
  63 +CREATE TABLE IF NOT EXISTS `pay_order_extension` (
  64 + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  65 + `no` varchar(64) NOT NULL,
  66 + `order_id` bigint(20) NOT NULL,
  67 + `channel_id` bigint(20) NOT NULL,
  68 + `channel_code` varchar(32) NOT NULL,
  69 + `user_ip` varchar(50) NULL DEFAULT NULL,
  70 + `status` tinyint(4) NOT NULL,
  71 + `channel_extras` varchar(1024) NULL DEFAULT NULL,
  72 + `channel_error_code` varchar(64) NULL,
  73 + `channel_error_msg` varchar(64) NULL,
  74 + `channel_notify_data` varchar(1024) NULL,
  75 + `creator` varchar(64) NULL DEFAULT '',
  76 + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  77 + `updater` varchar(64) NULL DEFAULT '',
  78 + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  79 + `deleted` bit(1) NOT NULL DEFAULT FALSE,
  80 + PRIMARY KEY ("id")
  81 +) COMMENT = '支付订单拓展';
  82 +
65 83 CREATE TABLE IF NOT EXISTS `pay_refund` (
66 84 "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
67 85 `app_id` bigint(20) NOT NULL,
... ...
yudao-ui-admin/src/api/pay/app.js
1 1 import request from '@/utils/request'
2 2  
3   -// 创建支付应用信息
  3 +// 创建支付应用
4 4 export function createApp(data) {
5 5 return request({
6 6 url: '/pay/app/create',
... ... @@ -9,7 +9,7 @@ export function createApp(data) {
9 9 })
10 10 }
11 11  
12   -// 更新支付应用信息
  12 +// 更新支付应用
13 13 export function updateApp(data) {
14 14 return request({
15 15 url: '/pay/app/update',
... ... @@ -18,7 +18,7 @@ export function updateApp(data) {
18 18 })
19 19 }
20 20  
21   -// 支付应用信息状态修改
  21 +// 支付应用状态修改
22 22 export function changeAppStatus(id, status) {
23 23 const data = {
24 24 id,
... ... @@ -31,7 +31,7 @@ export function changeAppStatus(id, status) {
31 31 })
32 32 }
33 33  
34   -// 删除支付应用信息
  34 +// 删除支付应用
35 35 export function deleteApp(id) {
36 36 return request({
37 37 url: '/pay/app/delete?id=' + id,
... ... @@ -39,7 +39,7 @@ export function deleteApp(id) {
39 39 })
40 40 }
41 41  
42   -// 获得支付应用信息
  42 +// 获得支付应用
43 43 export function getApp(id) {
44 44 return request({
45 45 url: '/pay/app/get?id=' + id,
... ... @@ -47,7 +47,7 @@ export function getApp(id) {
47 47 })
48 48 }
49 49  
50   -// 获得支付应用信息分页
  50 +// 获得支付应用分页
51 51 export function getAppPage(query) {
52 52 return request({
53 53 url: '/pay/app/page',
... ... @@ -56,23 +56,10 @@ export function getAppPage(query) {
56 56 })
57 57 }
58 58  
59   -// 导出支付应用信息 Excel
60   -export function exportAppExcel(query) {
  59 +// 获得支付应用列表
  60 +export function getAppList() {
61 61 return request({
62   - url: '/pay/app/export-excel',
63   - method: 'get',
64   - params: query,
65   - responseType: 'blob'
66   - })
67   -}
68   -
69   -// 根据商ID称搜索应用列表
70   -export function getAppListByMerchantId(merchantId) {
71   - return request({
72   - url: '/pay/app/list-merchant-id',
73   - params:{
74   - merchantId:merchantId
75   - },
  62 + url: '/pay/app/list',
76 63 method: 'get'
77 64 })
78 65 }
... ...
yudao-ui-admin/src/api/pay/notify.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得支付通知明细
  4 +export function getNotifyTaskDetail(id) {
  5 + return request({
  6 + url: '/pay/notify/get-detail?id=' + id,
  7 + method: 'get'
  8 + })
  9 +}
  10 +
  11 +// 获得支付通知分页
  12 +export function getNotifyTaskPage(query) {
  13 + return request({
  14 + url: '/pay/notify/page',
  15 + method: 'get',
  16 + params: query
  17 + })
  18 +}
... ...
yudao-ui-admin/src/utils/constants.js
... ... @@ -207,42 +207,6 @@ export const PayOrderStatusEnum = {
207 207 }
208 208  
209 209 /**
210   - * 支付订单回调状态枚举
211   - */
212   -export const PayOrderNotifyStatusEnum = {
213   - NO: {
214   - status: 0,
215   - name: '未通知'
216   - },
217   - SUCCESS: {
218   - status: 10,
219   - name: '通知成功'
220   - },
221   - FAILURE: {
222   - status: 20,
223   - name: '通知失败'
224   - }
225   -}
226   -
227   -/**
228   - * 支付订单退款状态枚举
229   - */
230   -export const PayOrderRefundStatusEnum = {
231   - NO: {
232   - status: 0,
233   - name: '未退款'
234   - },
235   - SOME: {
236   - status: 10,
237   - name: '部分退款'
238   - },
239   - ALL: {
240   - status: 20,
241   - name: '全部退款'
242   - }
243   -}
244   -
245   -/**
246 210 * 支付退款订单状态枚举
247 211 */
248 212 export const PayRefundStatusEnum = {
... ...
yudao-ui-admin/src/utils/dict.js
... ... @@ -51,15 +51,12 @@ export const DICT_TYPE = {
51 51  
52 52 // ========== PAY 模块 ==========
53 53 PAY_CHANNEL_WECHAT_VERSION: 'pay_channel_wechat_version', // 微信渠道版本
54   - PAY_CHANNEL_ALIPAY_SIGN_TYPE: 'pay_channel_alipay_sign_type', // 支付渠道支付宝算法类型
55   - PAY_CHANNEL_ALIPAY_MODE: 'pay_channel_alipay_mode', // 支付宝公钥类型
56   - PAY_CHANNEL_ALIPAY_SERVER_TYPE: 'pay_channel_alipay_server_type', // 支付宝网关地址
  54 +
57 55 PAY_CHANNEL_CODE: 'pay_channel_code', // 支付渠道编码类型
58   - PAY_ORDER_NOTIFY_STATUS: 'pay_order_notify_status', // 商户支付订单回调状态
59 56 PAY_ORDER_STATUS: 'pay_order_status', // 商户支付订单状态
60   - PAY_ORDER_REFUND_STATUS: 'pay_order_refund_status', // 商户支付订单退款状态
61   - PAY_REFUND_ORDER_STATUS: 'pay_refund_order_status', // 退款订单状态
62   - PAY_REFUND_ORDER_TYPE: 'pay_refund_order_type', // 退款订单类别
  57 + PAY_REFUND_STATUS: 'pay_refund_status', // 退款订单状态
  58 + PAY_NOTIFY_STATUS: 'pay_notify_status', // 商户支付回调状态
  59 + PAY_NOTIFY_TYPE: 'pay_notify_type', // 商户支付回调状态
63 60  
64 61 // ========== MP 模块 ==========
65 62 MP_AUTO_REPLY_REQUEST_MATCH: 'mp_auto_reply_request_match', // 自动回复请求匹配类型
... ...
yudao-ui-admin/src/views/pay/app/components/aliPayChannelForm.vue deleted
1   -<template>
2   - <div>
3   - <el-dialog :visible.sync="transferParam.aliPayOpen" :title="title" @closed="close" append-to-body width="800px">
4   - <el-form ref="aliPayForm" :model="form" :rules="rules" size="medium" label-width="100px"
5   - v-loading="transferParam.loading">
6   - <el-form-item label-width="180px" label="渠道费率" prop="feeRate">
7   - <el-input v-model="form.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}">
8   - <template slot="append">%</template>
9   - </el-input>
10   - </el-form-item>
11   - <el-form-item label-width="180px" label="开放平台APPID" prop="aliPayConfig.appId">
12   - <el-input v-model="form.aliPayConfig.appId" placeholder="请输入开放平台APPID" clearable :style="{width: '100%'}">
13   - </el-input>
14   - </el-form-item>
15   - <el-form-item label-width="180px" label="渠道状态" prop="status">
16   - <el-radio-group v-model="form.status" size="medium">
17   - <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
18   - {{ dict.label }}
19   - </el-radio>
20   - </el-radio-group>
21   - </el-form-item>
22   - <el-form-item label-width="180px" label="网关地址" prop="aliPayConfig.serverUrl">
23   - <el-radio-group v-model="form.aliPayConfig.serverUrl" size="medium">
24   - <el-radio v-for="dict in aliPayServerDatas" :key="dict.value" :label="dict.value">
25   - {{ dict.label }}
26   - </el-radio>
27   - </el-radio-group>
28   - </el-form-item>
29   - <el-form-item label-width="180px" label="算法类型" prop="aliPayConfig.signType">
30   - <el-radio-group v-model="form.aliPayConfig.signType" size="medium">
31   - <el-radio v-for="dict in aliPaySignTypeDatas" :key="dict.value" :label="dict.value">
32   - {{ dict.label }}
33   - </el-radio>
34   - </el-radio-group>
35   - </el-form-item>
36   - <el-form-item label-width="180px" label="公钥类型" prop="aliPayConfig.mode">
37   - <el-radio-group v-model="form.aliPayConfig.mode" size="medium">
38   - <el-radio v-for="dict in aliPayModeDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
39   - {{ dict.label }}
40   - </el-radio>
41   - </el-radio-group>
42   - </el-form-item>
43   - <div v-if="form.aliPayConfig.mode === 1">
44   - <el-form-item label-width="180px" label="应用私钥" prop="aliPayConfig.privateKey">
45   - <el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="form.aliPayConfig.privateKey"
46   - placeholder="请输入应用私钥" clearable :style="{width: '100%'}">
47   - </el-input>
48   - </el-form-item>
49   - <el-form-item label-width="180px" label="支付宝公钥" prop="aliPayConfig.alipayPublicKey">
50   - <el-input
51   - type="textarea"
52   - :autosize="{minRows: 8, maxRows: 8}"
53   - v-model="form.aliPayConfig.alipayPublicKey"
54   - placeholder="请输入支付宝公钥" clearable
55   - :style="{width: '100%'}">
56   - </el-input>
57   - </el-form-item>
58   - </div>
59   - <div v-if="form.aliPayConfig.mode === 2">
60   - <el-form-item label-width="180px" label="商户公钥应用证书" prop="aliPayConfig.appCertContent">
61   - <el-input v-model="form.aliPayConfig.appCertContent" type="textarea"
62   - placeholder="请上传商户公钥应用证书"
63   - readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
64   - </el-form-item>
65   - <el-form-item label-width="180px" label="">
66   - <el-upload
67   - action=""
68   - ref="privateKeyContentFile"
69   - :limit="1"
70   - :accept="fileAccept"
71   - :http-request="appCertUpload"
72   - :before-upload="fileBeforeUpload">
73   - <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
74   - </el-upload>
75   - </el-form-item>
76   - <el-form-item label-width="180px" label="支付宝公钥证书" prop="aliPayConfig.alipayPublicCertContent">
77   - <el-input v-model="form.aliPayConfig.alipayPublicCertContent" type="textarea"
78   - placeholder="请上传支付宝公钥证书"
79   - readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
80   - </el-form-item>
81   - <el-form-item label-width="180px" label="">
82   - <el-upload
83   - ref="privateCertContentFile"
84   - action=""
85   - :limit="1"
86   - :accept="fileAccept"
87   - :before-upload="fileBeforeUpload"
88   - :http-request="alipayPublicCertUpload">
89   - <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
90   - </el-upload>
91   - </el-form-item>
92   - <el-form-item label-width="180px" label="根证书" prop="aliPayConfig.rootCertContent">
93   - <el-input
94   - v-model="form.aliPayConfig.rootCertContent"
95   - type="textarea"
96   - placeholder="请上传根证书"
97   - readonly :autosize="{minRows: 8, maxRows: 8}"
98   - :style="{width: '100%'}">
99   - </el-input>
100   - </el-form-item>
101   - <el-form-item label-width="180px" label="">
102   - <el-upload
103   - ref="privateCertContentFile"
104   - :limit="1"
105   - :accept="fileAccept"
106   - action=""
107   - :before-upload="fileBeforeUpload"
108   - :http-request="rootCertUpload">
109   - <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
110   - </el-upload>
111   - </el-form-item>
112   - </div>
113   - <el-form-item label-width="180px" label="备注" prop="remark">
114   - <el-input v-model="form.remark" :style="{width: '100%'}"></el-input>
115   - </el-form-item>
116   - </el-form>
117   - <div slot="footer" class="dialog-footer">
118   - <el-button @click="close">取消</el-button>
119   - <el-button type="primary" @click="handleConfirm">确定</el-button>
120   - </div>
121   - </el-dialog>
122   - </div>
123   -</template>
124   -<script>
125   -import {DICT_TYPE, getDictDatas} from "@/utils/dict";
126   -import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
127   -
128   -const defaultForm = {
129   - code: '',
130   - status: null,
131   - remark: '',
132   - feeRate: null,
133   - appId: '',
134   - aliPayConfig: {
135   - appId: '',
136   - serverUrl: null,
137   - signType: '',
138   - mode: null,
139   - privateKey: '',
140   - alipayPublicKey: '',
141   - appCertContent: '',
142   - alipayPublicCertContent: '',
143   - rootCertContent: ''
144   - }
145   -};
146   -
147   -export default {
148   - name: "aliPayChannelForm",
149   - components: {},
150   - props: {
151   - // 传输的参数
152   - transferParam: {
153   - // 加载动画
154   - "loading": false,
155   - // 是否修改
156   - "edit": false,
157   - // 是否显示
158   - "aliPayOpen": false,
159   - // 应用ID
160   - "appId": null,
161   - // 渠道编码
162   - "payCode": null,
163   - }
164   - },
165   - data() {
166   - return {
167   - title:'',
168   - form: JSON.parse(JSON.stringify(defaultForm)),
169   - rules: {
170   - feeRate: [{
171   - required: true,
172   - message: '请输入渠道费率',
173   - trigger: 'blur'
174   - }],
175   - 'aliPayConfig.appId': [{
176   - required: true,
177   - message: '请输入开放平台上创建的应用的 ID',
178   - trigger: 'blur'
179   - }],
180   - status: [{
181   - required: true,
182   - message: '渠道状态不能为空',
183   - trigger: 'blur'
184   - }],
185   - 'aliPayConfig.serverUrl': [{
186   - required: true,
187   - message: '请传入网关地址',
188   - trigger: 'blur'
189   - }],
190   - 'aliPayConfig.signType': [{
191   - required: true,
192   - message: '请传入签名算法类型',
193   - trigger: 'blur'
194   - }],
195   - 'aliPayConfig.mode': [{
196   - required: true,
197   - message: '公钥类型不能为空',
198   - trigger: 'blur'
199   - }],
200   - 'aliPayConfig.privateKey': [{
201   - required: true,
202   - message: '请输入商户私钥',
203   - trigger: 'blur'
204   - }],
205   - 'aliPayConfig.alipayPublicKey': [{
206   - required: true,
207   - message: '请输入支付宝公钥字符串',
208   - trigger: 'blur'
209   - }],
210   - 'aliPayConfig.appCertContent': [{
211   - required: true,
212   - message: '请上传商户公钥应用证书',
213   - trigger: 'blur'
214   - }],
215   - 'aliPayConfig.alipayPublicCertContent': [{
216   - required: true,
217   - message: '请上传支付宝公钥证书',
218   - trigger: 'blur'
219   - }],
220   - 'aliPayConfig.rootCertContent': [{
221   - required: true,
222   - message: '请上传指定根证书',
223   - trigger: 'blur'
224   - }],
225   - },
226   - fileAccept: ".crt",
227   - // 渠道状态 数据字典
228   - statusDictDatas: getDictDatas(DICT_TYPE.COMMON_STATUS),
229   - // 支付宝加密方式
230   - aliPaySignTypeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SIGN_TYPE),
231   - // 版本状态 数据字典
232   - aliPayModeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_MODE),
233   - // 支付宝网关地址
234   - aliPayServerDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SERVER_TYPE),
235   - }
236   - },
237   - watch: {
238   - transferParam: {
239   - deep: true, // 深度监听
240   - handler(newVal) {
241   - if (newVal.aliPayOpen) {
242   - this.form.code = newVal.payCode;
243   - this.form.appId = newVal.appId;
244   - // 只有在初次进来为编辑 并且为加载中的时候才回去请求数据
245   - if (newVal.edit === true && newVal.loading) {
246   - this.title = "编辑支付渠道";
247   - this.init();
248   - } else {
249   - this.title = "创建支付渠道";
250   - }
251   - }
252   - }
253   - }
254   - },
255   -
256   - methods: {
257   - init() {
258   - getChannel(this.transferParam.appId, this.transferParam.payCode)
259   - .then(response => {
260   - this.form.id = response.data.id;
261   - this.form.feeRate = response.data.feeRate;
262   - this.form.status = response.data.status;
263   - this.form.remark = response.data.remark;
264   -
265   - let config = JSON.parse(response.data.config);
266   - this.form.aliPayConfig.appId = config.appId;
267   - this.form.aliPayConfig.serverUrl = config.serverUrl;
268   - this.form.aliPayConfig.signType = config.signType;
269   - this.form.aliPayConfig.mode = config.mode;
270   - this.form.aliPayConfig.privateKey = config.privateKey;
271   - this.form.aliPayConfig.alipayPublicKey = config.alipayPublicKey;
272   - this.form.aliPayConfig.appCertContent = config.appCertContent;
273   - this.form.aliPayConfig.alipayPublicCertContent = config.alipayPublicCertContent;
274   - this.form.aliPayConfig.rootCertContent = config.rootCertContent;
275   - this.transferParam.loading = false;
276   - })
277   - },
278   - close() {
279   - this.transferParam.aliPayOpen = false;
280   - this.form = JSON.parse(JSON.stringify(defaultForm));
281   - },
282   - handleConfirm() {
283   - this.$refs['aliPayForm'].validate(valid => {
284   - if (!valid) {
285   - return
286   - }
287   - let data = this.form;
288   - data.config = JSON.stringify(this.form.aliPayConfig);
289   - if (this.transferParam.edit) {
290   - updateChannel(data).then(response => {
291   - if (response.code === 0) {
292   - this.$modal.msgSuccess("修改成功");
293   - this.close();
294   - }
295   -
296   - })
297   - } else {
298   -
299   - createChannel(data).then(response => {
300   - if (response.code === 0) {
301   - this.$modal.msgSuccess("新增成功");
302   - this.$parent.refreshTable();
303   - this.close();
304   - }
305   - });
306   - }
307   - });
308   - },
309   - fileBeforeUpload(file) {
310   - let format = '.' + file.name.split(".")[1];
311   - if (format !== this.fileAccept) {
312   - this.$message.error('请上传指定格式"' + this.fileAccept + '"文件');
313   - return false;
314   - }
315   - let isRightSize = file.size / 1024 / 1024 < 2
316   - if (!isRightSize) {
317   - this.$message.error('文件大小超过 2MB')
318   - }
319   - return isRightSize
320   - },
321   - appCertUpload(event) {
322   - const readFile = new FileReader()
323   - readFile.onload = (e) => {
324   - this.form.aliPayConfig.appCertContent = e.target.result
325   - }
326   - readFile.readAsText(event.file);
327   - },
328   - alipayPublicCertUpload(event) {
329   - const readFile = new FileReader()
330   - readFile.onload = (e) => {
331   - this.form.aliPayConfig.alipayPublicCertContent = e.target.result
332   - }
333   - readFile.readAsText(event.file);
334   - },
335   - rootCertUpload(event) {
336   - const readFile = new FileReader()
337   - readFile.onload = (e) => {
338   - this.form.aliPayConfig.rootCertContent = e.target.result
339   - }
340   - readFile.readAsText(event.file);
341   - },
342   -
343   - }
344   -}
345   -
346   -</script>
347   -<style scoped>
348   -
349   -</style>
yudao-ui-admin/src/views/pay/app/components/alipayChannelForm.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-dialog :visible.sync="dialogVisible" :title="title" @closed="close" append-to-body width="800px">
  4 + <el-form ref="form" :model="formData" :rules="rules" size="medium" label-width="100px" v-loading="formLoading">
  5 + <el-form-item label-width="180px" label="渠道费率" prop="feeRate">
  6 + <el-input v-model="formData.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}">
  7 + <template slot="append">%</template>
  8 + </el-input>
  9 + </el-form-item>
  10 + <el-form-item label-width="180px" label="开放平台 APPID" prop="config.appId">
  11 + <el-input v-model="formData.config.appId" placeholder="请输入开放平台 APPID" clearable :style="{width: '100%'}">
  12 + </el-input>
  13 + </el-form-item>
  14 + <el-form-item label-width="180px" label="渠道状态" prop="status">
  15 + <el-radio-group v-model="formData.status" size="medium">
  16 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)" :key="parseInt(dict.value)"
  17 + :label="parseInt(dict.value)">
  18 + {{ dict.label }}
  19 + </el-radio>
  20 + </el-radio-group>
  21 + </el-form-item>
  22 + <el-form-item label-width="180px" label="网关地址" prop="config.serverUrl">
  23 + <el-radio-group v-model="formData.config.serverUrl" size="medium">
  24 + <el-radio label="https://openapi.alipay.com/gateway.do">线上环境</el-radio>
  25 + <el-radio label="https://openapi-sandbox.dl.alipaydev.com/gateway.do">沙箱环境</el-radio>
  26 + </el-radio-group>
  27 + </el-form-item>
  28 + <el-form-item label-width="180px" label="算法类型" prop="config.signType">
  29 + <el-radio-group v-model="formData.config.signType" size="medium">
  30 + <el-radio key="RSA2" label="RSA2">RSA2</el-radio>
  31 + </el-radio-group>
  32 + </el-form-item>
  33 + <el-form-item label-width="180px" label="公钥类型" prop="config.mode">
  34 + <el-radio-group v-model="formData.config.mode" size="medium">
  35 + <el-radio key="公钥模式" :label="1">公钥模式</el-radio>
  36 + <el-radio key="证书模式" :label="2">证书模式</el-radio>
  37 + </el-radio-group>
  38 + </el-form-item>
  39 + <div v-if="formData.config.mode === 1">
  40 + <el-form-item label-width="180px" label="应用私钥" prop="config.privateKey">
  41 + <el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="formData.config.privateKey"
  42 + placeholder="请输入应用私钥" clearable :style="{width: '100%'}">
  43 + </el-input>
  44 + </el-form-item>
  45 + <el-form-item label-width="180px" label="支付宝公钥" prop="config.alipayPublicKey">
  46 + <el-input
  47 + type="textarea"
  48 + :autosize="{minRows: 8, maxRows: 8}"
  49 + v-model="formData.config.alipayPublicKey"
  50 + placeholder="请输入支付宝公钥" clearable
  51 + :style="{width: '100%'}">
  52 + </el-input>
  53 + </el-form-item>
  54 + </div>
  55 + <div v-if="formData.config.mode === 2">
  56 + <el-form-item label-width="180px" label="商户公钥应用证书" prop="config.appCertContent">
  57 + <el-input v-model="formData.config.appCertContent" type="textarea"
  58 + placeholder="请上传商户公钥应用证书"
  59 + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
  60 + </el-form-item>
  61 + <el-form-item label-width="180px" label="">
  62 + <el-upload
  63 + action=""
  64 + ref="privateKeyContentFile"
  65 + :limit="1"
  66 + :accept="fileAccept"
  67 + :http-request="appCertUpload"
  68 + :before-upload="fileBeforeUpload">
  69 + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
  70 + </el-upload>
  71 + </el-form-item>
  72 + <el-form-item label-width="180px" label="支付宝公钥证书" prop="config.alipayPublicCertContent">
  73 + <el-input v-model="formData.config.alipayPublicCertContent" type="textarea"
  74 + placeholder="请上传支付宝公钥证书"
  75 + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
  76 + </el-form-item>
  77 + <el-form-item label-width="180px" label="">
  78 + <el-upload
  79 + ref="privateCertContentFile"
  80 + action=""
  81 + :limit="1"
  82 + :accept="fileAccept"
  83 + :before-upload="fileBeforeUpload"
  84 + :http-request="alipayPublicCertUpload">
  85 + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
  86 + </el-upload>
  87 + </el-form-item>
  88 + <el-form-item label-width="180px" label="根证书" prop="config.rootCertContent">
  89 + <el-input
  90 + v-model="formData.config.rootCertContent"
  91 + type="textarea"
  92 + placeholder="请上传根证书"
  93 + readonly :autosize="{minRows: 8, maxRows: 8}"
  94 + :style="{width: '100%'}">
  95 + </el-input>
  96 + </el-form-item>
  97 + <el-form-item label-width="180px" label="">
  98 + <el-upload
  99 + ref="privateCertContentFile"
  100 + :limit="1"
  101 + :accept="fileAccept"
  102 + action=""
  103 + :before-upload="fileBeforeUpload"
  104 + :http-request="rootCertUpload">
  105 + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
  106 + </el-upload>
  107 + </el-form-item>
  108 + </div>
  109 + <el-form-item label-width="180px" label="备注" prop="remark">
  110 + <el-input v-model="formData.remark" :style="{width: '100%'}"></el-input>
  111 + </el-form-item>
  112 + </el-form>
  113 + <div slot="footer" class="dialog-footer">
  114 + <el-button @click="close">取消</el-button>
  115 + <el-button type="primary" @click="submitForm">确定</el-button>
  116 + </div>
  117 + </el-dialog>
  118 + </div>
  119 +</template>
  120 +<script>
  121 +import { createChannel, getChannel, updateChannel } from "@/api/pay/channel";
  122 +import { CommonStatusEnum } from "@/utils/constants";
  123 +
  124 +export default {
  125 + name: "alipayChannelForm",
  126 + data() {
  127 + return {
  128 + dialogVisible: false,
  129 + formLoading: false,
  130 + title:'',
  131 + formData: {
  132 + appId: '',
  133 + code: '',
  134 + status: undefined,
  135 + feeRate: undefined,
  136 + remark: '',
  137 + config: {
  138 + appId: '',
  139 + serverUrl: null,
  140 + signType: '',
  141 + mode: null,
  142 + privateKey: '',
  143 + alipayPublicKey: '',
  144 + appCertContent: '',
  145 + alipayPublicCertContent: '',
  146 + rootCertContent: ''
  147 + }
  148 + },
  149 + rules: {
  150 + feeRate: [{ required: true, message: '请输入渠道费率', trigger: 'blur' }],
  151 + status: [{ required: true, message: '渠道状态不能为空', trigger: 'blur' }],
  152 + 'config.appId': [{ required: true, message: '请输入开放平台上创建的应用的 ID', trigger: 'blur' }],
  153 + 'config.serverUrl': [{ required: true, message: '请传入网关地址', trigger: 'blur' }],
  154 + 'config.signType': [{ required: true, message: '请传入签名算法类型', trigger: 'blur' }],
  155 + 'config.mode': [{ required: true, message: '公钥类型不能为空', trigger: 'blur'}],
  156 + 'config.privateKey': [{ required: true, message: '请输入商户私钥', trigger: 'blur' }],
  157 + 'config.alipayPublicKey': [{ required: true, message: '请输入支付宝公钥字符串', trigger: 'blur' }],
  158 + 'config.appCertContent': [{ required: true, message: '请上传商户公钥应用证书', trigger: 'blur' }],
  159 + 'config.alipayPublicCertContent': [{ required: true, message: '请上传支付宝公钥证书', trigger: 'blur'}],
  160 + 'config.rootCertContent': [{ required: true, message: '请上传指定根证书', trigger: 'blur' }],
  161 + },
  162 + fileAccept: ".crt",
  163 + }
  164 + },
  165 + methods: {
  166 + open(appId, code) {
  167 + this.dialogVisible = true;
  168 + this.formLoading = true;
  169 + this.reset(appId, code);
  170 + getChannel(appId, code).then(response => {
  171 + if (response.data && response.data.id) {
  172 + this.formData = response.data;
  173 + this.formData.config = JSON.parse(response.data.config);
  174 + }
  175 + this.title = this.formData.id ? '创建支付渠道' : '编辑支付渠道'
  176 + }).finally(() => {
  177 + this.formLoading = false;
  178 + });
  179 + },
  180 + close() {
  181 + this.dialogVisible = false;
  182 + this.reset(undefined, undefined);
  183 + },
  184 + submitForm() {
  185 + this.$refs['form'].validate(valid => {
  186 + if (!valid) {
  187 + return
  188 + }
  189 + const data = { ...this.formData };
  190 + data.config = JSON.stringify(this.formData.config);
  191 + if (!data.id) {
  192 + createChannel(data).then(response => {
  193 + this.$modal.msgSuccess("新增成功");
  194 + this.$emit('success')
  195 + this.close();
  196 + });
  197 + } else {
  198 + updateChannel(data).then(response => {
  199 + this.$modal.msgSuccess("修改成功");
  200 + this.$emit('success')
  201 + this.close();
  202 + })
  203 + }
  204 + });
  205 + },
  206 + /** 重置表单 */
  207 + reset(appId, code) {
  208 + this.formData = {
  209 + appId: appId,
  210 + code: code,
  211 + status: CommonStatusEnum.ENABLE,
  212 + remark: '',
  213 + feeRate: null,
  214 + config: {
  215 + appId: '',
  216 + serverUrl: null,
  217 + signType: 'RSA2',
  218 + mode: null,
  219 + privateKey: '',
  220 + alipayPublicKey: '',
  221 + appCertContent: '',
  222 + alipayPublicCertContent: '',
  223 + rootCertContent: ''
  224 + }
  225 + }
  226 + this.resetForm('form')
  227 + },
  228 + fileBeforeUpload(file) {
  229 + let format = '.' + file.name.split(".")[1];
  230 + if (format !== this.fileAccept) {
  231 + this.$message.error('请上传指定格式"' + this.fileAccept + '"文件');
  232 + return false;
  233 + }
  234 + let isRightSize = file.size / 1024 / 1024 < 2
  235 + if (!isRightSize) {
  236 + this.$message.error('文件大小超过 2MB')
  237 + }
  238 + return isRightSize
  239 + },
  240 + appCertUpload(event) {
  241 + const readFile = new FileReader()
  242 + readFile.onload = (e) => {
  243 + this.formData.config.appCertContent = e.target.result
  244 + }
  245 + readFile.readAsText(event.file);
  246 + },
  247 + alipayPublicCertUpload(event) {
  248 + const readFile = new FileReader()
  249 + readFile.onload = (e) => {
  250 + this.formData.config.alipayPublicCertContent = e.target.result
  251 + }
  252 + readFile.readAsText(event.file);
  253 + },
  254 + rootCertUpload(event) {
  255 + const readFile = new FileReader()
  256 + readFile.onload = (e) => {
  257 + this.formData.config.rootCertContent = e.target.result
  258 + }
  259 + readFile.readAsText(event.file);
  260 + },
  261 +
  262 + }
  263 +}
  264 +
  265 +</script>
  266 +<style scoped>
  267 +
  268 +</style>
... ...
yudao-ui-admin/src/views/pay/app/index.vue
... ... @@ -213,8 +213,10 @@
213 213 <el-button @click="cancel">取 消</el-button>
214 214 </div>
215 215 </el-dialog>
216   - <wechat-channel-form :transferParam="channelParam"></wechat-channel-form>
217   - <ali-pay-channel-form :transferParam="channelParam"></ali-pay-channel-form>
  216 +
  217 + <!-- 弹窗 -->
  218 + <wechat-channel-form :transferParam="channelParam" />
  219 + <alipay-channel-form ref="alipayChannelFormRef" @success="getList" />
218 220 </div>
219 221 </template>
220 222  
... ... @@ -223,13 +225,13 @@ import { createApp, updateApp, changeAppStatus, deleteApp, getApp, getAppPage }
223 225 import { DICT_TYPE, getDictDatas } from "@/utils/dict";
224 226 import { PayType, PayChannelEnum, CommonStatusEnum } from "@/utils/constants";
225 227 import wechatChannelForm from "@/views/pay/app/components/wechatChannelForm";
226   -import aliPayChannelForm from "@/views/pay/app/components/aliPayChannelForm";
  228 +import alipayChannelForm from "@/views/pay/app/components/alipayChannelForm";
227 229  
228 230 export default {
229 231 name: "PayApp",
230 232 components: {
231 233 "wechatChannelForm": wechatChannelForm,
232   - "aliPayChannelForm": aliPayChannelForm
  234 + "alipayChannelForm": alipayChannelForm
233 235 },
234 236 data() {
235 237 return {
... ... @@ -283,13 +285,6 @@ export default {
283 285 "appId": null,
284 286 // 渠道编码
285 287 "payCode": null,
286   - // 商户对象
287   - "payMerchant": {
288   - // 编号
289   - "id": null,
290   - // 名称
291   - "name": null
292   - },
293 288 }
294 289 };
295 290 },
... ... @@ -424,14 +419,12 @@ export default {
424 419 this.channelParam.aliPayOpen = false;
425 420 }
426 421 if (type === PayType.ALIPAY) {
427   - this.channelParam.aliPayOpen = true;
428   - this.channelParam.wechatOpen = false;
  422 + console.log(this.$refs['alipayChannelFormRef'])
  423 + this.$refs['alipayChannelFormRef'].open(row.id, payCode);
  424 + return;
  425 + // this.channelParam.aliPayOpen = true;
  426 + // this.channelParam.wechatOpen = false;
429 427 }
430   - this.channelParam.edit = false;
431   - this.channelParam.loading = false;
432   - this.channelParam.appId = row.id;
433   - this.channelParam.payCode = payCode;
434   - this.channelParam.payMerchant = row.payMerchant;
435 428 },
436 429 /**
437 430 * 根据渠道编码判断渠道列表中是否存在
... ...
yudao-ui-admin/src/views/pay/notify/index.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 +
  4 + <!-- 搜索工作栏 -->
  5 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
  6 + <el-form-item label="应用编号" prop="appId">
  7 + <el-select clearable v-model="queryParams.appId" filterable placeholder="请选择应用信息">
  8 + <el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
  9 + </el-select>
  10 + </el-form-item>
  11 + <el-form-item label="通知类型" prop="type">
  12 + <el-select v-model="queryParams.type" placeholder="请选择通知类型" clearable size="small">
  13 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_NOTIFY_TYPE)"
  14 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  15 + </el-select>
  16 + </el-form-item>
  17 + <el-form-item label="关联编号" prop="dataId">
  18 + <el-input v-model="queryParams.dataId" placeholder="请输入关联编号" clearable @keyup.enter.native="handleQuery"/>
  19 + </el-form-item>
  20 + <el-form-item label="通知状态" prop="status">
  21 + <el-select v-model="queryParams.status" placeholder="请选择通知状态" clearable size="small">
  22 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_NOTIFY_STATUS)"
  23 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  24 + </el-select>
  25 + </el-form-item>
  26 + <el-form-item label="商户订单编号" prop="merchantOrderId">
  27 + <el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户订单编号" clearable @keyup.enter.native="handleQuery"/>
  28 + </el-form-item>
  29 + <el-form-item label="创建时间" prop="createTime">
  30 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  31 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  32 + </el-form-item>
  33 + <el-form-item>
  34 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  35 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  36 + </el-form-item>
  37 + </el-form>
  38 +
  39 + <!-- 列表 -->
  40 + <el-table v-loading="loading" :data="list">
  41 + <el-table-column label="任务编号" align="center" prop="id" />
  42 + <el-table-column label="应用编号" align="center" prop="appName" />
  43 + <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" />
  44 + <el-table-column label="通知类型" align="center" prop="type">
  45 + <template v-slot="scope">
  46 + <dict-tag :type="DICT_TYPE.PAY_NOTIFY_TYPE" :value="scope.row.type" />
  47 + </template>
  48 + </el-table-column>
  49 + <el-table-column label="关联编号" align="center" prop="dataId" />
  50 + <el-table-column label="通知状态" align="center" prop="status">
  51 + <template v-slot="scope">
  52 + <dict-tag :type="DICT_TYPE.PAY_NOTIFY_STATUS" :value="scope.row.status" />
  53 + </template>
  54 + </el-table-column>
  55 + <el-table-column label="最后通知时间" align="center" prop="lastExecuteTime" width="180">
  56 + <template v-slot="scope">
  57 + <span>{{ parseTime(scope.row.lastExecuteTime) }}</span>
  58 + </template>
  59 + </el-table-column>
  60 + <el-table-column label="下次通知时间" align="center" prop="nextNotifyTime" width="180">
  61 + <template v-slot="scope">
  62 + <span>{{ parseTime(scope.row.nextNotifyTime) }}</span>
  63 + </template>
  64 + </el-table-column>
  65 + <el-table-column label="通知次数" align="center" prop="notifyTimes">
  66 + <template v-slot="scope">
  67 + <el-tag size="mini" type="success">
  68 + {{ scope.row.notifyTimes }} / {{ scope.row.maxNotifyTimes }}
  69 + </el-tag>
  70 + </template>
  71 + </el-table-column>
  72 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  73 + <template v-slot="scope">
  74 + <el-button size="mini" type="text" icon="el-icon-search" @click="handleDetail(scope.row)"
  75 + v-hasPermi="['pay:notify:query']">查看详情
  76 + </el-button>
  77 + </template>
  78 + </el-table-column>
  79 + </el-table>
  80 + <!-- 分页组件 -->
  81 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  82 + @pagination="getList"/>
  83 +
  84 + <!-- 对话框(详情) -->
  85 + <el-dialog title="通知详情" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
  86 + <el-descriptions :column="2" label-class-name="desc-label">
  87 + <el-descriptions-item label="商户订单编号">
  88 + <el-tag size="small">{{ notifyDetail.merchantOrderId }}</el-tag>
  89 + </el-descriptions-item>
  90 + <el-descriptions-item label="通知状态">
  91 + <dict-tag :type="DICT_TYPE.PAY_NOTIFY_STATUS" :value="notifyDetail.status" size="small" />
  92 + </el-descriptions-item>
  93 + </el-descriptions>
  94 + <el-descriptions :column="2" label-class-name="desc-label">
  95 + <el-descriptions-item label="应用编号">{{ notifyDetail.appId }}</el-descriptions-item>
  96 + <el-descriptions-item label="应用名称">{{ notifyDetail.appName }}</el-descriptions-item>
  97 + </el-descriptions>
  98 + <el-descriptions :column="2" label-class-name="desc-label">
  99 + <el-descriptions-item label="关联编号">{{ notifyDetail.dataId }}</el-descriptions-item>
  100 + <el-descriptions-item label="通知类型">
  101 + <dict-tag :type="DICT_TYPE.PAY_NOTIFY_TYPE" :value="notifyDetail.type" />
  102 + </el-descriptions-item>
  103 + </el-descriptions>
  104 + <el-descriptions :column="2" label-class-name="desc-label">
  105 + <el-descriptions-item label="通知次数">{{ notifyDetail.notifyTimes }}</el-descriptions-item>
  106 + <el-descriptions-item label="最大通知次数">{{ notifyDetail.maxNotifyTimes }}</el-descriptions-item>
  107 + </el-descriptions>
  108 + <el-descriptions :column="2" label-class-name="desc-label">
  109 + <el-descriptions-item label="最后通知时间">{{ parseTime(notifyDetail.lastExecuteTime) }}</el-descriptions-item>
  110 + <el-descriptions-item label="下次通知时间">{{ parseTime(notifyDetail.nextNotifyTime) }}</el-descriptions-item>
  111 + </el-descriptions>
  112 + <el-descriptions :column="2" label-class-name="desc-label">
  113 + <el-descriptions-item label="创建时间">{{ parseTime(notifyDetail.createTime) }}</el-descriptions-item>
  114 + <el-descriptions-item label="更新时间">{{ parseTime(notifyDetail.updateTime) }}</el-descriptions-item>
  115 + </el-descriptions>
  116 + <!-- 分割线 -->
  117 + <el-divider />
  118 + <el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
  119 + <el-descriptions-item label="回调日志">
  120 + <el-table :data="notifyDetail.logs">
  121 + <el-table-column label="日志编号" align="center" prop="id" />
  122 + <el-table-column label="通知状态" align="center" prop="status">
  123 + <template v-slot="scope">
  124 + <dict-tag :type="DICT_TYPE.PAY_NOTIFY_STATUS" :value="scope.row.status" />
  125 + </template>
  126 + </el-table-column>
  127 + <el-table-column label="通知次数" align="center" prop="notifyTimes" />
  128 + <el-table-column label="通知时间" align="center" prop="lastExecuteTime" width="180">
  129 + <template v-slot="scope">
  130 + <span>{{ parseTime(scope.row.createTime) }}</span>
  131 + </template>
  132 + </el-table-column>
  133 + <el-table-column label="响应结果" align="center" prop="response" />
  134 + </el-table>
  135 + </el-descriptions-item>
  136 + </el-descriptions>
  137 + </el-dialog>
  138 + </div>
  139 +</template>
  140 +
  141 +<script>
  142 +import { getNotifyTaskPage, getNotifyTaskDetail } from "@/api/pay/notify";
  143 +import { getAppList } from "@/api/pay/app";
  144 +
  145 +export default {
  146 + name: "PayNotify",
  147 + data() {
  148 + return {
  149 + // 遮罩层
  150 + loading: true,
  151 + // 导出遮罩层
  152 + exportLoading: false,
  153 + // 显示搜索条件
  154 + showSearch: true,
  155 + // 总条数
  156 + total: 0,
  157 + // 支付通知列表
  158 + list: [],
  159 + // 是否显示弹出层
  160 + open: false,
  161 + // 查询参数
  162 + queryParams: {
  163 + pageNo: 1,
  164 + pageSize: 10,
  165 + appId: null,
  166 + type: null,
  167 + dataId: null,
  168 + status: null,
  169 + merchantOrderId: null,
  170 + createTime: [],
  171 + },
  172 +
  173 + // 支付应用列表集合
  174 + appList: [],
  175 + // 通知详情
  176 + notifyDetail: {
  177 + logs: []
  178 + },
  179 + };
  180 + },
  181 + created() {
  182 + this.getList();
  183 + // 获得筛选项
  184 + getAppList().then(response => {
  185 + this.appList = response.data;
  186 + })
  187 + },
  188 + methods: {
  189 + /** 查询列表 */
  190 + getList() {
  191 + this.loading = true;
  192 + // 执行查询
  193 + getNotifyTaskPage(this.queryParams).then(response => {
  194 + this.list = response.data.list;
  195 + this.total = response.data.total;
  196 + this.loading = false;
  197 + });
  198 + },
  199 + /** 搜索按钮操作 */
  200 + handleQuery() {
  201 + this.queryParams.pageNo = 1;
  202 + this.getList();
  203 + },
  204 + /** 重置按钮操作 */
  205 + resetQuery() {
  206 + this.resetForm("queryForm");
  207 + this.handleQuery();
  208 + },
  209 + /** 详情按钮操作 */
  210 + handleDetail(row) {
  211 + this.notifyDetail = {};
  212 + getNotifyTaskDetail(row.id).then(response => {
  213 + // 设置值
  214 + this.notifyDetail = response.data;
  215 + // 弹窗打开
  216 + this.open = true;
  217 + });
  218 + },
  219 + }
  220 +};
  221 +</script>
... ...
yudao-ui-admin/src/views/pay/order/index.vue
... ... @@ -8,35 +8,26 @@
8 8 <el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
9 9 </el-select>
10 10 </el-form-item>
11   - <el-form-item label="渠道编码" prop="channelCode">
12   - <el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable
13   - @clear="()=>{queryParams.channelCode = null}">
14   - <el-option v-for="dict in payChannelCodeDictDatum" :key="dict.value" :label="dict.label" :value="dict.value"/>
  11 + <el-form-item label="支付渠道" prop="channelCode">
  12 + <el-select v-model="queryParams.channelCode" placeholder="请选择支付渠道" clearable>
  13 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE)" :key="dict.value" :label="dict.label" :value="dict.value"/>
15 14 </el-select>
16 15 </el-form-item>
17   - <el-form-item label="商户订单编号" prop="merchantOrderId">
18   - <el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户订单编号" clearable
  16 + <el-form-item label="商户单号" prop="merchantOrderId">
  17 + <el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户单号" clearable
19 18 @keyup.enter.native="handleQuery"/>
20 19 </el-form-item>
21   - <el-form-item label="渠道订单号" prop="channelOrderNo">
22   - <el-input v-model="queryParams.channelOrderNo" placeholder="请输入渠道订单号" clearable
  20 + <el-form-item label="支付单号" prop="no">
  21 + <el-input v-model="queryParams.no" placeholder="请输入支付单号" clearable
  22 + @keyup.enter.native="handleQuery"/>
  23 + </el-form-item>
  24 + <el-form-item label="渠道单号" prop="channelOrderNo">
  25 + <el-input v-model="queryParams.channelOrderNo" placeholder="请输入渠道单号" clearable
23 26 @keyup.enter.native="handleQuery"/>
24 27 </el-form-item>
25 28 <el-form-item label="支付状态" prop="status">
26 29 <el-select v-model="queryParams.status" placeholder="请选择支付状态" clearable size="small">
27   - <el-option v-for="dict in payOrderDictDatum" :key="parseInt(dict.value)"
28   - :label="dict.label" :value="parseInt(dict.value)"/>
29   - </el-select>
30   - </el-form-item>
31   - <el-form-item label="退款状态" prop="refundStatus">
32   - <el-select v-model="queryParams.refundStatus" placeholder="请选择退款状态" clearable>
33   - <el-option v-for="dict in payOrderRefundDictDatum" :key="parseInt(dict.value)"
34   - :label="dict.label" :value="parseInt(dict.value)"/>
35   - </el-select>
36   - </el-form-item>
37   - <el-form-item label="回调商户状态" prop="notifyStatus">
38   - <el-select v-model="queryParams.notifyStatus" placeholder="请选择订单回调商户状态" clearable>
39   - <el-option v-for="dict in payOrderNotifyDictDatum" :key="parseInt(dict.value)"
  30 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_ORDER_STATUS)" :key="parseInt(dict.value)"
40 31 :label="dict.label" :value="parseInt(dict.value)"/>
41 32 </el-select>
42 33 </el-form-item>
... ... @@ -62,69 +53,64 @@
62 53  
63 54 <!-- 列表 -->
64 55 <el-table v-loading="loading" :data="list">
65   - <el-table-column label="订单编号" align="center" prop="id" width="80"/>
66   - <el-table-column label="支付渠道" align="center" width="130">
  56 + <el-table-column label="编号" align="center" prop="id" width="80"/>
  57 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
67 58 <template v-slot="scope">
68   - <el-popover trigger="hover" placement="top">
69   - <p>应用名称: {{ scope.row.appName }}</p>
70   - <p>渠道名称: {{ scope.row.channelCodeName }}</p>
71   - <div slot="reference" class="name-wrapper">
72   - {{ scope.row.channelCodeName }}
73   - </div>
74   - </el-popover>
  59 + <span>{{ parseTime(scope.row.createTime) }}</span>
75 60 </template>
76 61 </el-table-column>
77   - <el-table-column label="支付订单" align="left" width="280">
  62 + <el-table-column label="支付金额" align="center" prop="price" width="100">
78 63 <template v-slot="scope">
79   - <p class="order-font"><el-tag size="mini">商户单号</el-tag> {{scope.row.merchantOrderId}}</p>
80   - <p class="order-font"><el-tag size="mini" type="warning">支付单号</el-tag> {{scope.row.channelOrderNo}}</p>
  64 + ¥{{ (scope.row.price / 100.0).toFixed(2) }}
81 65 </template>
82 66 </el-table-column>
83   - <el-table-column label="商品标题" align="center" prop="subject" width="180" :show-overflow-tooltip="true"/>
84   - <el-table-column label="支付金额" align="center" prop="price" width="100">
  67 + <el-table-column label="退款金额" align="center" prop="refundPrice" width="100">
85 68 <template v-slot="scope">
86   - ¥{{ parseFloat(scope.row.price / 100).toFixed(2) }}
  69 + ¥{{ (scope.row.refundPrice / 100.0).toFixed(2) }}
87 70 </template>
88 71 </el-table-column>
89 72 <el-table-column label="手续金额" align="center" prop="channelFeePrice" width="100">
90 73 <template v-slot="scope">
91   - ¥{{ parseFloat(scope.row.channelFeePrice / 100).toFixed(2) }}
  74 + ¥{{ (scope.row.channelFeePrice / 100.0).toFixed(2) }}
92 75 </template>
93 76 </el-table-column>
94   - <el-table-column label="退款金额" align="center" prop="refundPrice" width="100">
  77 + <el-table-column label="订单号" align="left" width="300">
95 78 <template v-slot="scope">
96   - ¥{{ parseFloat(scope.row.refundPrice / 100).toFixed(2) }}
  79 + <p class="order-font"><el-tag size="mini">
  80 + 商户</el-tag> {{scope.row.merchantOrderId}}
  81 + </p>
  82 + <p class="order-font" v-if="scope.row.no">
  83 + <el-tag size="mini" type="warning">支付</el-tag> {{scope.row.no}}
  84 + </p>
  85 + <p class="order-font" v-if="scope.row.channelOrderNo">
  86 + <el-tag size="mini" type="success">渠道</el-tag> {{scope.row.channelOrderNo}}
  87 + </p>
97 88 </template>
98 89 </el-table-column>
99 90 <el-table-column label="支付状态" align="center" prop="status">
100 91 <template v-slot="scope">
101 92 <dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="scope.row.status" />
102 93 </template>
103   -
104 94 </el-table-column>
105   -<!-- <el-table-column label="退款状态" align="center" prop="refundStatus">-->
106   -<!-- <template v-slot="scope">-->
107   -<!-- <span>{{ getDictDataLabel(DICT_TYPE.PAY_ORDER_REFUND_STATUS, scope.row.refundStatus) }}</span>-->
108   -<!-- </template>-->
109   -<!-- </el-table-column>-->
110   - <el-table-column label="回调状态" align="center" prop="notifyStatus" width="100">
  95 + <el-table-column label="支付渠道" align="center" width="140">
111 96 <template v-slot="scope">
112   - <dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
  97 + <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="scope.row.channelCode" />
113 98 </template>
114 99 </el-table-column>
115   - <el-table-column label="创建时间" align="center" prop="createTime" width="100">
  100 + <el-table-column label="支付时间" align="center" prop="successTime" width="180">
116 101 <template v-slot="scope">
117   - <span>{{ parseTime(scope.row.createTime) }}</span>
  102 + <span>{{ parseTime(scope.row.successTime) }}</span>
118 103 </template>
119 104 </el-table-column>
120   - <el-table-column label="支付时间" align="center" prop="successTime" width="100">
  105 + <el-table-column label="支付应用" align="center" prop="successTime" width="100">
121 106 <template v-slot="scope">
122   - <span>{{ parseTime(scope.row.successTime) }}</span>
  107 + <span>{{ scope.row.appName }}</span>
123 108 </template>
124 109 </el-table-column>
  110 + <el-table-column label="商品标题" align="center" prop="subject" width="180" :show-overflow-tooltip="true"/>
125 111 <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
126 112 <template v-slot="scope">
127   - <el-button size="mini" type="text" icon="el-icon-search" @click="handleQueryDetails(scope.row)"
  113 + <el-button size="mini" type="text" icon="el-icon-search" @click="handleDetail(scope.row)"
128 114 v-hasPermi="['pay:order:query']">查看详情
129 115 </el-button>
130 116 </template>
... ... @@ -134,67 +120,71 @@
134 120 <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
135 121 @pagination="getList"/>
136 122  
137   - <!-- 对话框(添加 / 修改) -->
138   - <el-dialog title="订单详情" :visible.sync="open" width="50%">
139   - <el-descriptions :column="2" label-class-name="desc-label">
140   - <el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item>
141   - <el-descriptions-item label="商品名称">{{ orderDetail.subject }}</el-descriptions-item>
142   - </el-descriptions>
143   - <el-divider></el-divider>
  123 + <!-- 对话框(详情) -->
  124 + <el-dialog title="订单详情" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
144 125 <el-descriptions :column="2" label-class-name="desc-label">
145   - <el-descriptions-item label="商户订单号">
  126 + <el-descriptions-item label="商户单号">
146 127 <el-tag size="small">{{ orderDetail.merchantOrderId }}</el-tag>
147 128 </el-descriptions-item>
148   - <el-descriptions-item label="渠道订单号">
149   - <el-tag class="tag-purple" size="small">{{ orderDetail.channelOrderNo }}</el-tag>
  129 + <el-descriptions-item label="支付单号">
  130 + <el-tag type="warning" size="small" v-if="orderDetail.no">{{ orderDetail.no }}</el-tag>
150 131 </el-descriptions-item>
151   - <el-descriptions-item label="支付订单号">
152   - <el-tag v-if="orderDetail.payOrderExtension.no !== ''" class="tag-pink" size="small">
153   - {{ orderDetail.payOrderExtension.no }}
154   - </el-tag>
  132 + </el-descriptions>
  133 + <el-descriptions :column="2" label-class-name="desc-label">
  134 + <el-descriptions-item label="应用编号">{{ orderDetail.appId }}</el-descriptions-item>
  135 + <el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item>
  136 + </el-descriptions>
  137 + <el-descriptions :column="2" label-class-name="desc-label">
  138 + <el-descriptions-item label="支付状态">
  139 + <dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="orderDetail.status" size="small" />
155 140 </el-descriptions-item>
156   - <el-descriptions-item label="金额">
157   - <el-tag type="success" size="small">{{ parseFloat(orderDetail.price / 100, 2) }}</el-tag>
  141 + <el-descriptions-item label="支付金额">
  142 + <el-tag type="success" size="small">¥{{ (orderDetail.price / 100.0).toFixed(2) }}</el-tag>
158 143 </el-descriptions-item>
  144 + </el-descriptions>
  145 + <el-descriptions :column="2" label-class-name="desc-label">
159 146 <el-descriptions-item label="手续费">
160   - <el-tag type="warning" size="small">{{ parseFloat(orderDetail.channelFeePrice / 100, 2) }}</el-tag>
161   - </el-descriptions-item>
162   - <el-descriptions-item label="手续费比例">
163   - {{ parseFloat(orderDetail.channelFeeRate / 100, 2) }}%
164   - </el-descriptions-item>
165   - <el-descriptions-item label="支付状态">
166   - <dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="orderDetail.status" />
  147 + <el-tag type="warning" size="small">¥{{ (orderDetail.channelFeePrice / 100.0).toFixed(2) }}</el-tag>
167 148 </el-descriptions-item>
168   - <el-descriptions-item label="回调状态">
169   - <dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="orderDetail.notifyStatus" />
170   - </el-descriptions-item>
171   - <el-descriptions-item label="回调地址">{{ orderDetail.notifyUrl }}</el-descriptions-item>
172   - <el-descriptions-item label="创建时间">{{ parseTime(orderDetail.createTime) }}</el-descriptions-item>
  149 + <el-descriptions-item label="手续费比例">{{ (orderDetail.channelFeeRate / 100.0).toFixed(2) }}%</el-descriptions-item>
  150 + </el-descriptions>
  151 + <el-descriptions :column="2" label-class-name="desc-label">
173 152 <el-descriptions-item label="支付时间">{{ parseTime(orderDetail.successTime) }}</el-descriptions-item>
174 153 <el-descriptions-item label="失效时间">{{ parseTime(orderDetail.expireTime) }}</el-descriptions-item>
175   - <el-descriptions-item label="通知时间">{{ parseTime(orderDetail.notifyTime) }}</el-descriptions-item>
176 154 </el-descriptions>
177   - <el-divider></el-divider>
178 155 <el-descriptions :column="2" label-class-name="desc-label">
179   - <el-descriptions-item label="支付渠道">{{ orderDetail.channelCodeName }}</el-descriptions-item>
180   - <el-descriptions-item label="支付IP">{{ orderDetail.userIp }}</el-descriptions-item>
181   - <el-descriptions-item label="退款状态">
182   - <dict-tag :type="DICT_TYPE.PAY_ORDER_REFUND_STATUS" :value="orderDetail.refundStatus" />
  156 + <el-descriptions-item label="创建时间">{{ parseTime(orderDetail.createTime) }}</el-descriptions-item>
  157 + <el-descriptions-item label="更新时间">{{ parseTime(orderDetail.updateTime) }}</el-descriptions-item>
  158 + </el-descriptions>
  159 + <!-- 分割线 -->
  160 + <el-divider/>
  161 + <el-descriptions :column="2" label-class-name="desc-label">
  162 + <el-descriptions-item label="商品标题">{{ orderDetail.subject }}</el-descriptions-item>
  163 + <el-descriptions-item label="商品描述">{{ orderDetail.body }}</el-descriptions-item>
  164 + </el-descriptions>
  165 + <el-descriptions :column="2" label-class-name="desc-label">
  166 + <el-descriptions-item label="支付渠道">
  167 + <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="orderDetail.channelCode" />
183 168 </el-descriptions-item>
184   - <el-descriptions-item label="退款次数">{{ orderDetail.refundTimes }}</el-descriptions-item>
  169 + <el-descriptions-item label="支付 IP">{{ orderDetail.userIp }}</el-descriptions-item>
  170 + </el-descriptions>
  171 + <el-descriptions :column="2" label-class-name="desc-label">
  172 + <el-descriptions-item label="渠道单号">
  173 + <el-tag size="mini" type="success" v-if="orderDetail.channelOrderNo">{{ orderDetail.channelOrderNo }}</el-tag>
  174 + </el-descriptions-item>
  175 + <el-descriptions-item label="渠道用户">{{ orderDetail.channelUserId }}</el-descriptions-item>
  176 + </el-descriptions>
  177 + <el-descriptions :column="2" label-class-name="desc-label">
185 178 <el-descriptions-item label="退款金额">
186   - <el-tag type="warning">
187   - {{ parseFloat(orderDetail.refundPrice / 100, 2) }}
188   - </el-tag>
  179 + <el-tag size="mini" type="danger">¥{{ (orderDetail.refundPrice / 100.0).toFixed(2) }}</el-tag>
189 180 </el-descriptions-item>
  181 + <el-descriptions-item label="通知 URL">{{ orderDetail.notifyUrl }}</el-descriptions-item>
190 182 </el-descriptions>
191   - <el-divider></el-divider>
  183 + <!-- 分割线 -->
  184 + <el-divider />
192 185 <el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
193   - <el-descriptions-item label="商品描述">
194   - {{ orderDetail.body }}
195   - </el-descriptions-item>
196 186 <el-descriptions-item label="支付通道异步回调内容">
197   - {{ orderDetail.payOrderExtension.channelNotifyData }}
  187 + {{ orderDetail.extension.channelNotifyData }}
198 188 </el-descriptions-item>
199 189 </el-descriptions>
200 190 </el-dialog>
... ... @@ -203,35 +193,7 @@
203 193  
204 194 <script>
205 195 import { getOrderDetail, getOrderPage, exportOrderExcel } from "@/api/pay/order";
206   -import { DICT_TYPE, getDictDatas } from "@/utils/dict";
207   -import { getNowDateTime} from "@/utils/ruoyi";
208   -
209   -const defaultOrderDetail = {
210   - appName: '',
211   - channelCodeName: '',
212   - subject: '',
213   - merchantOrderId: null,
214   - channelOrderNo: '',
215   - body: '',
216   - price: null,
217   - channelFeeRate: null,
218   - channelFeePrice: null,
219   - userIp: '',
220   - status: null,
221   - notifyUrl: '',
222   - notifyStatus: null,
223   - refundStatus: null,
224   - refundTimes: '',
225   - refundPrice: null,
226   - createTime: '',
227   - successTime: '',
228   - notifyTime: '',
229   - expireTime: '',
230   - payOrderExtension: {
231   - channelNotifyData: '',
232   - no: ''
233   - }
234   -};
  196 +import { getAppList } from "@/api/pay/app";
235 197  
236 198 export default {
237 199 name: "PayOrder",
... ... @@ -250,75 +212,37 @@ export default {
250 212 title: "",
251 213 // 是否显示弹出层
252 214 open: false,
253   - // 查询参数 TODO 芋艿:多余字段的清理
  215 + // 查询参数
254 216 queryParams: {
255 217 pageNo: 1,
256 218 pageSize: 10,
257 219 appId: null,
258   - channelId: null,
259 220 channelCode: null,
260 221 merchantOrderId: null,
261   - subject: null,
262   - body: null,
263   - notifyUrl: null,
264   - notifyStatus: null,
265   - price: null,
266   - channelFeeRate: null,
267   - channelFeePrice: null,
268   - status: null,
269   - userIp: null,
270   - successExtensionId: null,
271   - refundStatus: null,
272   - refundTimes: null,
273   - refundPrice: null,
274   - channelUserId: null,
275 222 channelOrderNo: null,
276   - expireTime: [],
277   - successTime: [],
278   - notifyTime: [],
279   - createTime: []
  223 + no: null,
  224 + status: null,
  225 + createTime: [],
280 226 },
281   - // 商户加载遮罩层
282   - merchantLoading: false,
283   - // 商户列表集合
284   - merchantList: null,
  227 +
285 228 // 支付应用列表集合
286   - appList: null,
287   - // 支付渠道编码字典数据集合
288   - payChannelCodeDictDatum: getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE),
289   - // 订单回调商户状态字典数据集合
290   - payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
291   - // 订单状态字典数据集合
292   - payOrderDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_STATUS),
293   - // 订单退款状态字典数据集合
294   - payOrderRefundDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_REFUND_STATUS),
295   - orderDetail: JSON.parse(JSON.stringify(defaultOrderDetail)),
  229 + appList: [],
  230 + // 订单详情
  231 + orderDetail: {
  232 + extension: {}
  233 + },
296 234 };
297 235 },
298 236 created() {
299   - // 初始化默认时间
300   - this.initTime();
301 237 this.getList();
302   - this.handleGetMerchantListByName(null);
  238 + // 获得筛选项
  239 + getAppList().then(response => {
  240 + this.appList = response.data;
  241 + })
303 242 },
304 243 methods: {
305   - initTime(){
306   - this.queryParams.createTime = [getNowDateTime("00:00:00"), getNowDateTime("23:59:59")];
307   - },
308 244 /** 查询列表 */
309 245 getList() {
310   - // 判断选择的日期是否超过了一个月
311   - let oneMonthTime = 31 * 24 * 3600 * 1000;
312   - if (this.queryParams.createTime == null){
313   - this.initTime();
314   - } else {
315   - let minDateTime = new Date(this.queryParams.createTime[0]).getTime();
316   - let maxDateTime = new Date(this.queryParams.createTime[1]).getTime()
317   - if (maxDateTime - minDateTime > oneMonthTime) {
318   - this.$message.error('时间范围最大为 31 天!');
319   - return false;
320   - }
321   - }
322 246 this.loading = true;
323 247 // 执行查询
324 248 getOrderPage(this.queryParams).then(response => {
... ... @@ -339,22 +263,20 @@ export default {
339 263 /** 重置按钮操作 */
340 264 resetQuery() {
341 265 this.resetForm("queryForm");
342   - this.initTime();
343 266 this.handleQuery();
344 267 },
345   - /**
346   - * 查看订单详情
347   - */
348   - handleQueryDetails(row) {
349   - this.orderDetail = JSON.parse(JSON.stringify(defaultOrderDetail));
  268 + /** 详情按钮操作 */
  269 + handleDetail(row) {
  270 + this.orderDetail = {};
350 271 getOrderDetail(row.id).then(response => {
  272 + // 设置值
351 273 this.orderDetail = response.data;
352   - if (response.data.payOrderExtension === null) {
353   - this.orderDetail.payOrderExtension = Object.assign(defaultOrderDetail.payOrderExtension, {});
  274 + if (!this.orderDetail.extension) {
  275 + this.orderDetail.extension = {}
354 276 }
  277 + // 弹窗打开
355 278 this.open = true;
356 279 });
357   -
358 280 },
359 281 /** 导出按钮操作 */
360 282 handleExport() {
... ...
yudao-ui-admin/src/views/pay/refund/index.vue
... ... @@ -7,31 +7,30 @@
7 7 <el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
8 8 </el-select>
9 9 </el-form-item>
10   - <el-form-item label="渠道编码" prop="channelCode">
11   - <el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable
12   - @clear="()=>{queryParams.channelCode = null}">
13   - <el-option v-for="dict in payChannelCodeDictDatum" :key="dict.value" :label="dict.label" :value="dict.value"/>
  10 + <el-form-item label="退款渠道" prop="channelCode">
  11 + <el-select v-model="queryParams.channelCode" placeholder="请选择退款渠道" clearable>
  12 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE)" :key="dict.value" :label="dict.label" :value="dict.value"/>
14 13 </el-select>
15 14 </el-form-item>
16   - <el-form-item label="退款类型" prop="type">
17   - <el-select v-model="queryParams.type" placeholder="请选择退款类型" clearable>
18   - <el-option v-for="dict in payRefundOrderTypeDictDatum" :key="parseInt(dict.value)"
19   - :label="dict.label" :value="parseInt(dict.value)"/>
20   - </el-select>
  15 + <el-form-item label="商户支付单号" prop="merchantOrderId">
  16 + <el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户支付单号" clearable
  17 + @keyup.enter.native="handleQuery"/>
  18 + </el-form-item>
  19 + <el-form-item label="商户退款单号" prop="merchantRefundId">
  20 + <el-input v-model="queryParams.merchantRefundId" placeholder="请输入商户退款单号" clearable
  21 + @keyup.enter.native="handleQuery"/>
  22 + </el-form-item>
  23 + <el-form-item label="渠道支付单号" prop="channelOrderNo">
  24 + <el-input v-model="queryParams.channelOrderNo" placeholder="请输入渠道支付单号" clearable
  25 + @keyup.enter.native="handleQuery"/>
21 26 </el-form-item>
22   - <el-form-item label="商户退款订单号" prop="merchantRefundNo">
23   - <el-input v-model="queryParams.merchantRefundNo" placeholder="请输入商户退款订单号" clearable
  27 + <el-form-item label="渠道退款单号" prop="channelRefundNo">
  28 + <el-input v-model="queryParams.channelRefundNo" placeholder="请输入渠道退款单号" clearable
24 29 @keyup.enter.native="handleQuery"/>
25 30 </el-form-item>
26 31 <el-form-item label="退款状态" prop="status">
27 32 <el-select v-model="queryParams.status" placeholder="请选择退款状态" clearable>
28   - <el-option v-for="dict in payRefundOrderDictDatum" :key="parseInt(dict.value)"
29   - :label="dict.label" :value="parseInt(dict.value)"/>
30   - </el-select>
31   - </el-form-item>
32   - <el-form-item label="退款回调状态" prop="notifyStatus">
33   - <el-select v-model="queryParams.notifyStatus" placeholder="请选择通知商户退款结果的回调状态" clearable>
34   - <el-option v-for="dict in payOrderNotifyDictDatum" :key="parseInt(dict.value)"
  33 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_REFUND_STATUS)" :key="parseInt(dict.value)"
35 34 :label="dict.label" :value="parseInt(dict.value)"/>
36 35 </el-select>
37 36 </el-form-item>
... ... @@ -58,77 +57,62 @@
58 57 <!-- 列表 -->
59 58 <el-table v-loading="loading" :data="list">
60 59 <el-table-column label="编号" align="center" prop="id"/>
61   - <el-table-column label="支付渠道" align="center" width="130">
  60 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
62 61 <template v-slot="scope">
63   - <el-popover trigger="hover" placement="top">
64   - <p>应用名称: {{ scope.row.appName }}</p>
65   - <p>渠道名称: {{ scope.row.channelCodeName }}</p>
66   - <div slot="reference" class="name-wrapper">
67   - {{ scope.row.channelCodeName }}
68   - </div>
69   - </el-popover>
  62 + <span>{{ parseTime(scope.row.createTime) }}</span>
  63 + </template>
  64 + </el-table-column>
  65 + <el-table-column label="支付金额" align="center" prop="payPrice" width="100">
  66 + <template v-slot="scope" class="">
  67 + ¥{{ (scope.row.payPrice / 100.0).toFixed(2) }}
70 68 </template>
71 69 </el-table-column>
72   - <!-- <el-table-column label="交易订单号" align="center" prop="tradeNo" width="140"/>-->
73   - <!-- <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" width="140"/>-->
74   - <el-table-column label="商户订单号" align="left" width="230">
  70 + <el-table-column label="退款金额" align="center" prop="refundPrice" width="100">
  71 + <template v-slot="scope">
  72 + ¥{{ (scope.row.refundPrice / 100.0).toFixed(2) }}
  73 + </template>
  74 + </el-table-column>
  75 + <el-table-column label="退款订单号" align="left" width="300">
75 76 <template v-slot="scope">
76 77 <p class="order-font">
77   - <el-tag size="mini">退款</el-tag>
78   - {{ scope.row.merchantRefundNo }}
  78 + <el-tag size="mini">商户</el-tag> {{scope.row.merchantRefundId}}
79 79 </p>
80 80 <p class="order-font">
81   - <el-tag type="success">交易</el-tag>
82   - {{ scope.row.merchantOrderId }}
  81 + <el-tag size="mini" type="warning">退款</el-tag> {{scope.row.no}}
  82 + </p>
  83 + <p class="order-font" v-if="scope.row.channelRefundNo">
  84 + <el-tag size="mini" type="success">渠道</el-tag> {{scope.row.channelRefundNo}}
83 85 </p>
84 86 </template>
85 87 </el-table-column>
86   - <el-table-column label="支付订单号" align="center" prop="merchantRefundNo" width="250">
  88 + <el-table-column label="支付订单号" align="left" width="300">
87 89 <template v-slot="scope">
88 90 <p class="order-font">
89   - <el-tag size="mini">交易</el-tag>
90   - {{ scope.row.tradeNo }}
  91 + <el-tag size="mini">商户</el-tag> {{scope.row.merchantOrderId}}
91 92 </p>
92 93 <p class="order-font">
93   - <el-tag size="mini" type="warning">渠道</el-tag>
94   - {{ scope.row.channelOrderNo }}
  94 + <el-tag size="mini" type="success">渠道</el-tag> {{scope.row.channelOrderNo}}
95 95 </p>
96 96 </template>
97 97 </el-table-column>
98   - <el-table-column label="支付金额(元)" align="center" prop="payPrice" width="100">
99   - <template v-slot="scope" class="">
100   - ¥{{ parseFloat(scope.row.payPrice / 100).toFixed(2) }}
101   - </template>
102   - </el-table-column>
103   - <el-table-column label="退款金额(元)" align="center" prop="refundPrice" width="100">
104   - <template v-slot="scope">
105   - ¥{{ parseFloat(scope.row.refundPrice / 100).toFixed(2) }}
106   - </template>
107   - </el-table-column>
108   - <el-table-column label="退款类型" align="center" prop="type" width="80">
109   - <template v-slot="scope">
110   - <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="scope.row.type" />
111   - </template>
112   - </el-table-column>
113 98 <el-table-column label="退款状态" align="center" prop="status">
114 99 <template v-slot="scope">
115   - <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="scope.row.status" />
  100 + <dict-tag :type="DICT_TYPE.PAY_REFUND_STATUS" :value="scope.row.status" />
116 101 </template>
117 102 </el-table-column>
118   - <el-table-column label="回调状态" align="center" prop="notifyStatus">
  103 + <el-table-column label="退款渠道" align="center" width="140">
119 104 <template v-slot="scope">
120   - <dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
  105 + <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="scope.row.channelCode" />
121 106 </template>
122 107 </el-table-column>
123   - <el-table-column label="退款原因" align="center" prop="reason" width="140" :show-overflow-tooltip="true"/>
124   - <el-table-column label="创建时间" align="center" prop="createTime" width="100">
  108 + <el-table-column label="成功时间" align="center" prop="successTime" width="180">
125 109 <template v-slot="scope">
126   - <span>{{ parseTime(scope.row.createTime) }}</span>
  110 + <span>{{ parseTime(scope.row.successTime) }}</span>
127 111 </template>
128 112 </el-table-column>
129   - <el-table-column label="退款成功时间" align="center" prop="successTime" width="100">
  113 + <el-table-column label="支付应用" align="center" prop="successTime" width="100">
130 114 <template v-slot="scope">
131   - <span>{{ parseTime(scope.row.successTime) }}</span>
  115 + <span>{{ scope.row.appName }}</span>
132 116 </template>
133 117 </el-table-column>
134 118 <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
... ... @@ -143,107 +127,76 @@
143 127 <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
144 128 @pagination="getList"/>
145 129  
146   - <!-- 对话框(添加 / 修改) -->
147   - <el-dialog title="退款订单详情" :visible.sync="open" width="700px" append-to-body>
  130 + <!-- 对话框(详情) -->
  131 + <el-dialog title="退款订单详情" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
148 132 <el-descriptions :column="2" label-class-name="desc-label">
149   - <el-descriptions-item label="应用名称">{{ refundDetail.appName }}</el-descriptions-item>
150   - <el-descriptions-item label="商品名称">{{ refundDetail.subject }}</el-descriptions-item>
  133 + <el-descriptions-item label="商户退款单号">
  134 + <el-tag size="small">{{ refundDetail.merchantRefundId }}</el-tag>
  135 + </el-descriptions-item>
  136 + <el-descriptions-item label="渠道退款单号">
  137 + <el-tag type="success" size="small" v-if="refundDetail.channelRefundNo">{{ refundDetail.channelRefundNo }}</el-tag>
  138 + </el-descriptions-item>
151 139 </el-descriptions>
152   - <el-divider></el-divider>
153 140 <el-descriptions :column="2" label-class-name="desc-label">
154   - <el-descriptions-item label="商户退款单号">
155   - <el-tag size="mini">{{ refundDetail.merchantRefundNo }}</el-tag>
  141 + <el-descriptions-item label="商户支付单号">
  142 + <el-tag size="small">{{ refundDetail.merchantOrderId }}</el-tag>
156 143 </el-descriptions-item>
157   - <el-descriptions-item label="商户订单号">{{ refundDetail.merchantOrderId }}</el-descriptions-item>
158   - <el-descriptions-item label="交易订单号">{{ refundDetail.tradeNo }}</el-descriptions-item>
  144 + <el-descriptions-item label="渠道支付单号">
  145 + <el-tag type="success" size="small">{{ refundDetail.channelOrderNo }}</el-tag>
  146 + </el-descriptions-item>
  147 + </el-descriptions>
  148 + <el-descriptions :column="2" label-class-name="desc-label">
  149 + <el-descriptions-item label="应用编号">{{ refundDetail.appId }}</el-descriptions-item>
  150 + <el-descriptions-item label="应用名称">{{ refundDetail.appName }}</el-descriptions-item>
159 151 </el-descriptions>
160   - <el-divider></el-divider>
161 152 <el-descriptions :column="2" label-class-name="desc-label">
162 153 <el-descriptions-item label="支付金额">
163   - {{ parseFloat(refundDetail.payPrice / 100).toFixed(2) }}
  154 + <el-tag type="success" size="small">¥{{ (refundDetail.payPrice / 100.0).toFixed(2) }}</el-tag>
164 155 </el-descriptions-item>
165   - <el-descriptions-item label="退款金额" size="mini">
166   - <el-tag class="tag-purple" size="mini">{{ parseFloat(refundDetail.refundPrice / 100).toFixed(2) }}</el-tag>
167   - </el-descriptions-item>
168   - <el-descriptions-item label="退款类型">
169   - <template v-slot="scope">
170   - <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="refundDetail.type" />
171   - </template>
  156 + <el-descriptions-item label="退款金额">
  157 + <el-tag size="mini" type="danger">¥{{ (refundDetail.refundPrice / 100.0).toFixed(2) }}</el-tag>
172 158 </el-descriptions-item>
  159 + </el-descriptions>
  160 + <el-descriptions :column="2" label-class-name="desc-label">
173 161 <el-descriptions-item label="退款状态">
174   - <dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="refundDetail.status" />
  162 + <dict-tag :type="DICT_TYPE.PAY_REFUND_STATUS" :value="refundDetail.status" />
175 163 </el-descriptions-item>
  164 + <el-descriptions-item label="退款时间">{{ parseTime(refundDetail.successTime) }}</el-descriptions-item>
  165 + </el-descriptions>
  166 + <el-descriptions :column="2" label-class-name="desc-label">
176 167 <el-descriptions-item label="创建时间">{{ parseTime(refundDetail.createTime) }}</el-descriptions-item>
177   - <el-descriptions-item label="退款成功时间">{{ parseTime(refundDetail.successTime) }}</el-descriptions-item>
178   - <el-descriptions-item label="退款失效时间">{{ parseTime(refundDetail.expireTime) }}</el-descriptions-item>
179 168 <el-descriptions-item label="更新时间">{{ parseTime(refundDetail.updateTime) }}</el-descriptions-item>
180 169 </el-descriptions>
181   - <el-divider></el-divider>
  170 + <!-- 分割线 -->
  171 + <el-divider />
182 172 <el-descriptions :column="2" label-class-name="desc-label">
183   - <el-descriptions-item label="支付渠道">
184   - {{ refundDetail.channelCodeName }}
185   - </el-descriptions-item>
186   - <el-descriptions-item label="支付IP" size="mini">
187   - {{refundDetail.userIp}}
188   - </el-descriptions-item>
189   - <el-descriptions-item label="回调地址">{{ refundDetail.notifyUrl }}</el-descriptions-item>
190   - <el-descriptions-item label="回调状态">
191   - <dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="refundDetail.notifyStatus" />
  173 + <el-descriptions-item label="退款渠道">
  174 + <dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="refundDetail.channelCode" />
192 175 </el-descriptions-item>
193   - <el-descriptions-item label="回调时间">{{ parseTime(refundDetail.notifyTime) }}</el-descriptions-item>
  176 + <el-descriptions-item label="退款原因">{{ refundDetail.reason }}</el-descriptions-item>
  177 + </el-descriptions>
  178 + <el-descriptions :column="2" label-class-name="desc-label">
  179 + <el-descriptions-item label="退款 IP">{{ refundDetail.userIp }}</el-descriptions-item>
  180 + <el-descriptions-item label="通知 URL">{{ refundDetail.notifyUrl }}</el-descriptions-item>
194 181 </el-descriptions>
195   - <el-divider></el-divider>
  182 + <!-- 分割线 -->
  183 + <el-divider />
196 184 <el-descriptions :column="2" label-class-name="desc-label">
197   - <el-descriptions-item label="渠道订单号">{{ refundDetail.channelOrderNo }}</el-descriptions-item>
198   - <el-descriptions-item label="渠道退款单号">{{ refundDetail.channelRefundNo }}</el-descriptions-item>
199 185 <el-descriptions-item label="渠道错误码">{{refundDetail.channelErrorCode}}</el-descriptions-item>
200 186 <el-descriptions-item label="渠道错误码描述">{{refundDetail.channelErrorMsg}}</el-descriptions-item>
201 187 </el-descriptions>
202   - <br>
203 188 <el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
204   - <el-descriptions-item label="渠道额外参数">{{ refundDetail.channelExtras }}</el-descriptions-item>
205   - <el-descriptions-item label="退款原因">{{ refundDetail.reason }}</el-descriptions-item>
  189 + <el-descriptions-item label="支付通道异步回调内容">
  190 + {{ refundDetail.channelNotifyData }}
  191 + </el-descriptions-item>
206 192 </el-descriptions>
207 193 </el-dialog>
208 194 </div>
209 195 </template>
210 196  
211 197 <script>
212   -import {getRefundPage, exportRefundExcel, getRefund} from "@/api/pay/refund";
213   -import { DICT_TYPE, getDictDatas } from "@/utils/dict";
214   -import { PayOrderRefundStatusEnum, PayRefundStatusEnum } from "@/utils/constants";
215   -import { getNowDateTime } from "@/utils/ruoyi";
216   -
217   -const defaultRefundDetail = {
218   - id: null,
219   - appId: null,
220   - appName: '',
221   - channelCode: '',
222   - channelCodeName: '',
223   - channelErrorCode: '',
224   - channelErrorMsg: '',
225   - channelExtras: '',
226   - channelId: null,
227   - channelOrderNo: '',
228   - channelRefundNo: '',
229   - createTime: null,
230   - expireTime: null,
231   - merchantOrderId: '',
232   - merchantRefundNo: '',
233   - notifyStatus: null,
234   - notifyTime: null,
235   - notifyUrl: '',
236   - orderId: null,
237   - payPrice: null,
238   - reason: '',
239   - refundPrice: null,
240   - status: null,
241   - subject: '',
242   - successTime: null,
243   - tradeNo: '',
244   - type: null,
245   - userIp: ''
246   -}
  198 +import { getRefundPage, exportRefundExcel, getRefund } from "@/api/pay/refund";
  199 +import { getAppList } from "@/api/pay/app";
247 200  
248 201 export default {
249 202 name: "PayRefund",
... ... @@ -267,73 +220,30 @@ export default {
267 220 pageNo: 1,
268 221 pageSize: 10,
269 222 appId: null,
270   - channelId: null,
271 223 channelCode: null,
272   - orderId: null,
273   - tradeNo: null,
274 224 merchantOrderId: null,
275   - merchantRefundNo: null,
276   - notifyUrl: null,
277   - notifyStatus: null,
278   - status: null,
279   - type: null,
280   - payPrice: null,
281   - refundPrice: null,
282   - reason: null,
283   - userIp: null,
  225 + merchantRefundId: null,
284 226 channelOrderNo: null,
285 227 channelRefundNo: null,
286   - channelErrorCode: null,
287   - channelErrorMsg: null,
288   - channelExtras: null,
289   - expireTime: [],
290   - successTime: [],
291   - notifyTime: [],
  228 + status: null,
292 229 createTime: []
293 230 },
294   - // 商户加载遮罩层
295   - merchantLoading: false,
296   - // 商户列表集合
297   - merchantList: null,
298 231 // 支付应用列表集合
299   - appList: null,
300   - // 支付渠道编码字典数据集合
301   - payChannelCodeDictDatum: getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE),
302   - // 订单退款状态字典数据集合
303   - payRefundOrderDictDatum: getDictDatas(DICT_TYPE.PAY_REFUND_ORDER_STATUS),
304   - // 退款订单类别字典数据集合
305   - payRefundOrderTypeDictDatum: getDictDatas(DICT_TYPE.PAY_REFUND_ORDER_TYPE),
306   - // 订单回调商户状态字典数据集合
307   - payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
308   - // el-tag订单退款状态type值
309   - refundStatusType: '',
  232 + appList: [],
310 233 // 退款订单详情
311   - refundDetail: JSON.parse(JSON.stringify(defaultRefundDetail)),
  234 + refundDetail: {},
312 235 };
313 236 },
314 237 created() {
315   - this.initTime();
316 238 this.getList();
317   - this.handleGetMerchantListByName(null);
  239 + // 获得筛选项
  240 + getAppList().then(response => {
  241 + this.appList = response.data;
  242 + })
318 243 },
319 244 methods: {
320   - initTime(){
321   - this.queryParams.createTime = [getNowDateTime("00:00:00"), getNowDateTime("23:59:59")];
322   - },
323 245 /** 查询列表 */
324 246 getList() {
325   - // 判断选择的日期是否超过了一个月
326   - let oneMonthTime = 31 * 24 * 3600 * 1000;
327   - if (this.queryParams.createTime == null){
328   - this.initTime();
329   - } else {
330   - let minDateTime = new Date(this.queryParams.createTime[0]).getTime();
331   - let maxDateTime = new Date(this.queryParams.createTime[1]).getTime()
332   - if (maxDateTime - minDateTime > oneMonthTime) {
333   - this.$message.error('时间范围最大为 31 天!');
334   - return false;
335   - }
336   - }
337 247 this.loading = true;
338 248 // 执行查询
339 249 getRefundPage(this.queryParams).then(response => {
... ... @@ -369,60 +279,13 @@ export default {
369 279 this.$download.excel(response, '退款订单.xls');
370 280 }).catch(() => {});
371 281 },
372   - /**
373   - * 根据商户名称模糊匹配商户信息
374   - * @param name 商户名称
375   - */
376   - handleGetMerchantListByName(name) {
377   - getMerchantListByName(name).then(response => {
378   - this.merchantList = response.data;
379   - this.merchantLoading = false;
380   - });
381   - },
382   - /**
383   - * 根据退款类别得到样式名称
384   - * @param refundType 退款类别
385   - */
386   - findByRefundTypeGetStyle(refundType) {
387   - switch (refundType) {
388   - case PayOrderRefundStatusEnum.NO.status:
389   - return "success";
390   - case PayOrderRefundStatusEnum.SOME.status:
391   - return "warning";
392   - case PayOrderRefundStatusEnum.ALL.status:
393   - return "danger";
394   - }
395   - },
396   - /**
397   - * 根据退款状态得到样式名称
398   - * @param refundStatus 退款状态
399   - */
400   - findByRefundStatusGetStyle(refundStatus) {
401   - switch (refundStatus) {
402   - case PayRefundStatusEnum.CREATE.status:
403   - return "info";
404   - case PayRefundStatusEnum.SUCCESS.status:
405   - return "success";
406   - case PayRefundStatusEnum.FAILURE.status:
407   - case PayRefundStatusEnum.CLOSE.status:
408   - return "danger";
409   - case PayRefundStatusEnum.PROCESSING_NOTIFY.status:
410   - case PayRefundStatusEnum.PROCESSING_QUERY.status:
411   - case PayRefundStatusEnum.UNKNOWN_RETRY.status:
412   - case PayRefundStatusEnum.UNKNOWN_QUERY.status:
413   - return "warning";
414   - }
415   - },
416   - /**
417   - * 查看订单详情
418   - */
  282 + /** 详情按钮操作 */
419 283 handleQueryDetails(row) {
420   - this.refundDetail = JSON.parse(JSON.stringify(defaultRefundDetail));
  284 + this.refundDetail = {};
421 285 getRefund(row.id).then(response => {
422 286 this.refundDetail = response.data;
423 287 this.open = true;
424 288 });
425   -
426 289 },
427 290 }
428 291 };
... ...