diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java index 704dab307..e57f4ac70 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java +++ b/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; * @author 芋道源码 */ @AutoConfiguration -@EnableConfigurationProperties(PayProperties.class) public class YudaoPayAutoConfiguration { @Bean diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java index 928830f66..163ffce65 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java +++ b/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 { /** * 创建【WAITING】状态的订单返回 */ - public PayOrderRespDTO(String displayMode, String displayContent, - String outTradeNo, Object rawData) { - this.status = PayOrderStatusRespEnum.WAITING.getStatus(); - this.displayMode = displayMode; - this.displayContent = displayContent; + public static PayOrderRespDTO waitingOf(String displayMode, String displayContent, + String outTradeNo, Object rawData) { + PayOrderRespDTO respDTO = new PayOrderRespDTO(); + respDTO.status = PayOrderStatusRespEnum.WAITING.getStatus(); + respDTO.displayMode = displayMode; + respDTO.displayContent = displayContent; // 相对通用的字段 - this.outTradeNo = outTradeNo; - this.rawData = rawData; + respDTO.outTradeNo = outTradeNo; + respDTO.rawData = rawData; + return respDTO; } /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java index 6d10dc9a0..3184f278d 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.pay.core.client.dto.refund; +import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; import lombok.Data; @@ -44,4 +45,71 @@ public class PayRefundRespDTO { */ private Object rawData; + /** + * 调用渠道的错误码 + * + * 注意:这里返回的是业务异常,而是不系统异常。 + * 如果是系统异常,则会抛出 {@link PayException} + */ + private String channelErrorCode; + /** + * 调用渠道报错时,错误信息 + */ + private String channelErrorMsg; + + private PayRefundRespDTO() { + } + + /** + * 创建【WAITING】状态的退款返回 + */ + public static PayRefundRespDTO waitingOf(String channelRefundNo, + String outRefundNo, Object rawData) { + PayRefundRespDTO respDTO = new PayRefundRespDTO(); + respDTO.status = PayRefundStatusRespEnum.WAITING.getStatus(); + respDTO.channelRefundNo = channelRefundNo; + // 相对通用的字段 + respDTO.outRefundNo = outRefundNo; + respDTO.rawData = rawData; + return respDTO; + } + + /** + * 创建【SUCCESS】状态的退款返回 + */ + public static PayRefundRespDTO successOf(String channelRefundNo, LocalDateTime successTime, + String outRefundNo, Object rawData) { + PayRefundRespDTO respDTO = new PayRefundRespDTO(); + respDTO.status = PayRefundStatusRespEnum.SUCCESS.getStatus(); + respDTO.channelRefundNo = channelRefundNo; + respDTO.successTime = successTime; + // 相对通用的字段 + respDTO.outRefundNo = outRefundNo; + respDTO.rawData = rawData; + return respDTO; + } + + /** + * 创建【FAILURE】状态的退款返回 + */ + public static PayRefundRespDTO failureOf(String outRefundNo, Object rawData) { + return failureOf(null, null, + outRefundNo, rawData); + } + + /** + * 创建【FAILURE】状态的退款返回 + */ + public static PayRefundRespDTO failureOf(String channelErrorCode, String channelErrorMsg, + String outRefundNo, Object rawData) { + PayRefundRespDTO respDTO = new PayRefundRespDTO(); + respDTO.status = PayRefundStatusRespEnum.FAILURE.getStatus(); + respDTO.channelErrorCode = channelErrorCode; + respDTO.channelErrorMsg = channelErrorMsg; + // 相对通用的字段 + respDTO.outRefundNo = outRefundNo; + respDTO.rawData = rawData; + return respDTO; + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java index 8be5aa498..194432738 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java +++ b/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 implemen try { return doParseOrderNotify(params, body); } catch (Throwable ex) { - log.error("[parseOrderNotify][params({}) body({}) 解析失败]", params, body, ex); + log.error("[parseOrderNotify][客户端({}) params({}) body({}) 解析失败]", + getId(), params, body, ex); throw buildPayException(ex); } } @@ -129,6 +130,20 @@ public abstract class AbstractPayClient implemen protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; + @Override + public PayRefundRespDTO parseRefundNotify(Map params, String body) { + try { + return doParseRefundNotify(params, body); + } catch (Throwable ex) { + log.error("[parseRefundNotify][客户端({}) params({}) body({}) 解析失败]", + getId(), params, body, ex); + throw buildPayException(ex); + } + } + + protected abstract PayRefundRespDTO doParseRefundNotify(Map params, String body) + throws Throwable; + // ========== 各种工具方法 ========== private PayException buildPayException(Throwable ex) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java index f11c88ec0..8c4a66b11 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java +++ b/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; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; @@ -29,7 +31,6 @@ import java.util.Objects; import java.util.function.Supplier; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款) @@ -74,10 +75,15 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient 0) { + status = PayOrderStatusRespEnum.REFUND.getStatus(); + } Assert.notNull(status, (Supplier) () -> { throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body)); }); @@ -94,7 +100,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient params, String body) { + public PayRefundRespDTO doParseRefundNotify(Map params, String body) { // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。 // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调 // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java index 926094af2..d949dd4b7 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java +++ b/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 { if (!response.isSuccess()) { return buildClosedPayOrderRespDTO(reqDTO, response); } - return new PayOrderRespDTO(displayMode, "", + return PayOrderRespDTO.waitingOf(displayMode, "", reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java index 36155f004..e4e644d26 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java +++ b/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 { if (!response.isSuccess()) { return buildClosedPayOrderRespDTO(reqDTO, response); } - return new PayOrderRespDTO(displayMode, "", + return PayOrderRespDTO.waitingOf(displayMode, "", reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java index 692f30633..5fdff2671 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java +++ b/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 { if (!response.isSuccess()) { return buildClosedPayOrderRespDTO(reqDTO, response); } - return new PayOrderRespDTO(displayMode, response.getBody(), + return PayOrderRespDTO.waitingOf(displayMode, response.getBody(), reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java index e68b74156..57393e972 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java +++ b/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 { if (!response.isSuccess()) { return buildClosedPayOrderRespDTO(reqDTO, response); } - return new PayOrderRespDTO(displayMode, response.getQrCode(), + return PayOrderRespDTO.waitingOf(displayMode, response.getQrCode(), reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java index 85ab79303..8ecb63447 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java +++ b/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 { if (!response.isSuccess()) { return buildClosedPayOrderRespDTO(reqDTO, response); } - return new PayOrderRespDTO(displayMode, response.getBody(), + return PayOrderRespDTO.waitingOf(displayMode, response.getBody(), reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index 05534d0b6..eeef301c0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/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; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; @@ -131,6 +130,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient params, String body) { - try { - // 微信支付 v2 回调结果处理 - switch (config.getApiVersion()) { - case API_VERSION_V2: - return parseRefundNotifyV2(body); - case WxPayClientConfig.API_VERSION_V3: - return parseRefundNotifyV3(body); - default: - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); - } - } catch (WxPayException e) { - log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e); - throw new RuntimeException(e); - // TODO 芋艿:缺一个异常翻译 + public PayRefundRespDTO doParseRefundNotify(Map params, String body) throws WxPayException { + switch (config.getApiVersion()) { + case API_VERSION_V2: + return doParseRefundNotifyV2(body); + case WxPayClientConfig.API_VERSION_V3: + return parseRefundNotifyV3(body); + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); } } - @SuppressWarnings("DuplicatedCode") - private PayRefundRespDTO parseRefundNotifyV2(String body) throws WxPayException { + private PayRefundRespDTO doParseRefundNotifyV2(String body) throws WxPayException { // 1. 解析回调 WxPayRefundNotifyResult response = client.parseRefundNotifyResult(body); - WxPayRefundNotifyResult.ReqInfo responseResult = response.getReqInfo(); + WxPayRefundNotifyResult.ReqInfo result = response.getReqInfo(); // 2. 构建结果 - PayRefundRespDTO notify = new PayRefundRespDTO() - .setChannelRefundNo(responseResult.getRefundId()) - .setOutRefundNo(responseResult.getOutRefundNo()) - .setRawData(response); - if (Objects.equals("SUCCESS", responseResult.getRefundStatus())) { - notify.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()) - .setSuccessTime(parseDateV2B(responseResult.getSuccessTime())); - } else { - notify.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()); + if (Objects.equals("SUCCESS", result.getRefundStatus())) { + return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV2B(result.getSuccessTime()), + result.getOutRefundNo(), response); } - return notify; + return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response); } - @SuppressWarnings("DuplicatedCode") private PayRefundRespDTO parseRefundNotifyV3(String body) throws WxPayException { // 1. 解析回调 WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, null); - WxPayRefundNotifyV3Result.DecryptNotifyResult responseResult = response.getResult(); + WxPayRefundNotifyV3Result.DecryptNotifyResult result = response.getResult(); // 2. 构建结果 - PayRefundRespDTO notify = new PayRefundRespDTO() - .setChannelRefundNo(responseResult.getRefundId()) - .setOutRefundNo(responseResult.getOutRefundNo()) - .setRawData(response); - if (Objects.equals("SUCCESS", responseResult.getRefundStatus())) { - notify.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()) - .setSuccessTime(parseDateV3(responseResult.getSuccessTime())); - } else { - notify.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()); + if (Objects.equals("SUCCESS", result.getRefundStatus())) { + return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV3(result.getSuccessTime()), + result.getOutRefundNo(), response); } - return notify; + return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response); } // ========== 各种工具方法 ========== diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java index 480c23923..77de2ab64 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java +++ b/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 { for (int i = 1; i < Byte.MAX_VALUE; i++) { try { WxPayMicropayResult response = client.micropay(request); - // 支付成功,例如说:1)用户输入了密码;2) + // 支付成功,例如说:1)用户输入了密码;2)用户免密支付 return new PayOrderRespDTO(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()), response.getOutTradeNo(), response) .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode()); diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java index ab37bb4e8..d7660476d 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java +++ b/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 { WxPayNativeOrderResult response = client.createOrder(request); // 转换结果 - return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(), + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(), reqDTO.getOutTradeNo(), response); } @@ -66,7 +66,7 @@ public class WxNativePayClient extends AbstractWxPayClient { String response = client.createOrderV3(TradeTypeEnum.NATIVE, request); // 转换结果 - return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response, + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), response, reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java index db35f10ac..f88899c5c 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java +++ b/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 { WxPayMpOrderResult response = client.createOrder(request); // 转换结果 - return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response), + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response), reqDTO.getOutTradeNo(), response); } @@ -77,7 +77,7 @@ public class WxPubPayClient extends AbstractWxPayClient { WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request); // 转换结果 - return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response), + return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response), reqDTO.getOutTradeNo(), response); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java index 6cfcb5236..0969a2953 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java +++ b/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 { WAITING(0, "未支付"), SUCCESS(10, "支付成功"), - CLOSED(20, "支付关闭"), // 未付款交易超时关闭,或支付完成后全额退款 + REFUND(20, "已退款"), + CLOSED(30, "支付关闭"), ; private final Integer status; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java index 74b2f8d7d..8ad61a6cf 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java +++ b/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; @AllArgsConstructor public enum PayRefundStatusRespEnum { - WAITING(0, "未退款"), + WAITING(0, "等待退款"), SUCCESS(10, "退款成功"), FAILURE(20, "退款失败"); @@ -25,4 +25,8 @@ public enum PayRefundStatusRespEnum { return Objects.equals(status, SUCCESS.getStatus()); } + public static boolean isFailure(Integer status) { + return Objects.equals(status, FAILURE.getStatus()); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java new file mode 100644 index 000000000..ee66fe7de --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.framework.excel.core.convert; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 金额转换器 + * + * 金额单位:分 + * + * @author 芋道源码 + */ +public class MoneyConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + throw new UnsupportedOperationException("暂不支持,也不需要"); + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + throw new UnsupportedOperationException("暂不支持,也不需要"); + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + BigDecimal result = BigDecimal.valueOf(value) + .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP); + return new WriteCellData<>(result.toString()); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java index 779803059..fbe6acfb6 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java +++ b/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 { } return RandomUtil.randomInt(); }); + // LocalDateTime + PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(LocalDateTime.class, + (dataProviderStrategy, attributeMetadata, map) -> randomLocalDateTime()); // Boolean PODAM_FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> { // 如果是 deleted 的字段,返回非删除 @@ -82,7 +85,8 @@ public class RandomUtils { } public static LocalDateTime randomLocalDateTime() { - return LocalDateTimeUtil.of(randomDate()); + // 设置 Nano 为零的原因,避免 MySQL、H2 存储不到时间戳 + return LocalDateTimeUtil.of(randomDate()).withNano(0); } public static Short randomShort() { diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java index 4b5c0b899..934b452fb 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java +++ b/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. import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; /** @@ -344,7 +345,7 @@ public class TradeOrderServiceImpl implements TradeOrderService { PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId); if (payOrder == null) { log.error("[validateOrderPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId); - throw exception(PAY_ORDER_NOT_FOUND); + throw exception(cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.ORDER_NOT_FOUND); } // 校验支付单已支付 if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/DictTypeConstants.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/DictTypeConstants.java index cfe92bbac..8f0d9b718 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/DictTypeConstants.java +++ b/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; */ public interface DictTypeConstants { - String CHANNEL_CODE = "pay_channel_code"; // 支付-渠道名 + String CHANNEL_CODE = "pay_channel_code"; // 支付渠道编码 - String ORDER_STATUS = "pay_order_status"; // 支付-订单-订单状态 - String ORDER_NOTIFY_STATUS = "pay_order_notify_status"; // 支付-订单-订单回调商户状态 + String ORDER_STATUS = "pay_order_status"; // 支付渠道 - String ORDER_REFUND_STATUS = "pay_order_refund_status"; // 支付-订单-订单退款状态 - String REFUND_ORDER_STATUS = "pay_refund_order_status"; // 支付-退款订单-退款状态 - String REFUND_ORDER_TYPE = "pay_refund_order_type"; // 支付-退款订单-退款类别 + String REFUND_STATUS = "pay_order_status"; // 退款状态 + + String NOTIFY_STATUS = "pay_notify_status"; // 回调状态 } diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java index 16174285f..7cc1b815c 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java +++ b/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; public interface ErrorCodeConstants { // ========== APP 模块 1007000000 ========== - ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在"); - ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用"); - ErrorCode PAY_APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1007000003, "支付应用存在支付订单,无法删除"); - ErrorCode PAY_APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1007000004, "支付应用存在退款订单,无法删除"); + ErrorCode APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在"); + ErrorCode APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用"); + ErrorCode APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1007000003, "支付应用存在支付订单,无法删除"); + ErrorCode APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1007000004, "支付应用存在退款订单,无法删除"); // ========== CHANNEL 模块 1007001000 ========== - ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在"); - ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用"); - ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在"); - ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在"); - ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道"); + ErrorCode CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在"); + ErrorCode CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用"); + ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001004, "已存在相同的渠道"); // ========== ORDER 模块 1007002000 ========== - ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在"); - ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); - ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); - ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期"); - ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}"); + ErrorCode ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在"); + ErrorCode ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); + ErrorCode ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); + ErrorCode ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期"); + ErrorCode ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}"); + ErrorCode ORDER_REFUND_FAIL_STATUS_ERROR = new ErrorCode(1007002005, "支付订单退款失败,原因:状态不是已支付或已退款"); // ========== ORDER 模块(拓展单) 1007003000 ========== - ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); - ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付"); + ErrorCode ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); + ErrorCode ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付"); // ========== 支付模块(退款) 1007006000 ========== - ErrorCode PAY_REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额"); - ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款"); - ErrorCode PAY_REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中"); - ErrorCode PAY_REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单"); - ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); - ErrorCode PAY_REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款"); + ErrorCode REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额"); + ErrorCode REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中"); + ErrorCode REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单"); + ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); + ErrorCode REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款"); // ========== 示例订单 1007900000 ========== - ErrorCode PAY_DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态"); - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1007900002, "示例订单更新支付状态失败,支付单编号不匹配"); - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007900003, "示例订单更新支付状态失败,支付单状态不是【支付成功】状态"); - ErrorCode PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1007900004, "示例订单更新支付状态失败,支付单金额不匹配"); - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_NOT_PAID = new ErrorCode(1007900005, "发起退款失败,示例订单未支付"); - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUNDED = new ErrorCode(1007900006, "发起退款失败,示例订单已退款"); - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007900007, "发起退款失败,退款订单不存在"); - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS = new ErrorCode(1007900008, "发起退款失败,退款订单未退款成功"); - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007900009, "发起退款失败,退款单编号不匹配"); - ErrorCode PAY_DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007900010, "发起退款失败,退款单金额不匹配"); + ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); + ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态"); + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR = new ErrorCode(1007900002, "示例订单更新支付状态失败,支付单编号不匹配"); + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007900003, "示例订单更新支付状态失败,支付单状态不是【支付成功】状态"); + ErrorCode DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1007900004, "示例订单更新支付状态失败,支付单金额不匹配"); + ErrorCode DEMO_ORDER_REFUND_FAIL_NOT_PAID = new ErrorCode(1007900005, "发起退款失败,示例订单未支付"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUNDED = new ErrorCode(1007900006, "发起退款失败,示例订单已退款"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007900007, "发起退款失败,退款订单不存在"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS = new ErrorCode(1007900008, "发起退款失败,退款订单未退款成功"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007900009, "发起退款失败,退款单编号不匹配"); + ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007900010, "发起退款失败,退款单金额不匹配"); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java similarity index 61% rename from yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java rename to yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java index d1b7c1015..b735598d5 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyStatusEnum.java +++ b/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; @AllArgsConstructor public enum PayNotifyStatusEnum { - WAITING(1, "等待通知"), - SUCCESS(2, "通知成功"), - FAILURE(3, "通知失败"), // 多次尝试,彻底失败 - REQUEST_SUCCESS(4, "请求成功,但是结果失败"), - REQUEST_FAILURE(5, "请求失败"), + WAITING(0, "等待通知"), + SUCCESS(10, "通知成功"), + FAILURE(20, "通知失败"), // 多次尝试,彻底失败 + REQUEST_SUCCESS(21, "请求成功,但是结果失败"), + REQUEST_FAILURE(22, "请求失败"), ; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java similarity index 100% rename from yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java rename to yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java index 2ee45a1f4..86a9e1704 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.pay.enums.order; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import lombok.AllArgsConstructor; import lombok.Getter; @@ -17,7 +18,8 @@ public enum PayOrderStatusEnum implements IntArrayValuable { WAITING(0, "未支付"), SUCCESS(10, "支付成功"), - CLOSED(20, "支付关闭"), // 未付款交易超时关闭,或支付完成后全额退款 + REFUND(20, "已退款"), + CLOSED(30, "支付关闭"), // 注意:全部退款后,还是 REFUND 状态 ; private final Integer status; @@ -38,6 +40,17 @@ public enum PayOrderStatusEnum implements IntArrayValuable { return Objects.equals(status, SUCCESS.getStatus()); } + /** + * 判断是否支付成功或者已退款 + * + * @param status 状态 + * @return 是否支付成功或者已退款 + */ + public static boolean isSuccessOrRefund(Integer status) { + return ObjectUtils.equalsAny(status, + SUCCESS.getStatus(), REFUND.getStatus()); + } + /** * 判断是否支付关闭 * diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java index 29fdc923b..4ae14cc67 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java +++ b/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 { return Objects.equals(status, SUCCESS.getStatus()); } + public static boolean isFailure(Integer status) { + return Objects.equals(status, FAILURE.getStatus()); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/PayAppController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/PayAppController.java index 562b2e4d4..b0d80eac3 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/PayAppController.java +++ b/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 { return success(PayAppConvert.INSTANCE.convertPage(pageResult, channels)); } + @GetMapping("/list") + @Operation(summary = "获得应用列表") + @PreAuthorize("@ss.hasPermission('pay:merchant:query')") + public CommonResult> getAppList() { + List appListDO = appService.getAppList(); + return success(PayAppConvert.INSTANCE.convertList(appListDO)); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java index e37ddfc11..9db3bba30 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java @@ -1,26 +1,43 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; +import cn.iocoder.yudao.module.pay.convert.notify.PayNotifyTaskConvert; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; +import cn.iocoder.yudao.module.pay.service.app.PayAppService; +import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.annotation.security.PermitAll; +import javax.validation.Valid; +import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_FOUND; -@Tag(name = "管理后台 - 支付通知") +@Tag(name = "管理后台 - 回调通知") @RestController @RequestMapping("/pay/notify") @Validated @@ -31,6 +48,10 @@ public class PayNotifyController { private PayOrderService orderService; @Resource private PayRefundService refundService; + @Resource + private PayNotifyService notifyService; + @Resource + private PayAppService appService; @Resource private PayClientFactory payClientFactory; @@ -47,7 +68,7 @@ public class PayNotifyController { PayClient payClient = payClientFactory.getPayClient(channelId); if (payClient == null) { log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId); - throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); + throw exception(CHANNEL_NOT_FOUND); } // 2. 解析通知数据 @@ -68,7 +89,7 @@ public class PayNotifyController { PayClient payClient = payClientFactory.getPayClient(channelId); if (payClient == null) { log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId); - throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); + throw exception(CHANNEL_NOT_FOUND); } // 2. 解析通知数据 @@ -77,4 +98,29 @@ public class PayNotifyController { return "success"; } + @GetMapping("/get-detail") + @Operation(summary = "获得回调通知的明细") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('pay:notify:query')") + public CommonResult getNotifyTaskDetail(@RequestParam("id") Long id) { + PayNotifyTaskDO task = notifyService.getNotifyTask(id); + if (task == null) { + return success(null); + } + // 拼接返回 + PayAppDO app = appService.getApp(task.getAppId()); + List logs = notifyService.getNotifyLogList(id); + return success(PayNotifyTaskConvert.INSTANCE.convert(task, app, logs)); + } + + @GetMapping("/page") + @Operation(summary = "获得回调通知分页") + @PreAuthorize("@ss.hasPermission('pay:notify:query')") + public CommonResult> getNotifyTaskPage(@Valid PayNotifyTaskPageReqVO pageVO) { + PageResult pageResult = notifyService.getNotifyTaskPage(pageVO); + // 拼接返回 + Map appMap = appService.getAppMap(convertList(pageResult.getList(), PayNotifyTaskDO::getAppId)); + return success(PayNotifyTaskConvert.INSTANCE.convertPage(pageResult, appMap)); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java new file mode 100644 index 000000000..1e623751b --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 回调通知 Base VO,提供给添加、修改、详细的子 VO 使用 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 + */ +@Data +public class PayNotifyTaskBaseVO { + + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10636") + private Long appId; + + @Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Byte type; + + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6722") + private Long dataId; + + @Schema(description = "通知状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Byte status; + + @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26697") + private String merchantOrderId; + + @Schema(description = "下一次通知时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime nextNotifyTime; + + @Schema(description = "最后一次执行时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime lastExecuteTime; + + @Schema(description = "当前通知次数", requiredMode = Schema.RequiredMode.REQUIRED) + private Byte notifyTimes; + + @Schema(description = "最大可通知次数", requiredMode = Schema.RequiredMode.REQUIRED) + private Byte maxNotifyTimes; + + @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + private String notifyUrl; + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java new file mode 100644 index 000000000..7c75613e2 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java @@ -0,0 +1,54 @@ + +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 回调通知的明细 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayNotifyTaskDetailRespVO extends PayNotifyTaskBaseVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "应用名称", example = "wx_pay") + private String appName; + + @Schema(description = "回调日志列表") + private List logs; + + @Schema(description = "管理后台 - 回调日志") + @Data + public static class Log { + + @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8848") + private Long id; + + @Schema(description = "通知状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Byte status; + + @Schema(description = "当前通知次数", requiredMode = Schema.RequiredMode.REQUIRED) + private Byte notifyTimes; + + @Schema(description = "HTTP 响应结果", requiredMode = Schema.RequiredMode.REQUIRED) + private String response; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + } + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java new file mode 100644 index 000000000..5697d0d87 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 回调通知分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayNotifyTaskPageReqVO extends PageParam { + + @Schema(description = "应用编号", example = "10636") + private Long appId; + + @Schema(description = "通知类型", example = "2") + private Byte type; + + @Schema(description = "数据编号", example = "6722") + private Long dataId; + + @Schema(description = "通知状态", example = "1") + private Byte status; + + @Schema(description = "商户订单编号", example = "26697") + private String merchantOrderId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java new file mode 100644 index 000000000..d7f7fe6fb --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.pay.controller.admin.notify.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 回调通知 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayNotifyTaskRespVO extends PayNotifyTaskBaseVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380") + private Long id; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "应用名称", example = "wx_pay") + private String appName; + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java index 6acc41ceb..9a2afbe66 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java @@ -1,20 +1,16 @@ package cn.iocoder.yudao.module.pay.controller.admin.order; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.module.pay.service.app.PayAppService; -import cn.iocoder.yudao.module.pay.service.order.PayOrderExtensionService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -32,6 +28,7 @@ import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @@ -42,9 +39,7 @@ import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.E public class PayOrderController { @Resource - private PayOrderService payOrderService; - @Resource - private PayOrderExtensionService orderExtensionService; + private PayOrderService orderService; @Resource private PayAppService appService; @@ -53,40 +48,29 @@ public class PayOrderController { @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('pay:order:query')") public CommonResult getOrder(@RequestParam("id") Long id) { - return success(PayOrderConvert.INSTANCE.convert(payOrderService.getOrder(id))); + return success(PayOrderConvert.INSTANCE.convert(orderService.getOrder(id))); } - // TODO 芋艿:看看怎么优化下; @GetMapping("/get-detail") @Operation(summary = "获得支付订单详情") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('pay:order:query')") public CommonResult getOrderDetail(@RequestParam("id") Long id) { - PayOrderDO order = payOrderService.getOrder(id); - if (ObjectUtil.isNull(order)) { - return success(new PayOrderDetailsRespVO()); + PayOrderDO order = orderService.getOrder(id); + if (order == null) { + return success(null); } - PayAppDO appDO = appService.getApp(order.getAppId()); - PayChannelEnum channelEnum = PayChannelEnum.getByCode(order.getChannelCode()); - - // TODO @aquan:文案,都是前端 format; - PayOrderDetailsRespVO respVO = PayOrderConvert.INSTANCE.orderDetailConvert(order); - respVO.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用"); - respVO.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道"); - - PayOrderExtensionDO extensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); - if (ObjectUtil.isNotNull(extensionDO)) { - respVO.setPayOrderExtension(PayOrderConvert.INSTANCE.orderDetailExtensionConvert(extensionDO)); - } - - return success(respVO); + // 拼接返回 + PayAppDO app = appService.getApp(order.getAppId()); + PayOrderExtensionDO orderExtension = orderService.getOrderExtension(order.getExtensionId()); + return success(PayOrderConvert.INSTANCE.convert(order, orderExtension, app)); } @PostMapping("/submit") @Operation(summary = "提交支付订单") public CommonResult submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { - PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); + PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP()); return success(respVO); } @@ -94,62 +78,34 @@ public class PayOrderController { @Operation(summary = "获得支付订单分页") @PreAuthorize("@ss.hasPermission('pay:order:query')") public CommonResult> getOrderPage(@Valid PayOrderPageReqVO pageVO) { - PageResult pageResult = payOrderService.getOrderPage(pageVO); + PageResult pageResult = orderService.getOrderPage(pageVO); if (CollectionUtil.isEmpty(pageResult.getList())) { return success(new PageResult<>(pageResult.getTotal())); } - // 处理应用ID数据 - Map appMap = appService.getAppMap( - CollectionUtils.convertList(pageResult.getList(), PayOrderDO::getAppId)); - - List pageList = new ArrayList<>(pageResult.getList().size()); - pageResult.getList().forEach(c -> { - PayAppDO appDO = appMap.get(c.getAppId()); - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode()); - - PayOrderPageItemRespVO orderItem = PayOrderConvert.INSTANCE.pageConvertItemPage(c); - orderItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用"); - orderItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道"); - pageList.add(orderItem); - }); - return success(new PageResult<>(pageList, pageResult.getTotal())); + // 拼接返回 + Map appMap = appService.getAppMap(convertList(pageResult.getList(), PayOrderDO::getAppId)); + return success(PayOrderConvert.INSTANCE.convertPage(pageResult, appMap)); } @GetMapping("/export-excel") - @Operation(summary = "导出支付订单Excel") + @Operation(summary = "导出支付订单 Excel") @PreAuthorize("@ss.hasPermission('pay:order:export')") @OperateLog(type = EXPORT) public void exportOrderExcel(@Valid PayOrderExportReqVO exportReqVO, HttpServletResponse response) throws IOException { - List list = payOrderService.getOrderList(exportReqVO); + List list = orderService.getOrderList(exportReqVO); if (CollectionUtil.isEmpty(list)) { ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, new ArrayList<>()); + return; } - // 处理应用ID数据 - Map appMap = appService.getAppMap( - CollectionUtils.convertList(list, PayOrderDO::getAppId)); - // 处理扩展订单数据 - Map orderExtensionMap = orderExtensionService - .getOrderExtensionMap(CollectionUtils.convertList(list, PayOrderDO::getSuccessExtensionId)); - - List excelDatum = new ArrayList<>(list.size()); - list.forEach(c -> { - PayAppDO appDO = appMap.get(c.getAppId()); - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode()); - PayOrderExtensionDO orderExtensionDO = orderExtensionMap.get(c.getSuccessExtensionId()); - - PayOrderExcelVO excelItem = PayOrderConvert.INSTANCE.excelConvert(c); - excelItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用"); - excelItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道"); - excelItem.setNo(ObjectUtil.isNotNull(orderExtensionDO) ? orderExtensionDO.getNo() : ""); - excelDatum.add(excelItem); - }); - + // 拼接返回 + Map appMap = appService.getAppMap(convertList(list, PayOrderDO::getAppId)); + List excelList = PayOrderConvert.INSTANCE.convertList(list, appMap); // 导出 Excel - ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, excelDatum); + ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, excelList); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderBaseVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderBaseVO.java index ebbc32bbf..568f84459 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderBaseVO.java +++ b/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_ @Data public class PayOrderBaseVO { - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "应用编号不能为空") private Long appId; - @Schema(description = "渠道编号") + @Schema(description = "渠道编号", example = "2048") private Long channelId; - @Schema(description = "渠道编码") + @Schema(description = "渠道编码", example = "wx_app") private String channelCode; - @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") @NotNull(message = "商户订单编号不能为空") private String merchantOrderId; - @Schema(description = "商品标题", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "商品标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") @NotNull(message = "商品标题不能为空") private String subject; - @Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "商品描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是土豆") @NotNull(message = "商品描述不能为空") private String body; - @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://127.0.0.1:48080/pay/notify") @NotNull(message = "异步通知地址不能为空") private String notifyUrl; - @Schema(description = "通知商户支付结果的回调状态", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "通知商户支付结果的回调状态不能为空") - private Integer notifyStatus; - - @Schema(description = "支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @NotNull(message = "支付金额,单位:分不能为空") private Long price; - @Schema(description = "渠道手续费,单位:百分比") + @Schema(description = "渠道手续费,单位:百分比", example = "10") private Double channelFeeRate; - @Schema(description = "渠道手续金额,单位:分") - private Long channelFeePrice; + @Schema(description = "渠道手续金额,单位:分", example = "100") + private Integer channelFeePrice; - @Schema(description = "支付状态", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "支付状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "支付状态不能为空") private Integer status; - @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") @NotNull(message = "用户 IP不能为空") private String userIp; @@ -74,29 +70,20 @@ public class PayOrderBaseVO { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime successTime; - @Schema(description = "订单支付通知时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime notifyTime; + @Schema(description = "支付成功的订单拓展单编号", example = "50") + private Long extensionId; - @Schema(description = "支付成功的订单拓展单编号") - private Long successExtensionId; + @Schema(description = "支付订单号", example = "2048888") + private String no; - @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "退款状态不能为空") - private Integer refundStatus; - - @Schema(description = "退款次数", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "退款次数不能为空") - private Integer refundTimes; - - @Schema(description = "退款总金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "退款总金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @NotNull(message = "退款总金额,单位:分不能为空") private Long refundPrice; - @Schema(description = "渠道用户编号") + @Schema(description = "渠道用户编号", example = "2048") private String channelUserId; - @Schema(description = "渠道订单号") + @Schema(description = "渠道订单号", example = "4096") private String channelOrderNo; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java index 759450c93..17644fd60 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderDetailsRespVO.java +++ b/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; @ToString(callSuper = true) public class PayOrderDetailsRespVO extends PayOrderBaseVO { - @Schema(description = "支付订单编号") + @Schema(description = "支付订单编号", required = true, example = "1024") private Long id; - @Schema(description = "应用名称") + @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") private String appName; - @Schema(description = "渠道编号名称") - private String channelCodeName; - - @Schema(description = "创建时间") + @Schema(description = "创建时间", required = true) private LocalDateTime createTime; + @Schema(description = "更新时间", required = true) + private LocalDateTime updateTime; + /** * 支付订单扩展 */ - private PayOrderExtension payOrderExtension; + private PayOrderExtension extension; @Data @Schema(description = "支付订单扩展") public static class PayOrderExtension { - @Schema(description = "支付订单号") + @Schema(description = "支付订单号", required = true, example = "1024") private String no; @Schema(description = "支付异步通知的内容") private String channelNotifyData; + } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java index fc861a0df..5dc17a0f0 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExcelVO.java +++ b/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; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; @@ -9,78 +10,56 @@ import lombok.Data; import java.time.LocalDateTime; /** - * 支付订单Excel VO + * 支付订单 Excel VO * * @author aquan */ @Data public class PayOrderExcelVO { - @ExcelProperty("支付订单编号") + @ExcelProperty("编号") private Long id; - @ExcelProperty(value = "应用名称") - private String appName; - - @ExcelProperty("商品标题") - private String subject; - - @ExcelProperty("商户订单编号") - private String merchantOrderId; - - @ExcelProperty("渠道订单号") - private String channelOrderNo; - - @ExcelProperty(value = "支付订单号") - private String no; - - @ExcelProperty("支付金额,单位:元") - private String price; - - @ExcelProperty("渠道手续金额,单位:元") - private String channelFeePrice; - - @ExcelProperty("渠道手续费,单位:百分比") - private String channelFeeRate; - - @DictFormat(DictTypeConstants.ORDER_STATUS) - @ExcelProperty(value = "支付状态", converter = DictConvert.class) - private Integer status; - - @DictFormat(DictTypeConstants.ORDER_NOTIFY_STATUS) - @ExcelProperty(value = "通知商户支付结果的回调状态", converter = DictConvert.class) - private Integer notifyStatus; - - @ExcelProperty("异步通知地址") - private String notifyUrl; - @ExcelProperty("创建时间") private LocalDateTime createTime; + @ExcelProperty(value = "支付金额", converter = MoneyConvert.class) + private Integer price; + + @ExcelProperty(value = "退款金额", converter = MoneyConvert.class) + private Integer refundPrice; + + @ExcelProperty(value = "手续金额", converter = MoneyConvert.class) + private Integer channelFeePrice; + + @ExcelProperty("商户单号") + private String merchantOrderId; + + @ExcelProperty(value = "支付单号") + private String no; + + @ExcelProperty("渠道单号") + private String channelOrderNo; + + @ExcelProperty(value = "支付状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.ORDER_STATUS) + private Integer status; + + @ExcelProperty(value = "渠道编号名称", converter = DictConvert.class) + @DictFormat(DictTypeConstants.CHANNEL_CODE) + private String channelCode; + @ExcelProperty("订单支付成功时间") private LocalDateTime successTime; @ExcelProperty("订单失效时间") private LocalDateTime expireTime; - @ExcelProperty("订单支付通知时间") - private LocalDateTime notifyTime; + @ExcelProperty(value = "应用名称") + private String appName; - @ExcelProperty(value = "渠道编号名称") - private String channelCodeName; - - @ExcelProperty("用户 IP") - private String userIp; - - @DictFormat(DictTypeConstants.ORDER_REFUND_STATUS) - @ExcelProperty(value = "退款状态", converter = DictConvert.class) - private Integer refundStatus; - - @ExcelProperty("退款次数") - private Integer refundTimes; - - @ExcelProperty("退款总金额,单位:元") - private String refundPrice; + @ExcelProperty("商品标题") + private String subject; @ExcelProperty("商品描述") private String body; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExportReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExportReqVO.java index e589d588c..9e4d20b91 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderExportReqVO.java +++ b/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_ @Data public class PayOrderExportReqVO { - @Schema(description = "应用编号") + @Schema(description = "应用编号", example = "1024") private Long appId; - @Schema(description = "渠道编号") - private Long channelId; - - @Schema(description = "渠道编码") + @Schema(description = "渠道编码", example = "wx_app") private String channelCode; - @Schema(description = "商户订单编号") + @Schema(description = "商户订单编号", example = "4096") private String merchantOrderId; - @Schema(description = "商品标题") - private String subject; - - @Schema(description = "商品描述") - private String body; - - @Schema(description = "异步通知地址") - private String notifyUrl; - - @Schema(description = "通知商户支付结果的回调状态") - private Integer notifyStatus; - - @Schema(description = "支付金额,单位:分") - private Long price; - - @Schema(description = "渠道手续费,单位:百分比") - private Double channelFeeRate; - - @Schema(description = "渠道手续金额,单位:分") - private Long channelFeePrice; - - @Schema(description = "支付状态") - private Integer status; - - @Schema(description = "用户 IP") - private String userIp; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "订单失效时间") - private LocalDateTime[] expireTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "开始订单支付成功时间") - private LocalDateTime[] successTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "开始订单支付通知时间") - private LocalDateTime[] notifyTime; - - @Schema(description = "支付成功的订单拓展单编号") - private Long successExtensionId; - - @Schema(description = "退款状态") - private Integer refundStatus; - - @Schema(description = "退款次数") - private Integer refundTimes; - - @Schema(description = "退款总金额,单位:分") - private Long refundPrice; - - @Schema(description = "渠道用户编号") - private String channelUserId; - - @Schema(description = "渠道订单号") + @Schema(description = "渠道编号", example = "1888") private String channelOrderNo; + @Schema(description = "支付单号", example = "2014888") + private String no; + + @Schema(description = "支付状态", example = "0") + private Integer status; + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @Schema(description = "创建时间") private LocalDateTime[] createTime; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java index ada7c179b..05411c913 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageItemRespVO.java +++ b/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; @ToString(callSuper = true) public class PayOrderPageItemRespVO extends PayOrderBaseVO { - @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; - @Schema(description = "应用名称") + @Schema(description = "应用名称", example = "wx_pay") private String appName; - @Schema(description = "渠道名称") - private String channelCodeName; - - @Schema(description = "支付订单号") - private String no; - - } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageReqVO.java index 290feaf20..f7ff801cb 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderPageReqVO.java +++ b/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_ @ToString(callSuper = true) public class PayOrderPageReqVO extends PageParam { - @Schema(description = "应用编号") + @Schema(description = "应用编号", example = "1024") private Long appId; - @Schema(description = "渠道编号") - private Long channelId; - - @Schema(description = "渠道编码") + @Schema(description = "渠道编码", example = "wx_app") private String channelCode; - @Schema(description = "商户订单编号") + @Schema(description = "商户订单编号", example = "4096") private String merchantOrderId; - @Schema(description = "商品标题") - private String subject; - - @Schema(description = "商品描述") - private String body; - - @Schema(description = "异步通知地址") - private String notifyUrl; - - @Schema(description = "通知商户支付结果的回调状态") - private Integer notifyStatus; - - @Schema(description = "支付金额,单位:分") - private Long price; - - @Schema(description = "渠道手续费,单位:百分比") - private Double channelFeeRate; - - @Schema(description = "渠道手续金额,单位:分") - private Long channelFeePrice; - - @Schema(description = "支付状态") - private Integer status; - - @Schema(description = "用户 IP") - private String userIp; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "订单失效时间") - private LocalDateTime[] expireTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "订单支付成功时间") - private LocalDateTime[] successTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "订单支付通知时间") - private LocalDateTime[] notifyTime; - - @Schema(description = "支付成功的订单拓展单编号") - private Long successExtensionId; - - @Schema(description = "退款状态") - private Integer refundStatus; - - @Schema(description = "退款次数") - private Integer refundTimes; - - @Schema(description = "退款总金额,单位:分") - private Long refundPrice; - - @Schema(description = "渠道用户编号") - private String channelUserId; - - @Schema(description = "渠道订单号") + @Schema(description = "渠道编号", example = "1888") private String channelOrderNo; + @Schema(description = "支付单号", example = "2014888") + private String no; + + @Schema(description = "支付状态", example = "0") + private Integer status; + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @Schema(description = "创建时间") private LocalDateTime[] createTime; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderRespVO.java old mode 100755 new mode 100644 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java index 5f8446192..24e2b22e5 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/PayRefundController.java @@ -1,24 +1,20 @@ package cn.iocoder.yudao.module.pay.controller.admin.refund; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*; import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -35,6 +31,7 @@ import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @Tag(name = "管理后台 - 退款订单") @@ -56,20 +53,13 @@ public class PayRefundController { @PreAuthorize("@ss.hasPermission('pay:refund:query')") public CommonResult getRefund(@RequestParam("id") Long id) { PayRefundDO refund = refundService.getRefund(id); - if (ObjectUtil.isNull(refund)) { + if (refund == null) { return success(new PayRefundDetailsRespVO()); } - PayAppDO appDO = appService.getApp(refund.getAppId()); - PayChannelEnum channelEnum = PayChannelEnum.getByCode(refund.getChannelCode()); - PayOrderDO orderDO = orderService.getOrder(refund.getOrderId()); - - PayRefundDetailsRespVO refundDetail = PayRefundConvert.INSTANCE.refundDetailConvert(refund); - refundDetail.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用"); - refundDetail.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道"); - refundDetail.setSubject(orderDO.getSubject()); - - return success(refundDetail); + // 拼接数据 + PayAppDO app = appService.getApp(refund.getAppId()); + return success(PayRefundConvert.INSTANCE.convert(refund, app)); } @GetMapping("/page") @@ -82,21 +72,8 @@ public class PayRefundController { } // 处理应用ID数据 - Map appMap = appService.getAppMap( - CollectionUtils.convertList(pageResult.getList(), PayRefundDO::getAppId)); - List list = new ArrayList<>(pageResult.getList().size()); - pageResult.getList().forEach(c -> { - PayAppDO appDO = appMap.get(c.getAppId()); - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode()); - - PayRefundPageItemRespVO item = PayRefundConvert.INSTANCE.pageItemConvert(c); - - item.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用"); - item.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道"); - list.add(item); - }); - - return success(new PageResult<>(list, pageResult.getTotal())); + Map appMap = appService.getAppMap(convertList(pageResult.getList(), PayRefundDO::getAppId)); + return success(PayRefundConvert.INSTANCE.convertPage(pageResult, appMap)); } @GetMapping("/export-excel") @@ -105,36 +82,18 @@ public class PayRefundController { @OperateLog(type = EXPORT) public void exportRefundExcel(@Valid PayRefundExportReqVO exportReqVO, HttpServletResponse response) throws IOException { - List list = refundService.getRefundList(exportReqVO); if (CollectionUtil.isEmpty(list)) { ExcelUtils.write(response, "退款订单.xls", "数据", PayRefundExcelVO.class, new ArrayList<>()); + return; } - // 处理应用ID数据 - Map appMap = appService.getAppMap( - CollectionUtils.convertList(list, PayRefundDO::getAppId)); - - List excelDatum = new ArrayList<>(list.size()); - // 处理商品名称数据 - Map orderMap = orderService.getOrderSubjectMap( - CollectionUtils.convertList(list, PayRefundDO::getOrderId)); - - list.forEach(c -> { - PayAppDO appDO = appMap.get(c.getAppId()); - PayChannelEnum channelEnum = PayChannelEnum.getByCode(c.getChannelCode()); - - PayRefundExcelVO excelItem = PayRefundConvert.INSTANCE.excelConvert(c); - excelItem.setAppName(ObjectUtil.isNotNull(appDO) ? appDO.getName() : "未知应用"); - excelItem.setChannelCodeName(ObjectUtil.isNotNull(channelEnum) ? channelEnum.getName() : "未知渠道"); - excelItem.setSubject(orderMap.get(c.getOrderId()).getSubject()); - - excelDatum.add(excelItem); - }); - + // 拼接返回 + Map appMap = appService.getAppMap(convertList(list, PayRefundDO::getAppId)); + List excelList = PayRefundConvert.INSTANCE.convertList(list, appMap); // 导出 Excel - ExcelUtils.write(response, "退款订单.xls", "数据", PayRefundExcelVO.class, excelDatum); + ExcelUtils.write(response, "退款订单.xls", "数据", PayRefundExcelVO.class, excelList); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundBaseVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundBaseVO.java index 35b791e10..25cadf478 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundBaseVO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundBaseVO.java @@ -1,13 +1,10 @@ package cn.iocoder.yudao.module.pay.controller.admin.refund.vo; + import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; -import javax.validation.constraints.NotNull; import java.time.LocalDateTime; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - /** * 退款订单 Base VO,提供给添加、修改、详细的子 VO 使用 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 @@ -15,91 +12,67 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Data public class PayRefundBaseVO { - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "应用编号不能为空") + @Schema(description = "外部退款号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110") + private String no; + + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long appId; - @Schema(description = "渠道编号", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "渠道编号不能为空") + @Schema(description = "渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") private Long channelId; - @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "渠道编码不能为空") + @Schema(description = "渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_app") private String channelCode; - @Schema(description = "支付订单编号 pay_order 表id", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "支付订单编号 pay_order 表id不能为空") + @Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long orderId; - @Schema(description = "交易订单号 pay_extension 表no 字段", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "交易订单号 pay_extension 表no 字段不能为空") - private String tradeNo; + // ========== 商户相关字段 ========== - @Schema(description = "商户订单编号(商户系统生成)", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "商户订单编号(商户系统生成)不能为空") + @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "225") private String merchantOrderId; - @Schema(description = "商户退款订单号(商户系统生成)", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "商户退款订单号(商户系统生成)不能为空") - private String merchantRefundNo; + @Schema(description = "商户退款订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "512") + private String merchantRefundId; - @Schema(description = "异步通知商户地址", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "异步通知商户地址不能为空") + @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED) private String notifyUrl; - @Schema(description = "通知商户退款结果的回调状态", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "通知商户退款结果的回调状态不能为空") - private Integer notifyStatus; + // ========== 退款相关字段 ========== - @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "退款状态不能为空") + @Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") private Integer status; - @Schema(description = "退款类型(部分退款,全部退款)", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "退款类型(部分退款,全部退款)不能为空") - private Integer type; + @Schema(description = "支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Long payPrice; - @Schema(description = "支付金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "支付金额,单位分不能为空") - private Long payAmount; + @Schema(description = "退款金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") + private Long refundPrice; - @Schema(description = "退款金额,单位分", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "退款金额,单位分不能为空") - private Long refundAmount; - - @Schema(description = "退款原因", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "退款原因不能为空") + @Schema(description = "退款原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "我要退了") private String reason; - @Schema(description = "用户 IP") + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") private String userIp; - @Schema(description = "渠道订单号,pay_order 中的channel_order_no 对应", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "渠道订单号,pay_order 中的channel_order_no 对应不能为空") + // ========== 渠道相关字段 ========== + + @Schema(description = "渠道订单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "233") private String channelOrderNo; - @Schema(description = "渠道退款单号,渠道返回") + @Schema(description = "渠道退款单号", example = "2022") private String channelRefundNo; - @Schema(description = "渠道调用报错时,错误码") + @Schema(description = "退款成功时间") + private LocalDateTime successTime; + + @Schema(description = "调用渠道的错误码") private String channelErrorCode; - @Schema(description = "渠道调用报错时,错误信息") + @Schema(description = "调用渠道的错误提示") private String channelErrorMsg; @Schema(description = "支付渠道的额外参数") - private String channelExtras; - - @Schema(description = "退款失效时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime expireTime; - - @Schema(description = "退款成功时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime successTime; - - @Schema(description = "退款通知时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime notifyTime; + private String channelNotifyData; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundCreateReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundCreateReqVO.java deleted file mode 100755 index 86cf94636..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundCreateReqVO.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.iocoder.yudao.module.pay.controller.admin.refund.vo; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; - -@Schema(description = "管理后台 - 退款订单创建 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class PayRefundCreateReqVO extends PayRefundBaseVO { - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java index f622dda79..8f50a3a13 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundDetailsRespVO.java +++ b/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; import lombok.EqualsAndHashCode; import lombok.ToString; -import javax.validation.constraints.NotNull; import java.time.LocalDateTime; @Schema(description = "管理后台 - 退款订单详情 Response VO") @@ -17,14 +16,11 @@ public class PayRefundDetailsRespVO extends PayRefundBaseVO { @Schema(description = "支付退款编号", requiredMode = Schema.RequiredMode.REQUIRED) private Long id; - @Schema(description = "应用名称") + @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是芋艿") private String appName; - @Schema(description = "渠道编号名称") - private String channelCodeName; - - @NotNull(message = "商品标题不能为空") - private String subject; + @Schema(description = "支付订单", requiredMode = Schema.RequiredMode.REQUIRED) + private Order order; @Schema(description = "创建时间") private LocalDateTime createTime; @@ -32,4 +28,13 @@ public class PayRefundDetailsRespVO extends PayRefundBaseVO { @Schema(description = "更新时间") private LocalDateTime updateTime; + @Schema(description = "管理后台 - 支付订单") + @Data + public static class Order { + + @Schema(description = "商品标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + private String subject; + + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java index 9a9fad090..758b6b6b0 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java +++ b/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; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.framework.excel.core.convert.MoneyConvert; import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; @@ -19,64 +20,42 @@ public class PayRefundExcelVO { @ExcelProperty("支付退款编号") private Long id; - @ExcelProperty("商品名称") - private String subject; + @ExcelProperty("创建时间") + private LocalDateTime createTime; - @ExcelProperty(value = "应用名称") - private String appName; + @ExcelProperty(value = "支付金额", converter = MoneyConvert.class) + private Integer payPrice; - @ExcelProperty(value = "渠道编号名称") - private String channelCodeName; + @ExcelProperty(value = "退款金额", converter = MoneyConvert.class) + private Integer refundPrice; - @ExcelProperty("交易订单号") - private String tradeNo; + @ExcelProperty("商户退款单号") + private String merchantRefundId; + @ExcelProperty("退款单号") + private String no; + @ExcelProperty("渠道退款单号") + private String channelRefundNo; - @ExcelProperty("商户订单编号") + @ExcelProperty("商户支付单号") private String merchantOrderId; + @ExcelProperty("渠道支付单号") + private String channelOrderNo; - @ExcelProperty("商户退款订单号") - private String merchantRefundNo; - - @ExcelProperty("异步通知商户地址") - private String notifyUrl; - - @DictFormat(DictTypeConstants.ORDER_NOTIFY_STATUS) - @ExcelProperty(value = "商户退款结果回调状态", converter = DictConvert.class) - private Integer notifyStatus; - - @DictFormat(DictTypeConstants.REFUND_ORDER_STATUS) @ExcelProperty(value = "退款状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.REFUND_STATUS) private Integer status; - @DictFormat(DictTypeConstants.REFUND_ORDER_TYPE) - @ExcelProperty(value = "退款类型", converter = DictConvert.class) - private Integer type; + @ExcelProperty(value = "退款渠道", converter = DictConvert.class) + @DictFormat(DictTypeConstants.CHANNEL_CODE) + private String channelCode; - @ExcelProperty("支付金额,单位:元") - private String payPrice; + @ExcelProperty("成功时间") + private LocalDateTime successTime; - @ExcelProperty("退款金额,单位:元") - private String refundPrice; + @ExcelProperty(value = "支付应用") + private String appName; @ExcelProperty("退款原因") private String reason; - @ExcelProperty("用户付款 IP") - private String userIp; - - @ExcelProperty("渠道订单号") - private String channelOrderNo; - - @ExcelProperty("渠道退款单号") - private String channelRefundNo; - - @ExcelProperty("创建时间") - private LocalDateTime createTime; - - @ExcelProperty("退款成功时间") - private LocalDateTime successTime; - - @ExcelProperty("退款失效时间") - private LocalDateTime expireTime; - } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExportReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExportReqVO.java index 215b51fab..645816ee2 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExportReqVO.java +++ b/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_ @Data public class PayRefundExportReqVO { - @Schema(description = "应用编号") + @Schema(description = "应用编号", example = "1024") private Long appId; - @Schema(description = "渠道编号") - private Long channelId; - - @Schema(description = "渠道编码") + @Schema(description = "渠道编码", example = "wx_app") private String channelCode; - @Schema(description = "支付订单编号 pay_order 表id") - private Long orderId; - - @Schema(description = "交易订单号 pay_extension 表no 字段") - private String tradeNo; - - @Schema(description = "商户订单编号(商户系统生成)") + @Schema(description = "商户支付单号", example = "10") private String merchantOrderId; - @Schema(description = "商户退款订单号(商户系统生成)") - private String merchantRefundNo; + @Schema(description = "商户退款单号", example = "20") + private String merchantRefundId; - @Schema(description = "异步通知商户地址") - private String notifyUrl; - - @Schema(description = "通知商户退款结果的回调状态") - private Integer notifyStatus; - - @Schema(description = "退款状态") - private Integer status; - - @Schema(description = "退款类型(部分退款,全部退款)") - private Integer type; - - @Schema(description = "支付金额,单位分") - private Long payPrice; - - @Schema(description = "退款金额,单位分") - private Long refundPrice; - - @Schema(description = "退款原因") - private String reason; - - @Schema(description = "用户 IP") - private String userIp; - - @Schema(description = "渠道订单号,pay_order 中的channel_order_no 对应") + @Schema(description = "渠道支付单号", example = "30") private String channelOrderNo; - @Schema(description = "渠道退款单号,渠道返回") + @Schema(description = "渠道退款单号", example = "40") private String channelRefundNo; - @Schema(description = "渠道调用报错时,错误码") - private String channelErrorCode; - - @Schema(description = "渠道调用报错时,错误信息") - private String channelErrorMsg; - - @Schema(description = "支付渠道的额外参数") - private String channelExtras; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "退款失效时间") - private LocalDateTime[] expireTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "退款成功时间") - private LocalDateTime[] successTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "退款通知时间") - private LocalDateTime[] notifyTime; + @Schema(description = "退款状态", example = "0") + private Integer status; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @Schema(description = "创建时间") diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java index 8c547c6e4..27c1285cc 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageItemRespVO.java +++ b/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; @ToString(callSuper = true) public class PayRefundPageItemRespVO extends PayRefundBaseVO { - @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "支付订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "应用名称") + @Schema(description = "应用名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是芋艿") private String appName; - @Schema(description = "渠道名称") - private String channelCodeName; - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageReqVO.java index 795786439..7ff1530f0 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundPageReqVO.java +++ b/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_ @ToString(callSuper = true) public class PayRefundPageReqVO extends PageParam { - @Schema(description = "应用编号") + @Schema(description = "应用编号", example = "1024") private Long appId; - @Schema(description = "渠道编号") - private Long channelId; - - @Schema(description = "渠道编码") + @Schema(description = "渠道编码", example = "wx_app") private String channelCode; - @Schema(description = "支付订单编号 pay_order 表id") - private Long orderId; - - @Schema(description = "交易订单号 pay_extension 表no 字段") - private String tradeNo; - - @Schema(description = "商户订单编号(商户系统生成)") + @Schema(description = "商户支付单号", example = "10") private String merchantOrderId; - @Schema(description = "商户退款订单号(商户系统生成)") - private String merchantRefundNo; + @Schema(description = "商户退款单号", example = "20") + private String merchantRefundId; - @Schema(description = "异步通知商户地址") - private String notifyUrl; - - @Schema(description = "通知商户退款结果的回调状态") - private Integer notifyStatus; - - @Schema(description = "退款状态") - private Integer status; - - @Schema(description = "退款类型(部分退款,全部退款)") - private Integer type; - - @Schema(description = "支付金额,单位分") - private Long payPrice; - - @Schema(description = "退款金额,单位分") - private Long refundPrice; - - @Schema(description = "退款原因") - private String reason; - - @Schema(description = "用户 IP") - private String userIp; - - @Schema(description = "渠道订单号,pay_order 中的channel_order_no 对应") + @Schema(description = "渠道支付单号", example = "30") private String channelOrderNo; - @Schema(description = "渠道退款单号,渠道返回") + @Schema(description = "渠道退款单号", example = "40") private String channelRefundNo; - @Schema(description = "渠道调用报错时,错误码") - private String channelErrorCode; - - @Schema(description = "渠道调用报错时,错误信息") - private String channelErrorMsg; - - @Schema(description = "支付渠道的额外参数") - private String channelExtras; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "退款失效时间") - private LocalDateTime[] expireTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "退款成功时间") - private LocalDateTime[] successTime; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "退款通知时间") - private LocalDateTime[] notifyTime; + @Schema(description = "退款状态", example = "0") + private Integer status; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @Schema(description = "创建时间") diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundRespVO.java deleted file mode 100755 index d3ee9f455..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundRespVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.pay.controller.admin.refund.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 退款订单 Response VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class PayRefundRespVO extends PayRefundBaseVO { - - @Schema(description = "支付退款编号", requiredMode = Schema.RequiredMode.REQUIRED) - private Long id; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundUpdateReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundUpdateReqVO.java deleted file mode 100755 index cf5b0be1f..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundUpdateReqVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.pay.controller.admin.refund.vo; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import javax.validation.constraints.*; - -@Schema(description = "管理后台 - 退款订单更新 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class PayRefundUpdateReqVO extends PayRefundBaseVO { - - @Schema(description = "支付退款编号", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "支付退款编号不能为空") - private Long id; - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/notify/PayNotifyTaskConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/notify/PayNotifyTaskConvert.java new file mode 100644 index 000000000..d0b8e36ff --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/notify/PayNotifyTaskConvert.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.pay.convert.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 支付通知 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface PayNotifyTaskConvert { + + PayNotifyTaskConvert INSTANCE = Mappers.getMapper(PayNotifyTaskConvert.class); + + PayNotifyTaskRespVO convert(PayNotifyTaskDO bean); + + default PageResult convertPage(PageResult page, Map appMap){ + PageResult result = convertPage(page); + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName()))); + return result; + } + PageResult convertPage(PageResult page); + + default PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, PayAppDO app, List logs) { + PayNotifyTaskDetailRespVO respVO = convert(task, logs); + if (app != null) { + respVO.setAppName(app.getName()); + } + return respVO; + } + PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, List logs); +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java index 647fe3dbb..682006041 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java @@ -1,20 +1,22 @@ package cn.iocoder.yudao.module.pay.convert.order; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; import cn.iocoder.yudao.module.pay.controller.app.order.vo.AppPayOrderSubmitRespVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.List; +import java.util.Map; /** * 支付订单 Convert @@ -30,60 +32,32 @@ public interface PayOrderConvert { PayOrderRespDTO convert2(PayOrderDO order); - PayOrderDetailsRespVO orderDetailConvert(PayOrderDO bean); - - PayOrderDetailsRespVO.PayOrderExtension orderDetailExtensionConvert(PayOrderExtensionDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - - /** - * 订单 DO 转自定义分页对象 - * - * @param bean 订单DO - * @return 分页对象 - */ - PayOrderPageItemRespVO pageConvertItemPage(PayOrderDO bean); - - // TODO 芋艿:优化下 convert 逻辑 - default PayOrderExcelVO excelConvert(PayOrderDO bean) { - if (bean == null) { - return null; + default PayOrderDetailsRespVO convert(PayOrderDO order, PayOrderExtensionDO orderExtension, PayAppDO app) { + PayOrderDetailsRespVO respVO = convertDetail(order); + respVO.setExtension(convert(orderExtension)); + if (app != null) { + respVO.setAppName(app.getName()); } - - PayOrderExcelVO payOrderExcelVO = new PayOrderExcelVO(); - - payOrderExcelVO.setId(bean.getId()); - payOrderExcelVO.setSubject(bean.getSubject()); - payOrderExcelVO.setMerchantOrderId(bean.getMerchantOrderId()); - payOrderExcelVO.setChannelOrderNo(bean.getChannelOrderNo()); - payOrderExcelVO.setStatus(bean.getStatus()); - payOrderExcelVO.setNotifyStatus(bean.getNotifyStatus()); - payOrderExcelVO.setNotifyUrl(bean.getNotifyUrl()); - payOrderExcelVO.setCreateTime(bean.getCreateTime()); - payOrderExcelVO.setSuccessTime(bean.getSuccessTime()); - payOrderExcelVO.setExpireTime(bean.getExpireTime()); - payOrderExcelVO.setNotifyTime(bean.getNotifyTime()); - payOrderExcelVO.setUserIp(bean.getUserIp()); - payOrderExcelVO.setRefundStatus(bean.getRefundStatus()); - payOrderExcelVO.setRefundTimes(bean.getRefundTimes()); - payOrderExcelVO.setBody(bean.getBody()); - - BigDecimal multiple = new BigDecimal(100); - - payOrderExcelVO.setPrice(BigDecimal.valueOf(bean.getPrice()) - .divide(multiple, 2, RoundingMode.HALF_UP).toString()); - - payOrderExcelVO.setChannelFeePrice(BigDecimal.valueOf(bean.getChannelFeePrice()) - .divide(multiple, 2, RoundingMode.HALF_UP).toString()); - payOrderExcelVO.setChannelFeeRate(java.math.BigDecimal.valueOf(bean.getChannelFeeRate()) - .multiply(multiple).toString()); - payOrderExcelVO.setRefundPrice(BigDecimal.valueOf(bean.getRefundPrice()) - .divide(multiple, 2, RoundingMode.HALF_UP).toString()); - - return payOrderExcelVO; + return respVO; } + PayOrderDetailsRespVO convertDetail(PayOrderDO bean); + PayOrderDetailsRespVO.PayOrderExtension convert(PayOrderExtensionDO bean); + + default PageResult convertPage(PageResult page, Map appMap) { + PageResult result = convertPage(page); + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName()))); + return result; + } + PageResult convertPage(PageResult page); + + default List convertList(List list, Map appMap) { + return CollectionUtils.convertList(list, order -> { + PayOrderExcelVO excelVO = convertExcel(order); + MapUtils.findAndThen(appMap, order.getAppId(), app -> excelVO.setAppName(app.getName())); + return excelVO; + }); + } + PayOrderExcelVO convertExcel(PayOrderDO bean); PayOrderDO convert(PayOrderCreateReqDTO bean); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java index eb2b2053d..9f087f702 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java @@ -1,87 +1,56 @@ package cn.iocoder.yudao.module.pay.convert.refund; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; -import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*; +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundDetailsRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExcelVO; +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageItemRespVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.List; +import java.util.Map; @Mapper public interface PayRefundConvert { PayRefundConvert INSTANCE = Mappers.getMapper(PayRefundConvert.class); - PayRefundDO convert(PayRefundCreateReqVO bean); - PayRefundDO convert(PayRefundUpdateReqVO bean); - - PayRefundRespVO convert(PayRefundDO bean); - - /** - * 退款订单 DO 转 退款详情订单 VO - * - * @param bean 退款订单 DO - * @return 退款详情订单 VO - */ - PayRefundDetailsRespVO refundDetailConvert(PayRefundDO bean); - - /** - * 退款订单DO 转 分页退款条目VO - * - * @param bean 退款订单DO - * @return 分页退款条目VO - */ - PayRefundPageItemRespVO pageItemConvert(PayRefundDO bean); - - List convertList(List list); - - PageResult convertPage(PageResult page); - - /** - * 退款订单DO 转 导出excel VO - * - * @param bean 退款订单DO - * @return 导出 excel VO - */ - default PayRefundExcelVO excelConvert(PayRefundDO bean) { - if (bean == null) { - return null; + default PayRefundDetailsRespVO convert(PayRefundDO refund, PayAppDO app) { + PayRefundDetailsRespVO respVO = convert(refund); + if (app != null) { + respVO.setAppName(app.getName()); } - - PayRefundExcelVO payRefundExcelVO = new PayRefundExcelVO(); - - payRefundExcelVO.setId(bean.getId()); - payRefundExcelVO.setTradeNo(bean.getNo()); - payRefundExcelVO.setMerchantOrderId(bean.getMerchantOrderId()); - // TODO 芋艿:晚点在改 -// payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo()); - payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl()); - payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus()); - payRefundExcelVO.setStatus(bean.getStatus()); - payRefundExcelVO.setReason(bean.getReason()); - payRefundExcelVO.setUserIp(bean.getUserIp()); - payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo()); - payRefundExcelVO.setChannelRefundNo(bean.getChannelRefundNo()); - payRefundExcelVO.setSuccessTime(bean.getSuccessTime()); - payRefundExcelVO.setCreateTime(bean.getCreateTime()); - - BigDecimal multiple = new BigDecimal(100); - payRefundExcelVO.setPayPrice(BigDecimal.valueOf(bean.getPayPrice()) - .divide(multiple, 2, RoundingMode.HALF_UP).toString()); - payRefundExcelVO.setRefundPrice(BigDecimal.valueOf(bean.getRefundPrice()) - .divide(multiple, 2, RoundingMode.HALF_UP).toString()); - - return payRefundExcelVO; + return respVO; } + PayRefundDetailsRespVO convert(PayRefundDO bean); + PayRefundDetailsRespVO.Order convert(PayOrderDO bean); + + default PageResult convertPage(PageResult page, Map appMap) { + PageResult result = convertPage(page); + result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName()))); + return result; + } + PageResult convertPage(PageResult page); PayRefundDO convert(PayRefundCreateReqDTO bean); PayRefundRespDTO convert02(PayRefundDO bean); + default List convertList(List list, Map appMap) { + return CollectionUtils.convertList(list, order -> { + PayRefundExcelVO excelVO = convertExcel(order); + MapUtils.findAndThen(appMap, order.getAppId(), app -> excelVO.setAppName(app.getName())); + return excelVO; + }); + } + PayRefundExcelVO convertExcel(PayRefundDO bean); + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java index 6bc60dc3a..bbcf105b4 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java +++ b/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; import java.time.LocalDateTime; /** - * 商户支付、退款等的通知 + * 支付通知 * 在支付系统收到支付渠道的支付、退款的结果后,需要不断的通知到业务系统,直到成功。 * * @author 芋道源码 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java index 62b43721e..a51b875fd 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java +++ b/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; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; @@ -71,12 +69,6 @@ public class PayOrderDO extends BaseDO { * 异步通知地址 */ private String notifyUrl; - /** - * 通知商户支付结果的回调状态 - * - * 枚举 {@link PayOrderNotifyStatusEnum} - */ - private Integer notifyStatus; // ========== 订单相关字段 ========== @@ -93,7 +85,7 @@ public class PayOrderDO extends BaseDO { /** * 渠道手续金额,单位:分 */ - private Long channelFeePrice; + private Integer channelFeePrice; /** * 支付状态 * @@ -112,28 +104,20 @@ public class PayOrderDO extends BaseDO { * 订单支付成功时间 */ private LocalDateTime successTime; - /** - * 订单支付通知时间,即支付渠道的通知时间 - */ - private LocalDateTime notifyTime; /** * 支付成功的订单拓展单编号 * - * 关联 {@link PayOrderDO#getId()} + * 关联 {@link PayOrderExtensionDO#getId()} */ - private Long successExtensionId; + private Long extensionId; + /** + * 支付成功的外部订单号 + * + * 关联 {@link PayOrderExtensionDO#getNo()} + */ + private String no; // ========== 退款相关字段 ========== - /** - * 退款状态 - * - * 枚举 {@link PayOrderRefundStatusEnum} - */ - private Integer refundStatus; - /** - * 退款次数 - */ - private Integer refundTimes; /** * 退款总金额,单位:分 */ diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java index 15d3a96ea..9466243e1 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java +++ b/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; /** * 支付订单拓展 DO * + * 每次调用支付渠道,都会生成一条对应记录 * * @author 芋道源码 */ diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java index eaf6cd876..a3ca04871 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java +++ b/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; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -89,12 +88,6 @@ public class PayRefundDO extends BaseDO { * 异步通知地址 */ private String notifyUrl; - /** - * 通知商户退款结果的回调状态 - * - * 枚举 {@link PayNotifyStatusEnum} - */ - private Integer notifyStatus; // ========== 退款相关字段 ========== /** @@ -147,7 +140,7 @@ public class PayRefundDO extends BaseDO { */ private String channelErrorCode; /** - * 调用渠道报错时,错误信息 + * 调用渠道的错误提示 */ private String channelErrorMsg; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyLogMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyLogMapper.java index 34a7b6249..8b586dff2 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyLogMapper.java +++ b/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; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface PayNotifyLogMapper extends BaseMapperX { + + default List selectListByTaskId(Long taskId) { + return selectList(PayNotifyLogDO::getTaskId, taskId); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyTaskMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyTaskMapper.java index 37ec65a3a..cc7701271 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyTaskMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyTaskMapper.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.module.pay.dal.mysql.notify; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; import java.time.LocalDateTime; @@ -21,10 +24,21 @@ public interface PayNotifyTaskMapper extends BaseMapperX { * @return PayTransactionNotifyTaskDO 数组 */ default List selectListByNotify() { - return selectList(new QueryWrapper() - .in("status", PayNotifyStatusEnum.WAITING.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(), - PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()) - .le("next_notify_time", LocalDateTime.now())); + return selectList(new LambdaQueryWrapper() + .in(PayNotifyTaskDO::getStatus, PayNotifyStatusEnum.WAITING.getStatus(), + PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()) + .le(PayNotifyTaskDO::getNextNotifyTime, LocalDateTime.now())); + } + + default PageResult selectPage(PayNotifyTaskPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayNotifyTaskDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayNotifyTaskDO::getType, reqVO.getType()) + .eqIfPresent(PayNotifyTaskDO::getDataId, reqVO.getDataId()) + .eqIfPresent(PayNotifyTaskDO::getStatus, reqVO.getStatus()) + .eqIfPresent(PayNotifyTaskDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .betweenIfPresent(PayNotifyTaskDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayNotifyTaskDO::getId)); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java index 49c7d2fbf..198383b4f 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderMapper.java @@ -1,53 +1,41 @@ package cn.iocoder.yudao.module.pay.dal.mysql.order; -import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.ibatis.annotations.Mapper; -import java.util.Collection; import java.util.List; @Mapper public interface PayOrderMapper extends BaseMapperX { default PageResult selectPage(PayOrderPageReqVO reqVO) { - return selectPage(reqVO, new QueryWrapperX() - .eqIfPresent("app_id", reqVO.getAppId()) - .eqIfPresent("channel_id", reqVO.getChannelId()) - .eqIfPresent("channel_code", reqVO.getChannelCode()) - .likeIfPresent("merchant_order_id", reqVO.getMerchantOrderId()) - .eqIfPresent("notify_status", reqVO.getNotifyStatus()) - .eqIfPresent("status", reqVO.getStatus()) - .eqIfPresent("refund_status", reqVO.getRefundStatus()) - .likeIfPresent("channel_order_no", reqVO.getChannelOrderNo()) - .betweenIfPresent("create_time", reqVO.getCreateTime()) - .orderByDesc("id")); + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayOrderDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayOrderDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayOrderDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayOrderDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(PayOrderDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayOrderDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayOrderDO::getId)); } default List selectList(PayOrderExportReqVO reqVO) { - return selectList(new QueryWrapperX() - .eqIfPresent("app_id", reqVO.getAppId()) - .eqIfPresent("channel_id", reqVO.getChannelId()) - .eqIfPresent("channel_code", reqVO.getChannelCode()) - .likeIfPresent("merchant_order_id", reqVO.getMerchantOrderId()) - .eqIfPresent("notify_status", reqVO.getNotifyStatus()) - .eqIfPresent("status", reqVO.getStatus()) - .eqIfPresent("refund_status", reqVO.getRefundStatus()) - .likeIfPresent("channel_order_no", reqVO.getChannelOrderNo()) - .betweenIfPresent("create_time", reqVO.getCreateTime()) - .orderByDesc("id")); - } - - default List findByIdListQueryOrderSubject(Collection idList) { - return selectList(new LambdaQueryWrapper() - .select(PayOrderDO::getId, PayOrderDO::getSubject) - .in(PayOrderDO::getId, idList)); + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(PayOrderDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayOrderDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayOrderDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayOrderDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayOrderDO::getNo, reqVO.getNo()) + .eqIfPresent(PayOrderDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayOrderDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayOrderDO::getId)); } default Long selectCountByAppId(Long appId) { @@ -55,13 +43,13 @@ public interface PayOrderMapper extends BaseMapperX { } default PayOrderDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) { - return selectOne(new QueryWrapper().eq("app_id", appId) - .eq("merchant_order_id", merchantOrderId)); + return selectOne(PayOrderDO::getAppId, appId, + PayOrderDO::getMerchantOrderId, merchantOrderId); } default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) { - return update(update, new QueryWrapper() - .eq("id", id).eq("status", status)); + return update(update, new LambdaQueryWrapper() + .eq(PayOrderDO::getId, id).eq(PayOrderDO::getStatus, status)); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java index 629650e0f..788576059 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; @@ -44,27 +43,29 @@ public interface PayRefundMapper extends BaseMapperX { } default PageResult selectPage(PayRefundPageReqVO reqVO) { - return selectPage(reqVO, new QueryWrapperX() - .eqIfPresent("app_id", reqVO.getAppId()) - .eqIfPresent("channel_code", reqVO.getChannelCode()) - .likeIfPresent("merchant_refund_no", reqVO.getMerchantRefundNo()) - .eqIfPresent("type", reqVO.getType()) - .eqIfPresent("status", reqVO.getStatus()) - .eqIfPresent("notify_status", reqVO.getNotifyStatus()) - .betweenIfPresent("create_time", reqVO.getCreateTime()) - .orderByDesc("id")); + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PayRefundDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayRefundDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayRefundDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayRefundDO::getMerchantRefundId, reqVO.getMerchantRefundId()) + .likeIfPresent(PayRefundDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayRefundDO::getChannelRefundNo, reqVO.getChannelRefundNo()) + .eqIfPresent(PayRefundDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayRefundDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayRefundDO::getId)); } default List selectList(PayRefundExportReqVO reqVO) { - return selectList(new QueryWrapperX() - .eqIfPresent("app_id", reqVO.getAppId()) - .eqIfPresent("channel_code", reqVO.getChannelCode()) - .likeIfPresent("merchant_refund_no", reqVO.getMerchantRefundNo()) - .eqIfPresent("type", reqVO.getType()) - .eqIfPresent("status", reqVO.getStatus()) - .eqIfPresent("notify_status", reqVO.getNotifyStatus()) - .betweenIfPresent("create_time", reqVO.getCreateTime()) - .orderByDesc("id")); + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(PayRefundDO::getAppId, reqVO.getAppId()) + .eqIfPresent(PayRefundDO::getChannelCode, reqVO.getChannelCode()) + .likeIfPresent(PayRefundDO::getMerchantOrderId, reqVO.getMerchantOrderId()) + .likeIfPresent(PayRefundDO::getMerchantRefundId, reqVO.getMerchantRefundId()) + .likeIfPresent(PayRefundDO::getChannelOrderNo, reqVO.getChannelOrderNo()) + .likeIfPresent(PayRefundDO::getChannelRefundNo, reqVO.getChannelRefundNo()) + .eqIfPresent(PayRefundDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(PayRefundDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PayRefundDO::getId)); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java index 4f5aaf750..e9bc1d59d 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/RedisKeyConstants.java +++ b/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 { "pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类 RedisKeyDefine.KeyTypeEnum.HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 + /** + * 支付序号的缓存 + * + * KEY 格式:pay_no:{prefix} + * VALUE 数据格式:编号自增 + */ + String PAY_NO = "pay_no"; + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/no/PayNoRedisDAO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/no/PayNoRedisDAO.java new file mode 100644 index 000000000..7ce6caadc --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/redis/no/PayNoRedisDAO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.pay.dal.redis.no; + +import cn.hutool.core.date.DatePattern;import cn.hutool.core.date.DateUtil;import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import javax.annotation.Resource;import java.time.LocalDateTime; + +/** + * 支付序号的 Redis DAO + * + * @author 芋道源码 + */ +@Repository +public class PayNoRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生成序号 + * + * @param prefix 前缀 + * @return 序号 + */ + public String generate(String prefix) { + String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + Long no = stringRedisTemplate.opsForValue().increment(noPrefix); + return noPrefix + no; + } + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderNotifyStatusEnum.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderNotifyStatusEnum.java deleted file mode 100644 index b7161a9f4..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderNotifyStatusEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.pay.enums.order; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 支付订单的通知状态枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum PayOrderNotifyStatusEnum implements IntArrayValuable { - - NO(0, "未通知"), - SUCCESS(10, "通知成功"), - FAILURE(20, "通知失败") - ; - - private final Integer status; - private final String name; - - @Override - public int[] array() { - return new int[0]; - } - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java deleted file mode 100644 index b9dbcb67b..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.pay.enums.order; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 支付订单的退款状态枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum PayOrderRefundStatusEnum implements IntArrayValuable { - - NO(0, "未退款"), - PART(10, "部分退款"), - ALL(20, "全部退款") - ; - - private final Integer status; - private final String name; - - @Override - public int[] array() { - return new int[0]; - } - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayConfiguration.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayConfiguration.java new file mode 100644 index 000000000..376cd0acb --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayConfiguration.java @@ -0,0 +1,9 @@ +package cn.iocoder.yudao.module.pay.framework.pay.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(PayProperties.class) +public class PayConfiguration { +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java similarity index 71% rename from yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java rename to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java index dc7380865..d422b3528 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.framework.pay.config; +package cn.iocoder.yudao.module.pay.framework.pay.config; import lombok.Data; import org.hibernate.validator.constraints.URL; @@ -12,6 +12,9 @@ import javax.validation.constraints.NotEmpty; @Data public class PayProperties { + private static final String ORDER_NO_PREFIX = "P"; + private static final String REFUND_NO_PREFIX = "R"; + /** * 支付回调地址 * @@ -34,4 +37,16 @@ public class PayProperties { @URL(message = "支付回调地址的格式必须是 URL") private String refundNotifyUrl; + /** + * 支付订单 no 的前缀 + */ + @NotEmpty(message = "支付订单 no 的前缀不能为空") + private String orderNoPrefix = ORDER_NO_PREFIX; + + /** + * 退款订单 no 的前缀 + */ + @NotEmpty(message = "退款订单 no 的前缀不能为空") + private String refundNoPrefix = REFUND_NO_PREFIX; + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/package-info.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/package-info.java new file mode 100644 index 000000000..07f837ff5 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位,无实际作用 + */ +package cn.iocoder.yudao.module.pay.framework.pay.core; \ No newline at end of file diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppService.java index 7105b78a8..4f9e289d6 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppService.java +++ b/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; import java.util.Map; /** - * 支付应用信息 Service 接口 + * 支付应用 Service 接口 * * @author 芋艿 */ public interface PayAppService { /** - * 创建支付应用信息 + * 创建支付应用 * - * @param createReqVO 创建信息 + * @param createReqVO 创建 * @return 编号 */ Long createApp(@Valid PayAppCreateReqVO createReqVO); /** - * 更新支付应用信息 + * 更新支付应用 * - * @param updateReqVO 更新信息 + * @param updateReqVO 更新 */ void updateApp(@Valid PayAppUpdateReqVO updateReqVO); /** - * 修改应用信息状态 + * 修改应用状态 * * @param id 应用编号 * @param status 状态 @@ -44,33 +44,40 @@ public interface PayAppService { void updateAppStatus(Long id, Integer status); /** - * 删除支付应用信息 + * 删除支付应用 * * @param id 编号 */ void deleteApp(Long id); /** - * 获得支付应用信息 + * 获得支付应用 * * @param id 编号 - * @return 支付应用信息 + * @return 支付应用 */ PayAppDO getApp(Long id); /** - * 获得支付应用信息列表 + * 获得支付应用列表 * * @param ids 编号 - * @return 支付应用信息列表 + * @return 支付应用列表 */ List getAppList(Collection ids); /** - * 获得支付应用信息分页 + * 获得支付应用列表 + * + * @return 支付应用列表 + */ + List getAppList(); + + /** + * 获得支付应用分页 * * @param pageReqVO 分页查询 - * @return 支付应用信息分页 + * @return 支付应用分页 */ PageResult getAppPage(PayAppPageReqVO pageReqVO); @@ -91,7 +98,7 @@ public interface PayAppService { * 如果不合法,抛出 {@link ServiceException} 业务异常 * * @param id 应用编号 - * @return 应用信息 + * @return 应用 */ PayAppDO validPayApp(Long id); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java index 48e9c3c97..56087ef13 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.pay.service.app; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO; 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; import cn.iocoder.yudao.module.pay.convert.app.PayAppConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.mysql.app.PayAppMapper; -import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; import org.springframework.context.annotation.Lazy; @@ -77,10 +73,10 @@ public class PayAppServiceImpl implements PayAppService { validateAppExists(id); // 校验关联数据是否存在 if (orderService.getOrderCountByAppId(id) > 0) { - throw exception(PAY_APP_EXIST_ORDER_CANT_DELETE); + throw exception(APP_EXIST_ORDER_CANT_DELETE); } if (refundService.getRefundCountByAppId(id) > 0) { - throw exception(PAY_APP_EXIST_REFUND_CANT_DELETE); + throw exception(APP_EXIST_REFUND_CANT_DELETE); } // 删除 @@ -89,7 +85,7 @@ public class PayAppServiceImpl implements PayAppService { private void validateAppExists(Long id) { if (appMapper.selectById(id) == null) { - throw exception(PAY_APP_NOT_FOUND); + throw exception(APP_NOT_FOUND); } } @@ -103,6 +99,11 @@ public class PayAppServiceImpl implements PayAppService { return appMapper.selectBatchIds(ids); } + @Override + public List getAppList() { + return appMapper.selectList(); + } + @Override public PageResult getAppPage(PayAppPageReqVO pageReqVO) { return appMapper.selectPage(pageReqVO); @@ -113,11 +114,11 @@ public class PayAppServiceImpl implements PayAppService { PayAppDO app = appMapper.selectById(id); // 校验是否存在 if (app == null) { - throw exception(ErrorCodeConstants.PAY_APP_NOT_FOUND); + throw exception(ErrorCodeConstants.APP_NOT_FOUND); } // 校验是否禁用 if (CommonStatusEnum.DISABLE.getStatus().equals(app.getStatus())) { - throw exception(ErrorCodeConstants.PAY_APP_IS_DISABLE); + throw exception(ErrorCodeConstants.APP_IS_DISABLE); } return app; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java index 45604c04c..e76e4b35d 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java +++ b/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; import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR; -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; /** * 支付渠道 Service 实现类 @@ -142,7 +141,7 @@ public class PayChannelServiceImpl implements PayChannelService { // 解析配置 Class payClass = PayChannelEnum.getByCode(code).getConfigClass(); if (ObjectUtil.isNull(payClass)) { - throw exception(CHANNEL_NOT_EXISTS); + throw exception(CHANNEL_NOT_FOUND); } PayClientConfig config = JsonUtils.parseObject2(configStr, payClass); Assert.notNull(config); @@ -167,7 +166,7 @@ public class PayChannelServiceImpl implements PayChannelService { private PayChannelDO validateChannelExists(Long id) { PayChannelDO channel = channelMapper.selectById(id); if (channel == null) { - throw exception(CHANNEL_NOT_EXISTS); + throw exception(CHANNEL_NOT_FOUND); } return channel; } @@ -203,10 +202,10 @@ public class PayChannelServiceImpl implements PayChannelService { private void validPayChannel(PayChannelDO channel) { if (channel == null) { - throw exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND); + throw exception(CHANNEL_NOT_FOUND); } if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) { - throw exception(ErrorCodeConstants.PAY_CHANNEL_IS_DISABLE); + throw exception(CHANNEL_IS_DISABLE); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java index 24e7b7572..f5ebf7961 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java +++ b/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 { new PayDemoOrderDO().setPayStatus(true).setPayTime(LocalDateTime.now()) .setPayChannelCode(payOrder.getChannelCode())); if (updateCount == 0) { - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); + throw exception(DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); } } @@ -137,44 +137,44 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { // 1.1 校验订单是否存在 PayDemoOrderDO order = payDemoOrderMapper.selectById(id); if (order == null) { - throw exception(PAY_DEMO_ORDER_NOT_FOUND); + throw exception(DEMO_ORDER_NOT_FOUND); } // 1.2 校验订单未支付 if (order.getPayStatus()) { log.error("[validateDemoOrderCanPaid][order({}) 不处于待支付状态,请进行处理!order 数据是:{}]", id, toJsonString(order)); - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); + throw exception(DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); } // 1.3 校验支付订单匹配 if (notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号 log.error("[validateDemoOrderCanPaid][order({}) 支付单不匹配({}),请进行处理!order 数据是:{}]", id, payOrderId, toJsonString(order)); - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); } // 2.1 校验支付单是否存在 PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId); if (payOrder == null) { log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 不存在,请进行处理!]", id, payOrderId); - throw exception(PAY_ORDER_NOT_FOUND); + throw exception(ORDER_NOT_FOUND); } // 2.2 校验支付单已支付 if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) { log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]", id, payOrderId, toJsonString(payOrder)); - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS); } // 2.3 校验支付金额一致 if (notEqual(payOrder.getPrice(), order.getPrice())) { log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 支付金额不匹配,请进行处理!order 数据是:{},payOrder 数据是:{}]", id, payOrderId, toJsonString(order), toJsonString(payOrder)); - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH); } // 2.4 校验支付订单匹配(二次) if (notEqual(payOrder.getMerchantOrderId(), id.toString())) { log.error("[validateDemoOrderCanPaid][order({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]", id, payOrderId, toJsonString(payOrder)); - throw exception(PAY_DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); + throw exception(DEMO_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR); } return payOrder; } @@ -203,15 +203,15 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { // 校验订单是否存在 PayDemoOrderDO order = payDemoOrderMapper.selectById(id); if (order == null) { - throw exception(PAY_DEMO_ORDER_NOT_FOUND); + throw exception(DEMO_ORDER_NOT_FOUND); } // 校验订单是否支付 if (!order.getPayStatus()) { - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_NOT_PAID); + throw exception(DEMO_ORDER_REFUND_FAIL_NOT_PAID); } // 校验订单是否已退款 if (order.getPayRefundId() != null) { - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUNDED); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUNDED); } return order; } @@ -229,35 +229,35 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { // 1.1 校验示例订单 PayDemoOrderDO order = payDemoOrderMapper.selectById(id); if (order == null) { - throw exception(PAY_DEMO_ORDER_NOT_FOUND); + throw exception(DEMO_ORDER_NOT_FOUND); } // 1.2 校验退款订单匹配 if (Objects.equals(order.getPayOrderId(), payRefundId)) { log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!order 数据是:{}]", id, payRefundId, toJsonString(order)); - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); } // 2.1 校验退款订单 PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId); if (payRefund == null) { - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND); } // 2.2 if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS); } // 2.3 校验退款金额一致 if (notEqual(payRefund.getRefundPrice(), order.getPrice())) { log.error("[validateDemoOrderCanRefunded][order({}) payRefund({}) 退款金额不匹配,请进行处理!order 数据是:{},payRefund 数据是:{}]", id, payRefundId, toJsonString(order), toJsonString(payRefund)); - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); } // 2.4 校验退款订单匹配(二次) if (notEqual(payRefund.getMerchantOrderId(), id.toString())) { log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]", id, payRefundId, toJsonString(payRefund)); - throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); + throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); } return payRefund; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyService.java index 8b6aba199..fa302e782 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyService.java @@ -1,29 +1,58 @@ package cn.iocoder.yudao.module.pay.service.notify; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; import javax.validation.Valid; +import java.util.List; /** - * 支付通知 Service 接口 + * 回调通知 Service 接口 * * @author 芋道源码 */ public interface PayNotifyService { /** - * 创建支付通知任务 + * 创建回调通知任务 * * @param reqDTO 任务信息 */ void createPayNotifyTask(@Valid PayNotifyTaskCreateReqDTO reqDTO); /** - * 执行支付通知 + * 执行回调通知 * * 注意,该方法提供给定时任务调用。目前是 yudao-server 进行调用 * @return 通知数量 */ int executeNotify() throws InterruptedException; + /** + * 获得回调通知 + * + * @param id 编号 + * @return 回调通知 + */ + PayNotifyTaskDO getNotifyTask(Long id); + + /** + * 获得回调通知分页 + * + * @param pageReqVO 分页查询 + * @return 回调通知分页 + */ + PageResult getNotifyTaskPage(PayNotifyTaskPageReqVO pageReqVO); + + /** + * 获得回调日志列表 + * + * @param taskId 任务编号 + * @return 日志列表 + */ + List getNotifyLogList(Long taskId); + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java index f38c3c0d7..2503e1118 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java +++ b/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; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; @@ -72,15 +74,15 @@ public class PayNotifyServiceImpl implements PayNotifyService { private PayRefundService refundService; @Resource - private PayNotifyTaskMapper payNotifyTaskMapper; + private PayNotifyTaskMapper notifyTaskMapper; @Resource - private PayNotifyLogMapper payNotifyLogMapper; + private PayNotifyLogMapper notifyLogMapper; @Resource(name = NOTIFY_THREAD_POOL_TASK_EXECUTOR) private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Resource - private PayNotifyLockRedisDAO payNotifyLockCoreRedisDAO; + private PayNotifyLockRedisDAO notifyLockCoreRedisDAO; @Resource @Lazy // 循环依赖(自己依赖自己),避免报错 @@ -105,7 +107,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { } // 执行插入 - payNotifyTaskMapper.insert(task); + notifyTaskMapper.insert(task); // 必须在事务提交后,在发起任务,否则 PayNotifyTaskDO 还没入库,就提前回调接入的业务 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @@ -119,7 +121,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { @Override public int executeNotify() throws InterruptedException { // 获得需要通知的任务 - List tasks = payNotifyTaskMapper.selectListByNotify(); + List tasks = notifyTaskMapper.selectListByNotify(); if (CollUtil.isEmpty(tasks)) { return 0; } @@ -164,11 +166,11 @@ public class PayNotifyServiceImpl implements PayNotifyService { */ public void executeNotify(PayNotifyTaskDO task) { // 分布式锁,避免并发问题 - payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { + notifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> { // 校验,当前任务是否已经被通知过 // 虽然已经通过分布式加锁,但是可能同时满足通知的条件,然后都去获得锁。此时,第一个执行完后,第二个还是能拿到锁,然后会再执行一次。 // 因此,此处我们通过第 notifyTimes 通知次数是否匹配来判断 - PayNotifyTaskDO dbTask = payNotifyTaskMapper.selectById(task.getId()); + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); if (ObjectUtil.notEqual(task.getNotifyTimes(), dbTask.getNotifyTimes())) { log.warn("[executeNotifySync][task({}) 任务被忽略,原因是它的通知不是第 ({}) 次,可能是因为并发执行了]", JsonUtils.toJsonString(task), dbTask.getNotifyTimes()); @@ -197,7 +199,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { // 记录 PayNotifyLog 日志 String response = invokeException != null ? ExceptionUtil.getRootCauseMessage(invokeException) : JsonUtils.toJsonString(invokeResult); - payNotifyLogMapper.insert(PayNotifyLogDO.builder().taskId(task.getId()) + notifyLogMapper.insert(PayNotifyLogDO.builder().taskId(task.getId()) .notifyTimes(task.getNotifyTimes() + 1).status(newStatus).response(response).build()); } @@ -250,22 +252,37 @@ public class PayNotifyServiceImpl implements PayNotifyService { // 情况一:调用成功 if (invokeResult != null && invokeResult.isSuccess()) { updateTask.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); - payNotifyTaskMapper.updateById(updateTask); + notifyTaskMapper.updateById(updateTask); return updateTask.getStatus(); } // 情况二:调用失败、调用异常 // 2.1 超过最大回调次数 if (updateTask.getNotifyTimes() >= PayNotifyTaskDO.NOTIFY_FREQUENCY.length) { updateTask.setStatus(PayNotifyStatusEnum.FAILURE.getStatus()); - payNotifyTaskMapper.updateById(updateTask); + notifyTaskMapper.updateById(updateTask); return updateTask.getStatus(); } // 2.2 未超过最大回调次数 updateTask.setNextNotifyTime(addTime(Duration.ofSeconds(PayNotifyTaskDO.NOTIFY_FREQUENCY[updateTask.getNotifyTimes()]))); updateTask.setStatus(invokeException != null ? PayNotifyStatusEnum.REQUEST_FAILURE.getStatus() : PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus()); - payNotifyTaskMapper.updateById(updateTask); + notifyTaskMapper.updateById(updateTask); return updateTask.getStatus(); } + @Override + public PayNotifyTaskDO getNotifyTask(Long id) { + return notifyTaskMapper.selectById(id); + } + + @Override + public PageResult getNotifyTaskPage(PayNotifyTaskPageReqVO pageReqVO) { + return notifyTaskMapper.selectPage(pageReqVO); + } + + @Override + public List getNotifyLogList(Long taskId) { + return notifyLogMapper.selectListByTaskId(taskId); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java deleted file mode 100755 index 2db86a832..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.pay.service.order; - -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -// TODO 芋艿:合并到 PayOrder 里; -/** - * 支付订单 Service 接口 - * - * @author aquan - */ -public interface PayOrderExtensionService { - - /** - * 获得支付订单 - * - * @param id 编号 - * @return 支付订单 - */ - PayOrderExtensionDO getOrderExtension(Long id); - - /** - * 获得支付订单 - * 列表 - * - * @param ids 编号 - * @return 支付订单 - * 列表 - */ - List getOrderExtensionList(Collection ids); - - /** - * 根据订单成功的 扩展订单ID 查询所有的扩展订单转 成 map 返回 - * - * @param successExtensionIdList 订单 ID 集合 - * @return 订单扩展 map 集合 - */ - default Map getOrderExtensionMap(Collection successExtensionIdList) { - List list = getOrderExtensionList(successExtensionIdList); - return CollectionUtils.convertMap(list, PayOrderExtensionDO::getId); - } - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionServiceImpl.java deleted file mode 100755 index a97e3beff..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionServiceImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.pay.service.order; - -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; -import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; - -/** - * 支付订单 Service 实现类 - * - * @author aquan - */ -@Service -@Validated -public class PayOrderExtensionServiceImpl implements PayOrderExtensionService { - - @Resource - private PayOrderExtensionMapper orderExtensionMapper; - - @Override - public PayOrderExtensionDO getOrderExtension(Long id) { - return orderExtensionMapper.selectById(id); - } - - @Override - public List getOrderExtensionList(Collection ids) { - return orderExtensionMapper.selectBatchIds(ids); - } - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java index 8aadac856..3ad204744 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.pay.service.order; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 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; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitReqVO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitRespVO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; -import java.util.Collection; import java.util.List; -import java.util.Map; /** * 支付订单 Service 接口 @@ -49,44 +47,21 @@ public interface PayOrderService { Long getOrderCountByAppId(Long appId); /** - * 获得支付订单 - * 分页 + * 获得支付订单分页 * * @param pageReqVO 分页查询 - * @return 支付订单 - * 分页 + * @return 支付订单分页 */ PageResult getOrderPage(PayOrderPageReqVO pageReqVO); /** - * 获得支付订单 - * 列表, 用于 Excel 导出 + * 获得支付订单列表, 用于 Excel 导出 * * @param exportReqVO 查询条件 - * @return 支付订单 - * 列表 + * @return 支付订单列表 */ List getOrderList(PayOrderExportReqVO exportReqVO); - /** - * 根据 ID 集合获取只包含商品名称的订单集合 - * - * @param idList 订单 ID 集合 - * @return 只包含商品名称的订单集合 - */ - List getOrderSubjectList(Collection idList); - - /** - * 根据订单 ID 集合获取订单商品名称Map集合 - * - * @param ids 订单 ID 集合 - * @return 订单商品 map 集合 - */ - default Map getOrderSubjectMap(Collection ids) { - List list = getOrderSubjectList(ids); - return CollectionUtils.convertMap(list, PayOrderDO::getId); - } - /** * 创建支付单 * @@ -122,4 +97,12 @@ public interface PayOrderService { */ void updateOrderRefundPrice(Long id, Integer incrRefundPrice); + /** + * 获得支付订单 + * + * @param id 编号 + * @return 支付订单 + */ + PayOrderExtensionDO getOrderExtension(Long id); + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index 0e5fee42e..64ecaff94 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -1,13 +1,11 @@ package cn.iocoder.yudao.module.pay.service.order; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Pair; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; -import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 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; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; +import cn.iocoder.yudao.module.pay.util.MoneyUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collection; import java.util.List; import java.util.Objects; @@ -69,6 +66,8 @@ public class PayOrderServiceImpl implements PayOrderService { private PayOrderMapper orderMapper; @Resource private PayOrderExtensionMapper orderExtensionMapper; + @Resource + private PayNoRedisDAO noRedisDAO; @Resource private PayAppService appService; @@ -102,12 +101,6 @@ public class PayOrderServiceImpl implements PayOrderService { return orderMapper.selectList(exportReqVO); } - // TODO @艿艿:需要优化。不确定这个方法的作用 - @Override - public List getOrderSubjectList(Collection idList) { - return orderMapper.findByIdListQueryOrderSubject(idList); - } - @Override public Long createOrder(PayOrderCreateReqDTO reqDTO) { // 校验 App @@ -125,11 +118,11 @@ public class PayOrderServiceImpl implements PayOrderService { // 创建支付交易单 order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId()) // 商户相关字段 - .setNotifyUrl(app.getOrderNotifyUrl()).setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) + .setNotifyUrl(app.getOrderNotifyUrl()) // 订单相关字段 .setStatus(PayOrderStatusEnum.WAITING.getStatus()) // 退款相关字段 - .setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0); + .setRefundPrice(0); orderMapper.insert(order); return order.getId(); } @@ -143,8 +136,9 @@ public class PayOrderServiceImpl implements PayOrderService { PayClient client = payClientFactory.getPayClient(channel.getId()); // 2. 插入 PayOrderExtensionDO + String no = noRedisDAO.generate(payProperties.getOrderNoPrefix()); PayOrderExtensionDO orderExtension = PayOrderConvert.INSTANCE.convert(reqVO, userIp) - .setOrderId(order.getId()).setNo(generateOrderExtensionNo()) + .setOrderId(order.getId()).setNo(no) .setChannelId(channel.getId()).setChannelCode(channel.getCode()) .setStatus(PayOrderStatusEnum.WAITING.getStatus()); orderExtensionMapper.insert(orderExtension); @@ -162,10 +156,10 @@ public class PayOrderServiceImpl implements PayOrderService { // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 if (unifiedOrderResp != null) { - notifyPayOrder(channel, unifiedOrderResp); + getSelf().notifyOrder(channel, unifiedOrderResp); // 如有渠道错误码,则抛出业务异常,提示用户 if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) { - throw exception(PAY_ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(), + throw exception(ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(), unifiedOrderResp.getChannelErrorMsg()); } // 此处需要读取最新的状态 @@ -177,13 +171,13 @@ public class PayOrderServiceImpl implements PayOrderService { private PayOrderDO validateOrderCanSubmit(Long id) { PayOrderDO order = orderMapper.selectById(id); if (order == null) { // 是否存在 - throw exception(PAY_ORDER_NOT_FOUND); + throw exception(ORDER_NOT_FOUND); } if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + throw exception(ORDER_STATUS_IS_NOT_WAITING); } if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期 - throw exception(PAY_ORDER_IS_EXPIRED); + throw exception(ORDER_IS_EXPIRED); } return order; } @@ -196,7 +190,7 @@ public class PayOrderServiceImpl implements PayOrderService { PayClient client = payClientFactory.getPayClient(channel.getId()); if (client == null) { log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); - throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); + throw exception(CHANNEL_NOT_FOUND); } return channel; } @@ -211,61 +205,22 @@ public class PayOrderServiceImpl implements PayOrderService { return payProperties.getOrderNotifyUrl() + "/" + channel.getId(); } - private String generateOrderExtensionNo() { -// wx -// 2014 -// 10 -// 27 -// 20 -// 09 -// 39 -// 5522657 -// a690389285100 - // 目前的算法 - // 时间序列,年月日时分秒 14 位 - // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整 - return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列 - RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒 - ; - } - @Override - @Transactional(rollbackFor = Exception.class) public void notifyOrder(Long channelId, PayOrderRespDTO notify) { // 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(channelId); // 更新支付订单为已支付 - TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify)); + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyOrder(channel, notify)); } - @Override - public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) { - PayOrderDO order = orderMapper.selectById(id); - if (order == null) { - throw exception(PAY_ORDER_NOT_FOUND); - } - if (!PayOrderStatusEnum.isSuccess(order.getStatus())) { - throw exception(PAY_REFUND_PRICE_EXCEED); - } - if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) { - throw exception(PAY_REFUND_PRICE_EXCEED); - } - - // 更新订单 - PayOrderDO updateObj = new PayOrderDO() - .setRefundPrice(order.getRefundPrice() + incrRefundPrice) - .setRefundTimes(order.getRefundTimes() + 1); - if (Objects.equals(updateObj.getRefundPrice(), order.getPrice())) { - updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) - .setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()); - } else { - updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) - .setRefundStatus(PayOrderRefundStatusEnum.PART.getStatus()); - } - orderMapper.updateByIdAndStatus(id, PayOrderStatusEnum.SUCCESS.getStatus(), updateObj); - } - - private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { + /** + * 通知并更新订单的支付结果 + * + * @param channel 支付渠道 + * @param notify 通知 + */ + @Transactional(rollbackFor = Exception.class) // 注意,如果是方法内调用该方法,需要通过 getSelf().notifyPayOrder(channel, notify) 调用,否则事务不生效 + public void notifyOrder(PayChannelDO channel, PayOrderRespDTO notify) { // 情况一:支付成功的回调 if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { notifyOrderSuccess(channel, notify); @@ -275,20 +230,22 @@ public class PayOrderServiceImpl implements PayOrderService { if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) { notifyOrderClosed(channel, notify); } + // 情况三:WAITING:无需处理 + // 情况四:REFUND:通过退款回调处理 } private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { // 1. 更新 PayOrderExtensionDO 支付成功 - PayOrderExtensionDO orderExtension = updateOrderExtensionSuccess(notify); + PayOrderExtensionDO orderExtension = updateOrderSuccess(notify); // 2. 更新 PayOrderDO 支付成功 - Pair order = updateOrderExtensionSuccess(channel, orderExtension, notify); - if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 + Boolean paid = updateOrderSuccess(channel, orderExtension, notify); + if (paid) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 return; } // 3. 插入支付通知记录 notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getValue().getId()).build()); + .type(PayNotifyTypeEnum.ORDER.getType()).dataId(orderExtension.getOrderId()).build()); } /** @@ -297,25 +254,25 @@ public class PayOrderServiceImpl implements PayOrderService { * @param notify 通知 * @return PayOrderExtensionDO 对象 */ - private PayOrderExtensionDO updateOrderExtensionSuccess(PayOrderRespDTO notify) { + private PayOrderExtensionDO updateOrderSuccess(PayOrderRespDTO notify) { // 1. 查询 PayOrderExtensionDO PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); if (orderExtension == null) { - throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); + throw exception(ORDER_EXTENSION_NOT_FOUND); } if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新]", orderExtension.getId()); return orderExtension; } if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } // 2. 更新 PayOrderExtensionDO int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(), PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build()); if (updateCounts == 0) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } log.info("[updateOrderExtensionSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId()); return orderExtension; @@ -327,37 +284,37 @@ public class PayOrderServiceImpl implements PayOrderService { * @param channel 支付渠道 * @param orderExtension 支付拓展单 * @param notify 通知回调 - * @return key:是否之前已经成功回调 - * value:PayOrderDO 对象 + * @return 是否之前已经成功回调 */ - private Pair updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, - PayOrderRespDTO notify) { + private Boolean updateOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, + PayOrderRespDTO notify) { // 1. 判断 PayOrderDO 是否处于待支付 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); if (order == null) { - throw exception(PAY_ORDER_NOT_FOUND); + throw exception(ORDER_NOT_FOUND); } if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 - && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) { + && Objects.equals(order.getExtensionId(), orderExtension.getId())) { log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新]", order.getId()); - return Pair.of(true, order); + return true; } if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + throw exception(ORDER_STATUS_IS_NOT_WAITING); } // 2. 更新 PayOrderDO int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(), PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()) .channelId(channel.getId()).channelCode(channel.getCode()) - .successTime(notify.getSuccessTime()).successExtensionId(orderExtension.getId()) + .successTime(notify.getSuccessTime()).extensionId(orderExtension.getId()).no(orderExtension.getNo()) .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId()) - .notifyTime(LocalDateTime.now()).build()); + .channelFeeRate(channel.getFeeRate()).channelFeePrice(MoneyUtils.calculateRatePrice(order.getPrice(), channel.getFeeRate())) + .build()); if (updateCounts == 0) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); + throw exception(ORDER_STATUS_IS_NOT_WAITING); } log.info("[updateOrderExtensionSuccess][支付订单({}) 更新为已支付]", order.getId()); - return Pair.of(false, order); + return false; } private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) { @@ -368,7 +325,7 @@ public class PayOrderServiceImpl implements PayOrderService { // 1. 查询 PayOrderExtensionDO PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); if (orderExtension == null) { - throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); + throw exception(ORDER_EXTENSION_NOT_FOUND); } if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { // 如果已经是关闭,直接返回,不用重复更新 log.info("[updateOrderExtensionClosed][支付拓展单({}) 已经是支付关闭,无需更新]", orderExtension.getId()); @@ -380,7 +337,7 @@ public class PayOrderServiceImpl implements PayOrderService { return; } if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } // 2. 更新 PayOrderExtensionDO @@ -388,9 +345,46 @@ public class PayOrderServiceImpl implements PayOrderService { PayOrderExtensionDO.builder().status(PayOrderStatusEnum.CLOSED.getStatus()).channelNotifyData(toJsonString(notify)) .channelErrorCode(notify.getChannelErrorCode()).channelErrorMsg(notify.getChannelErrorMsg()).build()); if (updateCounts == 0) { // 校验状态,必须是待支付 - throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + throw exception(ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } log.info("[updateOrderExtensionClosed][支付拓展单({}) 更新为支付关闭]", orderExtension.getId()); } + @Override + public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) { + PayOrderDO order = orderMapper.selectById(id); + if (order == null) { + throw exception(ORDER_NOT_FOUND); + } + if (!PayOrderStatusEnum.isSuccessOrRefund(order.getStatus())) { + throw exception(ORDER_REFUND_FAIL_STATUS_ERROR); + } + if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) { + throw exception(REFUND_PRICE_EXCEED); + } + + // 更新订单 + PayOrderDO updateObj = new PayOrderDO() + .setRefundPrice(order.getRefundPrice() + incrRefundPrice) + .setStatus(PayOrderStatusEnum.REFUND.getStatus()); + int updateCount = orderMapper.updateByIdAndStatus(id, order.getStatus(), updateObj); + if (updateCount == 0) { + throw exception(ORDER_REFUND_FAIL_STATUS_ERROR); + } + } + + @Override + public PayOrderExtensionDO getOrderExtension(Long id) { + return orderExtensionMapper.selectById(id); + } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private PayOrderServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java index 7914b70f6..41f800393 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java +++ b/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; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 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; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; -import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; -import cn.iocoder.yudao.module.pay.service.order.PayOrderExtensionService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -43,6 +40,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_FOUND; /** * 退款订单 Service 实现类 @@ -66,8 +64,6 @@ public class PayRefundServiceImpl implements PayRefundService { @Resource private PayOrderService orderService; @Resource - private PayOrderExtensionService orderExtensionService; - @Resource private PayAppService appService; @Resource private PayChannelService channelService; @@ -106,13 +102,13 @@ public class PayRefundServiceImpl implements PayRefundService { PayClient client = payClientFactory.getPayClient(channel.getId()); if (client == null) { log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); - throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); + throw exception(CHANNEL_NOT_FOUND); } // 1.4 校验退款订单是否已经存在 PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId( app.getId(), reqDTO.getMerchantRefundId()); if (refund != null) { - throw exception(ErrorCodeConstants.PAY_REFUND_EXISTS); + throw exception(ErrorCodeConstants.REFUND_EXISTS); } // 2.1 插入退款单 @@ -120,7 +116,7 @@ public class PayRefundServiceImpl implements PayRefundService { .setNo(generateRefundNo()).setOrderId(order.getId()) .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode()) // 商户相关的字段 - .setNotifyUrl(app.getRefundNotifyUrl()).setNotifyStatus(PayNotifyStatusEnum.WAITING.getStatus()) + .setNotifyUrl(app.getRefundNotifyUrl()) // 渠道相关字段 .setChannelOrderNo(order.getChannelOrderNo()) // 退款相关字段 @@ -128,7 +124,7 @@ public class PayRefundServiceImpl implements PayRefundService { .setPayPrice(order.getPrice()).setRefundPrice(reqDTO.getPrice()); refundMapper.insert(refund); // 2.2 向渠道发起退款申请 - PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); + PayOrderExtensionDO orderExtension = orderService.getOrderExtension(order.getExtensionId()); PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO() .setPayPrice(order.getPrice()) .setRefundPrice(reqDTO.getPrice()) @@ -136,7 +132,7 @@ public class PayRefundServiceImpl implements PayRefundService { .setOutRefundNo(refund.getNo()) .setNotifyUrl(genChannelRefundNotifyUrl(channel)) .setReason(reqDTO.getReason()); - PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // TODO 增加一个 channelErrorCode、channelErrorMsg 字段 + PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // 2.3 处理退款返回 notifyRefund(channel, refundRespDTO); @@ -153,25 +149,21 @@ public class PayRefundServiceImpl implements PayRefundService { private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) { PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId()); if (order == null) { - throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); + throw exception(ErrorCodeConstants.ORDER_NOT_FOUND); } // 校验状态,必须是支付状态 if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS); + throw exception(ErrorCodeConstants.ORDER_STATUS_IS_NOT_SUCCESS); } - // 是否已经全额退款 - if (PayOrderRefundStatusEnum.ALL.getStatus().equals(order.getRefundStatus())) { - throw exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED); - } // 校验金额 退款金额不能大于原定的金额 if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){ - throw exception(ErrorCodeConstants.PAY_REFUND_PRICE_EXCEED); + throw exception(ErrorCodeConstants.REFUND_PRICE_EXCEED); } // 是否有退款中的订单 if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(), PayRefundStatusEnum.WAITING.getStatus()) > 0) { - throw exception(ErrorCodeConstants.PAY_REFUND_HAS_REFUNDING); + throw exception(ErrorCodeConstants.REFUND_HAS_REFUNDING); } return order; } @@ -205,7 +197,6 @@ public class PayRefundServiceImpl implements PayRefundService { } @Override - @Transactional(rollbackFor = Exception.class) public void notifyRefund(Long channelId, PayRefundRespDTO notify) { // 校验支付渠道是否有效 channelService.validPayChannel(channelId); @@ -217,28 +208,33 @@ public class PayRefundServiceImpl implements PayRefundService { TenantUtils.execute(channel.getTenantId(), () -> notifyRefund(channel, notify)); } + // TODO 芋艿:事务问题 private void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) { + // 情况一:退款成功 if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { notifyRefundSuccess(channel, notify); - } else { + return; + } + // 情况二:退款失败 + if (PayRefundStatusRespEnum.isFailure(notify.getStatus())) { notifyRefundFailure(channel, notify); } } - private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) { + public void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) { // 1.1 查询 PayRefundDO PayRefundDO refund = refundMapper.selectByAppIdAndNo( channel.getAppId(), notify.getOutRefundNo()); if (refund == null) { - throw exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); + throw exception(ErrorCodeConstants.REFUND_NOT_FOUND); } if (PayRefundStatusEnum.isSuccess(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 + log.info("[notifyRefundSuccess][退款订单({}) 已经是退款成功,无需更新]", refund.getId()); return; } if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) { - throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING); + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING); } - // 1.2 更新 PayRefundDO PayRefundDO updateRefundObj = new PayRefundDO() .setSuccessTime(notify.getSuccessTime()) @@ -247,19 +243,48 @@ public class PayRefundServiceImpl implements PayRefundService { .setChannelNotifyData(toJsonString(notify)); int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj); if (updateCounts == 0) { // 校验状态,必须是等待状态 - throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING); + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING); } + log.info("[notifyRefundSuccess][退款订单({}) 更新为退款成功]", refund.getId()); // 2. 更新订单 orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice()); - // 3. 插入退款通知记录 + // 3. 插入退款通知记录 TODO 芋艿:退款成功 notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); } - private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) { - // TODO 芋艿:未实现 + @Transactional(rollbackFor = Exception.class) + public void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) { + // 1.1 查询 PayRefundDO + PayRefundDO refund = refundMapper.selectByAppIdAndNo( + channel.getAppId(), notify.getOutRefundNo()); + if (refund == null) { + throw exception(ErrorCodeConstants.REFUND_NOT_FOUND); + } + if (PayRefundStatusEnum.isFailure(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 + log.info("[notifyRefundSuccess][退款订单({}) 已经是退款关闭,无需更新]", refund.getId()); + return; + } + if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) { + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING); + } + // 1.2 更新 PayRefundDO + PayRefundDO updateRefundObj = new PayRefundDO() + .setChannelRefundNo(notify.getChannelRefundNo()) + .setStatus(PayRefundStatusEnum.FAILURE.getStatus()) + .setChannelNotifyData(toJsonString(notify)) + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg()); + int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj); + if (updateCounts == 0) { // 校验状态,必须是等待状态 + throw exception(ErrorCodeConstants.REFUND_STATUS_IS_NOT_WAITING); + } + log.info("[notifyRefundFailure][退款订单({}) 更新为退款失败]", refund.getId()); + + // 2. 插入退款通知记录 TODO 芋艿:退款失败 + notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/util/MoneyUtils.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/util/MoneyUtils.java new file mode 100644 index 000000000..76cf5bfa3 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/util/MoneyUtils.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.pay.util; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 金额工具类 + * + * @author 芋道源码 + */ +public class MoneyUtils { + + /** + * 计算百分比金额,四舍五入 + * + * @param price 金额 + * @param rate 百分比,例如说 56.77% 则传入 56.77 + * @return 百分比金额 + */ + public static Integer calculateRatePrice(Integer price, Double rate) { + return new BigDecimal(price) + .multiply(BigDecimal.valueOf(rate)) // 乘以 + .setScale(0, RoundingMode.HALF_UP) // 四舍五入 + .intValue(); + } + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java index f25bf8942..1934cfe97 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java +++ b/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 { PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus()))); // 调用, 并断言异常 - assertServiceException(() -> appService.updateApp(reqVO), PAY_APP_NOT_FOUND); + assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_FOUND); } @Test @@ -130,7 +130,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { Long id = randomLongId(); // 调用, 并断言异常 - assertServiceException(() -> appService.deleteApp(id), PAY_APP_NOT_FOUND); + assertServiceException(() -> appService.deleteApp(id), APP_NOT_FOUND); } @Test @@ -144,7 +144,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { when(orderService.getOrderCountByAppId(eq(id))).thenReturn(10L); // 调用, 并断言异常 - assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_ORDER_CANT_DELETE); + assertServiceException(() -> appService.deleteApp(id), APP_EXIST_ORDER_CANT_DELETE); } @Test @@ -158,7 +158,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { when(refundService.getRefundCountByAppId(eq(id))).thenReturn(10L); // 调用, 并断言异常 - assertServiceException(() -> appService.deleteApp(id), PAY_APP_EXIST_REFUND_CANT_DELETE); + assertServiceException(() -> appService.deleteApp(id), APP_EXIST_REFUND_CANT_DELETE); } @Test @@ -239,7 +239,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { @Test public void testValidPayApp_notFound() { - assertServiceException(() -> appService.validPayApp(randomLongId()), PAY_APP_NOT_FOUND); + assertServiceException(() -> appService.validPayApp(randomLongId()), APP_NOT_FOUND); } @Test @@ -252,7 +252,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { Long id = dbApp.getId(); // 调用,并断言异常 - assertServiceException(() -> appService.validPayApp(id), PAY_APP_IS_DISABLE); + assertServiceException(() -> appService.validPayApp(id), APP_IS_DISABLE); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java index 0a5d33495..2304257ed 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java +++ b/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; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_EXIST_SAME_CHANNEL_ERROR; -import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_EXISTS; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; import static org.junit.jupiter.api.Assertions.*; @Import({PayChannelServiceImpl.class}) @@ -51,6 +50,40 @@ public class PayChannelServiceTest extends BaseDbUnitTest { channelService.setChannelCache(null); } + @Test + public void testInitLocalCache() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, + o -> o.setConfig(randomWxPayClientConfig())); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + + // 调用 + channelService.initLocalCache(); + // 校验缓存 + assertEquals(1, channelService.getChannelCache().size()); + assertEquals(dbChannel, channelService.getChannelCache().get(0)); + } + + @Test + public void testRefreshLocalCache() { + // mock 数据 01 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, + o -> o.setConfig(randomWxPayClientConfig())); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + channelService.initLocalCache(); + // mock 数据 02 + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, + o -> o.setConfig(randomWxPayClientConfig())); + channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据 + + // 调用 + channelService.refreshLocalCache(); + // 校验缓存 + assertEquals(2, channelService.getChannelCache().size()); + assertEquals(dbChannel, channelService.getChannelCache().get(0)); + assertEquals(dbChannel02, channelService.getChannelCache().get(1)); + } + @Test public void testCreateChannel_success() { // 准备参数 @@ -125,7 +158,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { }); // 调用, 并断言异常 - assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS); + assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_FOUND); } @Test @@ -153,7 +186,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { Long id = randomLongId(); // 调用, 并断言异常 - assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS); + assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_FOUND); } @Test @@ -214,6 +247,101 @@ public class PayChannelServiceTest extends BaseDbUnitTest { assertPojoEquals(channel, dbChannel); } + @Test + public void testValidPayChannel_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_NOT_FOUND); + } + + @Test + public void testValidPayChannel_isDisable() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + }); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbChannel.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_IS_DISABLE); + } + + @Test + public void testValidPayChannel_success() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbChannel.getId(); + + // 调用 + PayChannelDO channel = channelService.validPayChannel(id); + // 断言异常 + assertPojoEquals(channel, dbChannel); + } + + @Test + public void testValidPayChannel_appIdAndCode() { + // mock 数据 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long appId = dbChannel.getAppId(); + String code = dbChannel.getCode(); + + // 调用 + PayChannelDO channel = channelService.validPayChannel(appId, code); + // 断言异常 + assertPojoEquals(channel, dbChannel); + } + + @Test + public void testGetEnableChannelList() { + // 准备参数 + Long appId = randomLongId(); + // mock 数据 01(enable 不匹配) + PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + }); + channelMapper.insert(dbChannel01);// @Sql: 先插入出一条存在的数据 + // mock 数据 02(appId 不匹配) + PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据 + // mock 数据 03 + PayChannelDO dbChannel03 = randomPojo(PayChannelDO.class, o -> { + o.setCode(PayChannelEnum.ALIPAY_APP.getCode()); + o.setConfig(randomAlipayPayClientConfig()); + o.setAppId(appId); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + channelMapper.insert(dbChannel03);// @Sql: 先插入出一条存在的数据 + + // 调用 + List channel = channelService.getEnableChannelList(appId); + // 断言异常 + assertPojoEquals(channel, dbChannel03); + } + public WxPayClientConfig randomWxPayClientConfig() { return new WxPayClientConfig() .setAppId(randomString()) diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java index c225fa5b9..e81568480 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java @@ -1,48 +1,73 @@ package cn.iocoder.yudao.module.pay.service.order; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.RandomUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.pay.config.PayProperties; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitRespVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; +import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; +import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; +import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import java.time.LocalDateTime; +import java.time.Duration; import java.util.List; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; /** * {@link PayOrderServiceImpl} 的单元测试类 * * @author 芋艿 */ -@Import({PayOrderServiceImpl.class}) -public class PayOrderServiceTest extends BaseDbUnitTest { +@Import({PayOrderServiceImpl.class, PayNoRedisDAO.class}) +public class PayOrderServiceTest extends BaseDbAndRedisUnitTest { @Resource private PayOrderServiceImpl orderService; @Resource private PayOrderMapper orderMapper; + @Resource + private PayOrderExtensionMapper orderExtensionMapper; @MockBean private PayClientFactory payClientFactory; @@ -55,140 +80,92 @@ public class PayOrderServiceTest extends BaseDbUnitTest { @MockBean private PayNotifyService notifyService; - public String generateNo() { - return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + RandomUtil.randomInt(100000, 999999); + @BeforeEach + public void setUp() { + when(properties.getOrderNotifyUrl()).thenReturn("http://127.0.0.1"); } @Test public void testGetOrderPage() { - - String merchantOrderId = generateNo(); - String channelOrderId = generateNo(); - // mock 数据 PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到 o.setAppId(1L); - o.setChannelId(1L); o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); - o.setMerchantOrderId(merchantOrderId); - o.setSubject("灿灿子的炸弹猫"); - o.setBody("斌斌子送给灿灿子的炸弹猫"); - o.setNotifyUrl("https://hc.com/lbh"); - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); - o.setPrice(10000); - o.setChannelFeeRate(0.01); - o.setChannelFeePrice(1L); + o.setMerchantOrderId("110"); + o.setChannelOrderNo("220"); + o.setNo("330"); o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); - o.setUserIp("127.0.0.1"); - o.setCreateTime(LocalDateTime.of(2018, 1, 1, 10, 1, 0)); - o.setExpireTime(LocalDateTime.of(2018, 1, 1, 10, 30, 0)); - o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2)); - o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); - o.setSuccessExtensionId(1L); - o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); - o.setRefundTimes(0); - o.setRefundPrice(0); - o.setChannelUserId("1008611"); - o.setChannelOrderNo(channelOrderId); - o.setUpdateTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); + o.setCreateTime(buildTime(2018, 1, 15)); }); orderMapper.insert(dbOrder); // 测试 appId 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setAppId(2L))); - // 测试 channelId 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelId(2L))); // 测试 channelCode 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); // 测试 merchantOrderId 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(generateNo()))); - // 测试 notifyStatus 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(randomString()))); + // 测试 channelOrderNo 不匹配 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelOrderNo(randomString()))); + // 测试 no 不匹配 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNo(randomString()))); // 测试 status 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); - // 测试 refundStatus 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10, - 1)))); + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(buildTime(2019, 1, 1)))); // 准备参数 PayOrderPageReqVO reqVO = new PayOrderPageReqVO(); reqVO.setAppId(1L); - reqVO.setChannelId(1L); reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); - reqVO.setMerchantOrderId(merchantOrderId); - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); + reqVO.setMerchantOrderId("11"); + reqVO.setChannelOrderNo("22"); + reqVO.setNo("33"); reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); - reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); - reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)})); + reqVO.setCreateTime(buildBetweenTime(2018, 1, 10, 2018, 1, 30)); + // 调用 PageResult pageResult = orderService.getOrderPage(reqVO); // 断言 assertEquals(1, pageResult.getTotal()); assertEquals(1, pageResult.getList().size()); assertPojoEquals(dbOrder, pageResult.getList().get(0)); - // assertEquals(0, dbOrder.getUpdateTime().compareTo(pageResult.getList().get(0).getUpdateTime())); } @Test public void testGetOrderList() { // mock 数据 - String merchantOrderId = generateNo(); - String channelOrderId = generateNo(); PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到 o.setAppId(1L); - o.setChannelId(1L); o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); - o.setMerchantOrderId(merchantOrderId); - o.setSubject("灿灿子的炸弹猫"); - o.setBody("斌斌子送给灿灿子的炸弹猫"); - o.setNotifyUrl("https://hc.com/lbh"); - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); - o.setPrice(10000); - o.setChannelFeeRate(0.01); - o.setChannelFeePrice(1L); + o.setMerchantOrderId("110"); + o.setChannelOrderNo("220"); + o.setNo("330"); o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); - o.setUserIp("127.0.0.1"); - o.setCreateTime(LocalDateTime.of(2018, 1, 1, 10, 1, 0)); - o.setExpireTime(LocalDateTime.of(2018, 1, 1, 10, 30, 0)); - o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2)); - o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); - o.setSuccessExtensionId(1L); - o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); - o.setRefundTimes(0); - o.setRefundPrice(0); - o.setChannelUserId("1008611"); - o.setChannelOrderNo(channelOrderId); - o.setUpdateTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); - + o.setCreateTime(buildTime(2018, 1, 15)); }); orderMapper.insert(dbOrder); // 测试 appId 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setAppId(2L))); - // 测试 channelId 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelId(2L))); // 测试 channelCode 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); // 测试 merchantOrderId 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(generateNo()))); - // 测试 notifyStatus 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setMerchantOrderId(randomString()))); + // 测试 channelOrderNo 不匹配 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setChannelOrderNo(randomString()))); + // 测试 no 不匹配 + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNo(randomString()))); // 测试 status 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); - // 测试 refundStatus 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10, - 1)))); + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(buildTime(2019, 1, 1)))); // 准备参数 PayOrderExportReqVO reqVO = new PayOrderExportReqVO(); reqVO.setAppId(1L); - reqVO.setChannelId(1L); reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); - reqVO.setMerchantOrderId(merchantOrderId); - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); + reqVO.setMerchantOrderId("11"); + reqVO.setChannelOrderNo("22"); + reqVO.setNo("33"); reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); - reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); - reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)})); + reqVO.setCreateTime(buildBetweenTime(2018, 1, 10, 2018, 1, 30)); // 调用 List list = orderService.getOrderList(reqVO); @@ -197,4 +174,393 @@ public class PayOrderServiceTest extends BaseDbUnitTest { assertPojoEquals(dbOrder, list.get(0)); } + @Test + public void testCreateOrder_success() { + // mock 参数 + PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class, + o -> o.setAppId(1L).setMerchantOrderId("10") + .setSubject(randomString()).setBody(randomString())); + // mock 方法 + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L).setOrderNotifyUrl("http://127.0.0.1")); + when(appService.validPayApp(eq(reqDTO.getAppId()))).thenReturn(app); + + // 调用 + Long orderId = orderService.createOrder(reqDTO); + // 断言 + PayOrderDO order = orderMapper.selectById(orderId); + assertPojoEquals(order, reqDTO); + assertEquals(order.getAppId(), 1L); + assertEquals(order.getNotifyUrl(), "http://127.0.0.1"); + assertEquals(order.getStatus(), PayOrderStatusEnum.WAITING.getStatus()); + assertEquals(order.getRefundPrice(), 0); + } + + @Test + public void testCreateOrder_exists() { + // mock 参数 + PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class, + o -> o.setAppId(1L).setMerchantOrderId("10")); + // mock 数据 + PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> o.setAppId(1L).setMerchantOrderId("10")); + orderMapper.insert(dbOrder); + + // 调用 + Long orderId = orderService.createOrder(reqDTO); + // 断言 + PayOrderDO order = orderMapper.selectById(orderId); + assertPojoEquals(dbOrder, order); + } + + @Test + public void testSubmitOrder_notFound() { + // 准备参数 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), ORDER_NOT_FOUND); + } + + @Test + public void testSubmitOrder_notWaiting() { + // mock 数据(order) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + orderMapper.insert(order); + // 准备参数 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), ORDER_STATUS_IS_NOT_WAITING); + } + + @Test + public void testSubmitOrder_expired() { + // mock 数据(order) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setExpireTime(addTime(Duration.ofDays(-1)))); + orderMapper.insert(order); + // 准备参数 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId())); + String userIp = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), ORDER_IS_EXPIRED); + } + + @Test + public void testSubmitOrder_channelNotFound() { + // mock 数据(order) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); + orderMapper.insert(order); + // 准备参数 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + String userIp = randomString(); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq(1L))).thenReturn(app); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) + .thenReturn(channel); + + // 调用, 并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), CHANNEL_NOT_FOUND); + } + + @Test // 调用 unifiedOrder 接口,返回存在渠道错误 + public void testSubmitOrder_channelError() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // mock 数据(order) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); + orderMapper.insert(order); + // 准备参数 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + String userIp = randomString(); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq(1L))).thenReturn(app); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) + .thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(payClientFactory.getPayClient(eq(10L))).thenReturn(client); + // mock 方法() + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> + o.setChannelErrorCode("001").setChannelErrorMsg("模拟异常")); + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> { + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo()); + assertThat(payOrderUnifiedReqDTO) + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10", + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime()); + return true; + }))).thenReturn(unifiedOrderResp); + + // 调用,并断言异常 + assertServiceException(() -> orderService.submitOrder(reqVO, userIp), + ORDER_SUBMIT_CHANNEL_ERROR, "001", "模拟异常"); + // 断言,数据记录(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); + assertNotNull(orderExtension); + assertThat(orderExtension).extracting("no", "orderId").isNotNull(); + assertThat(orderExtension) + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", + "channelErrorCode", "channelErrorMsg", "channelNotifyData") + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), + null, null, null); + } + } + + @Test + public void testSubmitOrder_success() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + + // mock 数据(order) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setAppId(1L).setExpireTime(addTime(Duration.ofDays(1)))); + orderMapper.insert(order); + // 准备参数 + PayOrderSubmitReqVO reqVO = randomPojo(PayOrderSubmitReqVO.class, o -> o.setId(order.getId()) + .setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())); + String userIp = randomString(); + // mock 方法(app) + PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L)); + when(appService.validPayApp(eq(1L))).thenReturn(app); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setCode(PayChannelEnum.ALIPAY_APP.getCode())); + when(channelService.validPayChannel(eq(1L), eq(PayChannelEnum.ALIPAY_APP.getCode()))) + .thenReturn(channel); + // mock 方法(client) + PayClient client = mock(PayClient.class); + when(payClientFactory.getPayClient(eq(10L))).thenReturn(client); + // mock 方法() + PayOrderRespDTO unifiedOrderResp = randomPojo(PayOrderRespDTO.class, o -> o.setChannelErrorCode(null).setChannelErrorMsg(null) + .setDisplayMode(PayOrderDisplayModeEnum.URL.getMode()).setDisplayContent("tudou")); + when(client.unifiedOrder(argThat(payOrderUnifiedReqDTO -> { + assertNotNull(payOrderUnifiedReqDTO.getOutTradeNo()); + assertThat(payOrderUnifiedReqDTO) + .extracting("subject", "body", "notifyUrl", "returnUrl", "price", "expireTime") + .containsExactly(order.getSubject(), order.getBody(), "http://127.0.0.1/10", + reqVO.getReturnUrl(), order.getPrice(), order.getExpireTime()); + return true; + }))).thenReturn(unifiedOrderResp); + + // 调用 + PayOrderSubmitRespVO result = orderService.submitOrder(reqVO, userIp); + // 断言,数据记录(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectOne(null); + assertNotNull(orderExtension); + assertThat(orderExtension).extracting("no", "orderId").isNotNull(); + assertThat(orderExtension) + .extracting("channelId", "channelCode","userIp" ,"status", "channelExtras", + "channelErrorCode", "channelErrorMsg", "channelNotifyData") + .containsExactly(10L, PayChannelEnum.ALIPAY_APP.getCode(), userIp, + PayOrderStatusEnum.WAITING.getStatus(), reqVO.getChannelExtras(), + null, null, null); + // 断言,返回(PayOrderSubmitRespVO) + assertThat(result) + .extracting("status", "displayMode", "displayContent") + .containsExactly(PayOrderStatusEnum.WAITING.getStatus(), PayOrderDisplayModeEnum.URL.getMode(), "tudou"); + // 断言,调用 + verify(payOrderServiceImpl).notifyOrder(same(channel), same(unifiedOrderResp)); + } + } + + @Test + public void testNotifyOrder_channelId() { + PayOrderServiceImpl payOrderServiceImpl = mock(PayOrderServiceImpl.class); + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayOrderServiceImpl.class))) + .thenReturn(payOrderServiceImpl); + // 准备参数 + Long channelId = 10L; + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class); + // mock 方法(channel) + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + when(channelService.validPayChannel(eq(10L))).thenReturn(channel); + + // 调用 + orderService.notifyOrder(channelId, notify); + // 断言 + verify(payOrderServiceImpl).notifyOrder(same(channel), same(notify)); + } + } + + @Test + public void testNotifyOrderSuccess_orderExtension_notFound() { + // 准备参数 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + ORDER_EXTENSION_NOT_FOUND); + } + + @Test + public void testNotifyOrderSuccess_orderExtension_closed() { + // mock 数据(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备参数 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + } + + @Test + public void testNotifyOrderSuccess_order_notFound() { + // mock 数据(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110")); + orderExtensionMapper.insert(orderExtension); + // 准备参数 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + ORDER_NOT_FOUND); + // 断言 PayOrderExtensionDO :数据更新被回滚 + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + } + + @Test + public void testNotifyOrderSuccess_order_closed() { + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.CLOSED.getStatus()); + } + + @Test + public void testNotifyOrderSuccess_order_refund() { + testNotifyOrderSuccess_order_closedOrRefund(PayOrderStatusEnum.REFUND.getStatus()); + } + + private void testNotifyOrderSuccess_order_closedOrRefund(Integer status) { + // mock 数据(PayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status)); + orderMapper.insert(order); + // mock 数据(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110") + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // 准备参数 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + assertServiceException(() -> orderService.notifyOrder(channel, notify), + ORDER_STATUS_IS_NOT_WAITING); + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + } + + @Test + public void testNotifyOrderSuccess_order_paid() { + // mock 数据(PayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus())); + orderMapper.insert(order); + // mock 数据(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setNo("P110") + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // 重要:需要将 order 的 extensionId 更新下 + order.setExtensionId(orderExtension.getId()); + orderMapper.updateById(order); + // 准备参数 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null)); + // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS + assertPojoEquals(order, orderMapper.selectOne(null)); + // 断言,调用 + verify(notifyService, never()).createPayNotifyTask(any(PayNotifyTaskCreateReqDTO.class)); + } + + @Test + public void testNotifyOrderSuccess_order_waiting() { + // mock 数据(PayOrderDO) + PayOrderDO order = randomPojo(PayOrderDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setPrice(10)); + orderMapper.insert(order); + // mock 数据(PayOrderExtensionDO) + PayOrderExtensionDO orderExtension = randomPojo(PayOrderExtensionDO.class, + o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()) + .setNo("P110") + .setOrderId(order.getId())); + orderExtensionMapper.insert(orderExtension); + // 准备参数 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L) + .setFeeRate(0.1D)); + PayOrderRespDTO notify = randomPojo(PayOrderRespDTO.class, + o -> o.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()) + .setOutTradeNo("P110")); + + // 调用,并断言异常 + orderService.notifyOrder(channel, notify); + // 断言 PayOrderExtensionDO :数据未更新,因为它是 SUCCESS + orderExtension.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setChannelNotifyData(toJsonString(notify)); + assertPojoEquals(orderExtension, orderExtensionMapper.selectOne(null), + "updateTime", "updater"); + // 断言 PayOrderDO :数据未更新,因为它是 SUCCESS + order.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()) + .setChannelId(10L).setChannelCode(channel.getCode()) + .setSuccessTime(notify.getSuccessTime()).setExtensionId(orderExtension.getId()).setNo(orderExtension.getNo()) + .setChannelOrderNo(notify.getChannelOrderNo()).setChannelUserId(notify.getChannelUserId()) + .setChannelFeeRate(0.1D).setChannelFeePrice(1); + assertPojoEquals(order, orderMapper.selectOne(null), + "updateTime", "updater"); + // 断言,调用 + verify(notifyService).createPayNotifyTask(argThat(new ArgumentMatcher() { + @Override + public boolean matches(PayNotifyTaskCreateReqDTO reqDTO) { + assertEquals(reqDTO.getType(), PayNotifyTypeEnum.ORDER.getType()); + assertEquals(reqDTO.getDataId(), orderExtension.getOrderId()); + return true; + } + })); + } + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java index e97cdc027..53f3cab26 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java @@ -1,21 +1,20 @@ package cn.iocoder.yudao.module.pay.service.refund; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; -import cn.iocoder.yudao.module.pay.service.order.PayOrderExtensionService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; @@ -31,7 +30,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static org.junit.jupiter.api.Assertions.assertEquals; @Import(PayRefundServiceImpl.class) -public class PayRefundServiceTest extends BaseDbUnitTest { +public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @Resource private PayRefundServiceImpl refundService; @@ -46,8 +45,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest { @MockBean private PayOrderService orderService; @MockBean - private PayOrderExtensionService orderExtensionService; - @MockBean private PayAppService appService; @MockBean private PayChannelService channelService; @@ -66,8 +63,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setMerchantOrderId("MOT0000001"); o.setMerchantRefundId("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); - o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); o.setPayPrice(100); o.setRefundPrice(500); o.setReason("就是想退款了,你有意见吗"); @@ -87,11 +83,8 @@ public class PayRefundServiceTest extends BaseDbUnitTest { refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); // 测试 merchantRefundNo 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112"))); - // 测试 notifyStatus 不匹配 - refundMapper.insert( - cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); // 测试 status 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()))); // 测试 createTime 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); @@ -99,8 +92,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { PayRefundPageReqVO reqVO = new PayRefundPageReqVO(); reqVO.setAppId(1L); reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); - reqVO.setMerchantRefundNo("MRF0000001"); - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); + reqVO.setMerchantRefundId("MRF0000001"); reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); @@ -124,8 +116,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setMerchantOrderId("MOT0000001"); o.setMerchantRefundId("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); - o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); - o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); + o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); o.setPayPrice(100); o.setRefundPrice(500); o.setReason("就是想退款了,你有意见吗"); @@ -145,11 +136,8 @@ public class PayRefundServiceTest extends BaseDbUnitTest { refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); // 测试 merchantRefundNo 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112"))); - // 测试 notifyStatus 不匹配 - refundMapper.insert( - cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); // 测试 status 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus()))); // 测试 createTime 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); @@ -158,10 +146,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { PayRefundExportReqVO reqVO = new PayRefundExportReqVO(); reqVO.setAppId(1L); reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode()); - reqVO.setMerchantRefundNo("MRF0000001"); - reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); // 调用 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql b/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql index 2a4610d02..ff2e96788 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql @@ -1,4 +1,5 @@ DELETE FROM pay_app; DELETE FROM pay_channel; DELETE FROM pay_order; +DELETE FROM pay_order_extension; DELETE FROM pay_refund; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql b/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql index fea1365a5..a748b2340 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql +++ b/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` ( `subject` varchar(32) NOT NULL, `body` varchar(128) NOT NULL, `notify_url` varchar(1024) NOT NULL, - `notify_status` tinyint(4) NOT NULL, - `amount` bigint(20) NOT NULL, + `price` bigint(20) NOT NULL, `channel_fee_rate` double DEFAULT 0, - `channel_fee_amount` bigint(20) DEFAULT 0, + `channel_fee_price` bigint(20) DEFAULT 0, `status` tinyint(4) NOT NULL, `user_ip` varchar(50) NOT NULL, - `expire_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `expire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `success_time` datetime(0) DEFAULT CURRENT_TIMESTAMP, `notify_time` datetime(0) DEFAULT CURRENT_TIMESTAMP, - `success_extension_id` bigint(20) DEFAULT NULL COMMENT '支付成功的订单拓展单编号', - `refund_status` tinyint(4) NOT NULL, - `refund_times` tinyint(4) NOT NULL, - `refund_amount` bigint(20) NOT NULL, + `extension_id` bigint(20) DEFAULT NULL, + `no` varchar(64) NULL, + `refund_price` bigint(20) NOT NULL, `channel_user_id` varchar(255) DEFAULT NULL, `channel_order_no` varchar(64) DEFAULT NULL, `creator` varchar(64) DEFAULT '', @@ -62,6 +60,26 @@ CREATE TABLE IF NOT EXISTS `pay_order` ( PRIMARY KEY ("id") ) COMMENT = '支付订单'; +CREATE TABLE IF NOT EXISTS `pay_order_extension` ( + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `no` varchar(64) NOT NULL, + `order_id` bigint(20) NOT NULL, + `channel_id` bigint(20) NOT NULL, + `channel_code` varchar(32) NOT NULL, + `user_ip` varchar(50) NULL DEFAULT NULL, + `status` tinyint(4) NOT NULL, + `channel_extras` varchar(1024) NULL DEFAULT NULL, + `channel_error_code` varchar(64) NULL, + `channel_error_msg` varchar(64) NULL, + `channel_notify_data` varchar(1024) NULL, + `creator` varchar(64) NULL DEFAULT '', + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) NULL DEFAULT '', + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT = '支付订单拓展'; + CREATE TABLE IF NOT EXISTS `pay_refund` ( "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, `app_id` bigint(20) NOT NULL, diff --git a/yudao-ui-admin/src/api/pay/app.js b/yudao-ui-admin/src/api/pay/app.js index b8deeb1a7..b363ec8b9 100644 --- a/yudao-ui-admin/src/api/pay/app.js +++ b/yudao-ui-admin/src/api/pay/app.js @@ -1,6 +1,6 @@ import request from '@/utils/request' -// 创建支付应用信息 +// 创建支付应用 export function createApp(data) { return request({ url: '/pay/app/create', @@ -9,7 +9,7 @@ export function createApp(data) { }) } -// 更新支付应用信息 +// 更新支付应用 export function updateApp(data) { return request({ url: '/pay/app/update', @@ -18,7 +18,7 @@ export function updateApp(data) { }) } -// 支付应用信息状态修改 +// 支付应用状态修改 export function changeAppStatus(id, status) { const data = { id, @@ -31,7 +31,7 @@ export function changeAppStatus(id, status) { }) } -// 删除支付应用信息 +// 删除支付应用 export function deleteApp(id) { return request({ url: '/pay/app/delete?id=' + id, @@ -39,7 +39,7 @@ export function deleteApp(id) { }) } -// 获得支付应用信息 +// 获得支付应用 export function getApp(id) { return request({ url: '/pay/app/get?id=' + id, @@ -47,7 +47,7 @@ export function getApp(id) { }) } -// 获得支付应用信息分页 +// 获得支付应用分页 export function getAppPage(query) { return request({ url: '/pay/app/page', @@ -56,23 +56,10 @@ export function getAppPage(query) { }) } -// 导出支付应用信息 Excel -export function exportAppExcel(query) { +// 获得支付应用列表 +export function getAppList() { return request({ - url: '/pay/app/export-excel', - method: 'get', - params: query, - responseType: 'blob' - }) -} - -// 根据商ID称搜索应用列表 -export function getAppListByMerchantId(merchantId) { - return request({ - url: '/pay/app/list-merchant-id', - params:{ - merchantId:merchantId - }, + url: '/pay/app/list', method: 'get' }) } diff --git a/yudao-ui-admin/src/api/pay/notify.js b/yudao-ui-admin/src/api/pay/notify.js new file mode 100644 index 000000000..4cb6e20c1 --- /dev/null +++ b/yudao-ui-admin/src/api/pay/notify.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 获得支付通知明细 +export function getNotifyTaskDetail(id) { + return request({ + url: '/pay/notify/get-detail?id=' + id, + method: 'get' + }) +} + +// 获得支付通知分页 +export function getNotifyTaskPage(query) { + return request({ + url: '/pay/notify/page', + method: 'get', + params: query + }) +} diff --git a/yudao-ui-admin/src/utils/constants.js b/yudao-ui-admin/src/utils/constants.js index 781b0a0a9..70e0f5a02 100644 --- a/yudao-ui-admin/src/utils/constants.js +++ b/yudao-ui-admin/src/utils/constants.js @@ -206,42 +206,6 @@ export const PayOrderStatusEnum = { } } -/** - * 支付订单回调状态枚举 - */ -export const PayOrderNotifyStatusEnum = { - NO: { - status: 0, - name: '未通知' - }, - SUCCESS: { - status: 10, - name: '通知成功' - }, - FAILURE: { - status: 20, - name: '通知失败' - } -} - -/** - * 支付订单退款状态枚举 - */ -export const PayOrderRefundStatusEnum = { - NO: { - status: 0, - name: '未退款' - }, - SOME: { - status: 10, - name: '部分退款' - }, - ALL: { - status: 20, - name: '全部退款' - } -} - /** * 支付退款订单状态枚举 */ diff --git a/yudao-ui-admin/src/utils/dict.js b/yudao-ui-admin/src/utils/dict.js index 3f6cfbbd4..6019cdc83 100644 --- a/yudao-ui-admin/src/utils/dict.js +++ b/yudao-ui-admin/src/utils/dict.js @@ -51,15 +51,12 @@ export const DICT_TYPE = { // ========== PAY 模块 ========== PAY_CHANNEL_WECHAT_VERSION: 'pay_channel_wechat_version', // 微信渠道版本 - PAY_CHANNEL_ALIPAY_SIGN_TYPE: 'pay_channel_alipay_sign_type', // 支付渠道支付宝算法类型 - PAY_CHANNEL_ALIPAY_MODE: 'pay_channel_alipay_mode', // 支付宝公钥类型 - PAY_CHANNEL_ALIPAY_SERVER_TYPE: 'pay_channel_alipay_server_type', // 支付宝网关地址 + PAY_CHANNEL_CODE: 'pay_channel_code', // 支付渠道编码类型 - PAY_ORDER_NOTIFY_STATUS: 'pay_order_notify_status', // 商户支付订单回调状态 PAY_ORDER_STATUS: 'pay_order_status', // 商户支付订单状态 - PAY_ORDER_REFUND_STATUS: 'pay_order_refund_status', // 商户支付订单退款状态 - PAY_REFUND_ORDER_STATUS: 'pay_refund_order_status', // 退款订单状态 - PAY_REFUND_ORDER_TYPE: 'pay_refund_order_type', // 退款订单类别 + PAY_REFUND_STATUS: 'pay_refund_status', // 退款订单状态 + PAY_NOTIFY_STATUS: 'pay_notify_status', // 商户支付回调状态 + PAY_NOTIFY_TYPE: 'pay_notify_type', // 商户支付回调状态 // ========== MP 模块 ========== MP_AUTO_REPLY_REQUEST_MATCH: 'mp_auto_reply_request_match', // 自动回复请求匹配类型 diff --git a/yudao-ui-admin/src/views/pay/app/components/aliPayChannelForm.vue b/yudao-ui-admin/src/views/pay/app/components/aliPayChannelForm.vue deleted file mode 100644 index d3a8943b6..000000000 --- a/yudao-ui-admin/src/views/pay/app/components/aliPayChannelForm.vue +++ /dev/null @@ -1,349 +0,0 @@ - - - diff --git a/yudao-ui-admin/src/views/pay/app/components/alipayChannelForm.vue b/yudao-ui-admin/src/views/pay/app/components/alipayChannelForm.vue new file mode 100644 index 000000000..1043c414a --- /dev/null +++ b/yudao-ui-admin/src/views/pay/app/components/alipayChannelForm.vue @@ -0,0 +1,268 @@ + + + diff --git a/yudao-ui-admin/src/views/pay/app/index.vue b/yudao-ui-admin/src/views/pay/app/index.vue index b027e2c26..3a038f8be 100644 --- a/yudao-ui-admin/src/views/pay/app/index.vue +++ b/yudao-ui-admin/src/views/pay/app/index.vue @@ -213,8 +213,10 @@ 取 消 - - + + + + @@ -223,13 +225,13 @@ import { createApp, updateApp, changeAppStatus, deleteApp, getApp, getAppPage } import { DICT_TYPE, getDictDatas } from "@/utils/dict"; import { PayType, PayChannelEnum, CommonStatusEnum } from "@/utils/constants"; import wechatChannelForm from "@/views/pay/app/components/wechatChannelForm"; -import aliPayChannelForm from "@/views/pay/app/components/aliPayChannelForm"; +import alipayChannelForm from "@/views/pay/app/components/alipayChannelForm"; export default { name: "PayApp", components: { "wechatChannelForm": wechatChannelForm, - "aliPayChannelForm": aliPayChannelForm + "alipayChannelForm": alipayChannelForm }, data() { return { @@ -283,13 +285,6 @@ export default { "appId": null, // 渠道编码 "payCode": null, - // 商户对象 - "payMerchant": { - // 编号 - "id": null, - // 名称 - "name": null - }, } }; }, @@ -424,14 +419,12 @@ export default { this.channelParam.aliPayOpen = false; } if (type === PayType.ALIPAY) { - this.channelParam.aliPayOpen = true; - this.channelParam.wechatOpen = false; + console.log(this.$refs['alipayChannelFormRef']) + this.$refs['alipayChannelFormRef'].open(row.id, payCode); + return; + // this.channelParam.aliPayOpen = true; + // this.channelParam.wechatOpen = false; } - this.channelParam.edit = false; - this.channelParam.loading = false; - this.channelParam.appId = row.id; - this.channelParam.payCode = payCode; - this.channelParam.payMerchant = row.payMerchant; }, /** * 根据渠道编码判断渠道列表中是否存在 diff --git a/yudao-ui-admin/src/views/pay/notify/index.vue b/yudao-ui-admin/src/views/pay/notify/index.vue new file mode 100644 index 000000000..3c9e7d578 --- /dev/null +++ b/yudao-ui-admin/src/views/pay/notify/index.vue @@ -0,0 +1,221 @@ + + + diff --git a/yudao-ui-admin/src/views/pay/order/index.vue b/yudao-ui-admin/src/views/pay/order/index.vue index 6f3fe5006..6d7be5014 100755 --- a/yudao-ui-admin/src/views/pay/order/index.vue +++ b/yudao-ui-admin/src/views/pay/order/index.vue @@ -8,35 +8,26 @@ - - - + + + - - + - - + + + + - - - - - - - - - - - @@ -62,69 +53,64 @@ - - + + - - - - - - - + + + + + + - - - - - - - + - - - - + + + + + @@ -134,67 +120,71 @@ - - + + - {{ orderDetail.appName }} - {{ orderDetail.subject }} - - - - + {{ orderDetail.merchantOrderId }} - - {{ orderDetail.channelOrderNo }} - - - - {{ orderDetail.payOrderExtension.no }} - - - - {{ parseFloat(orderDetail.price / 100, 2) }} - - - {{ parseFloat(orderDetail.channelFeePrice / 100, 2) }} - - - {{ parseFloat(orderDetail.channelFeeRate / 100, 2) }}% + + {{ orderDetail.no }} + + + {{ orderDetail.appId }} + {{ orderDetail.appName }} + + - + - - + + ¥{{ (orderDetail.price / 100.0).toFixed(2) }} - {{ orderDetail.notifyUrl }} - {{ parseTime(orderDetail.createTime) }} + + + + ¥{{ (orderDetail.channelFeePrice / 100.0).toFixed(2) }} + + {{ (orderDetail.channelFeeRate / 100.0).toFixed(2) }}% + + {{ parseTime(orderDetail.successTime) }} {{ parseTime(orderDetail.expireTime) }} - {{ parseTime(orderDetail.notifyTime) }} - - {{ orderDetail.channelCodeName }} - {{ orderDetail.userIp }} - - - - {{ orderDetail.refundTimes }} - - - {{ parseFloat(orderDetail.refundPrice / 100, 2) }} - - + {{ parseTime(orderDetail.createTime) }} + {{ parseTime(orderDetail.updateTime) }} - - - - {{ orderDetail.body }} + + + + {{ orderDetail.subject }} + {{ orderDetail.body }} + + + + + {{ orderDetail.userIp }} + + + + {{ orderDetail.channelOrderNo }} + + {{ orderDetail.channelUserId }} + + + + ¥{{ (orderDetail.refundPrice / 100.0).toFixed(2) }} + + {{ orderDetail.notifyUrl }} + + + + - {{ orderDetail.payOrderExtension.channelNotifyData }} + {{ orderDetail.extension.channelNotifyData }} @@ -203,35 +193,7 @@