mall + pay:

1. 优化订单管理的接口实现
2. 支付状态增加 refund 类型,将 refund status 合并进去
This commit is contained in:
YunaiV 2023-07-19 21:09:02 +08:00
parent 5dcb3db5d7
commit b84da30234
55 changed files with 465 additions and 947 deletions

View File

@ -79,14 +79,16 @@ public class PayOrderRespDTO {
/**
* 创建WAITING状态的订单返回
*/
public PayOrderRespDTO(String displayMode, String displayContent,
public static PayOrderRespDTO waitingOf(String displayMode, String displayContent,
String outTradeNo, Object rawData) {
this.status = PayOrderStatusRespEnum.WAITING.getStatus();
this.displayMode = displayMode;
this.displayContent = displayContent;
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;
}
/**

View File

@ -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;
@ -73,10 +75,15 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
StandardCharsets.UTF_8.name(), config.getSignType());
// 2. 解析订单的状态
// 额外说明支付宝不仅仅支付成功会回调再各种触发支付单数据变化时都会进行回调所以这里 status 的解析会写的比较复杂
String tradeStatus = bodyObj.get("trade_status");
Integer status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
: Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS.getStatus()
: ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
: Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
// 特殊逻辑: 支付宝没有退款成功的状态所以如果有退款金额我们认为是退款成功
if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
status = PayOrderStatusRespEnum.REFUND.getStatus();
}
Assert.notNull(status, (Supplier<Throwable>) () -> {
throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
});

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -130,6 +130,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
// 1. 解析回调
WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
// 2. 构建结果
// 微信支付的回调只有 SUCCESS 支付成功CLOSED 支付失败两种情况无需像支付宝一样解析的比较复杂
Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
return new PayOrderRespDTO(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
@ -141,6 +142,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null);
WxPayOrderNotifyV3Result.DecryptNotifyResult result = response.getResult();
// 2. 构建结果
// 微信支付的回调只有 SUCCESS 支付成功CLOSED 支付失败两种情况无需像支付宝一样解析的比较复杂
Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;

View File

@ -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());

View File

@ -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);
}

View File

@ -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);
}

View File

@ -16,7 +16,8 @@ public enum PayOrderStatusRespEnum {
WAITING(0, "未支付"),
SUCCESS(10, "支付成功"),
CLOSED(20, "支付关闭"), // 未付款交易超时关闭或支付完成后全额退款
REFUND(20, "已退款"),
CLOSED(30, "支付关闭"),
;
private final Integer status;

View File

@ -14,7 +14,7 @@ import java.util.Objects;
@AllArgsConstructor
public enum PayRefundStatusRespEnum {
WAITING(0, "退款"),
WAITING(0, "等待退款"),
SUCCESS(10, "退款成功"),
FAILURE(20, "退款失败");

View File

@ -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<Integer> {
@Override
public Class<?> supportJavaTypeKey() {
throw new UnsupportedOperationException("暂不支持,也不需要");
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
throw new UnsupportedOperationException("暂不支持,也不需要");
}
@Override
public WriteCellData<String> 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());
}
}

View File

@ -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"; // 回调状态
}

View File

@ -26,6 +26,7 @@ public interface ErrorCodeConstants {
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 ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
@ -33,7 +34,6 @@ public interface ErrorCodeConstants {
// ========== 支付模块(退款) 1007006000 ==========
ErrorCode REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
ErrorCode REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款");
ErrorCode REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中");
ErrorCode REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单");
ErrorCode REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");

View File

@ -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, "请求失败"),
;

View File

@ -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());
}
/**
* 判断是否支付关闭
*

View File

@ -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<List<PayAppRespVO>> getAppList() {
List<PayAppDO> appListDO = appService.getAppList();
return success(PayAppConvert.INSTANCE.convertList(appListDO));
}
}

View File

@ -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,7 +48,7 @@ public class PayOrderController {
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('pay:order:query')")
public CommonResult<PayOrderRespVO> getOrder(@RequestParam("id") Long id) {
return success(PayOrderConvert.INSTANCE.convert(payOrderService.getOrder(id)));
return success(PayOrderConvert.INSTANCE.convert(orderService.getOrder(id)));
}
@GetMapping("/get-detail")
@ -61,86 +56,56 @@ public class PayOrderController {
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('pay:order:query')")
public CommonResult<PayOrderDetailsRespVO> getOrderDetail(@RequestParam("id") Long id) {
PayOrderDO order = payOrderService.getOrder(id);
PayOrderDO order = orderService.getOrder(id);
if (order == null) {
return success(null);
}
// 拼接返回
PayAppDO app = appService.getApp(order.getAppId());
PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
PayOrderExtensionDO orderExtension = orderService.getOrderExtension(order.getExtensionId());
return success(PayOrderConvert.INSTANCE.convert(order, orderExtension, app));
}
@PostMapping("/submit")
@Operation(summary = "提交支付订单")
public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP());
PayOrderSubmitRespVO respVO = orderService.submitOrder(reqVO, getClientIP());
return success(respVO);
}
// TODO 芋艿优化
@GetMapping("/page")
@Operation(summary = "获得支付订单分页")
@PreAuthorize("@ss.hasPermission('pay:order:query')")
public CommonResult<PageResult<PayOrderPageItemRespVO>> getOrderPage(@Valid PayOrderPageReqVO pageVO) {
PageResult<PayOrderDO> pageResult = payOrderService.getOrderPage(pageVO);
PageResult<PayOrderDO> pageResult = orderService.getOrderPage(pageVO);
if (CollectionUtil.isEmpty(pageResult.getList())) {
return success(new PageResult<>(pageResult.getTotal()));
}
// 处理应用ID数据
Map<Long, PayAppDO> appMap = appService.getAppMap(
CollectionUtils.convertList(pageResult.getList(), PayOrderDO::getAppId));
List<PayOrderPageItemRespVO> 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<Long, PayAppDO> appMap = appService.getAppMap(convertList(pageResult.getList(), PayOrderDO::getAppId));
return success(PayOrderConvert.INSTANCE.convertPage(pageResult, appMap));
}
// TODO 芋艿优化
@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<PayOrderDO> list = payOrderService.getOrderList(exportReqVO);
List<PayOrderDO> list = orderService.getOrderList(exportReqVO);
if (CollectionUtil.isEmpty(list)) {
ExcelUtils.write(response, "支付订单.xls", "数据",
PayOrderExcelVO.class, new ArrayList<>());
return;
}
// 处理应用ID数据
Map<Long, PayAppDO> appMap = appService.getAppMap(
CollectionUtils.convertList(list, PayOrderDO::getAppId));
// 处理扩展订单数据
Map<Long, PayOrderExtensionDO> orderExtensionMap = orderExtensionService
.getOrderExtensionMap(CollectionUtils.convertList(list, PayOrderDO::getSuccessExtensionId));
List<PayOrderExcelVO> 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<Long, PayAppDO> appMap = appService.getAppMap(convertList(list, PayOrderDO::getAppId));
List<PayOrderExcelVO> excelList = PayOrderConvert.INSTANCE.convertList(list, appMap);
// 导出 Excel
ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, excelDatum);
ExcelUtils.write(response, "支付订单.xls", "数据", PayOrderExcelVO.class, excelList);
}
}

View File

@ -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;
}

View File

@ -22,6 +22,9 @@ public class PayOrderDetailsRespVO extends PayOrderBaseVO {
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
@Schema(description = "更新时间", required = true)
private LocalDateTime updateTime;
/**
* 支付订单扩展
*/

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -47,10 +47,6 @@ public class PayRefundBaseVO {
@NotNull(message = "异步通知商户地址不能为空")
private String notifyUrl;
@Schema(description = "通知商户退款结果的回调状态", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "通知商户退款结果的回调状态不能为空")
private Integer notifyStatus;
@Schema(description = "退款状态", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "退款状态不能为空")
private Integer status;
@ -98,8 +94,4 @@ public class PayRefundBaseVO {
@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;
}

View File

@ -40,18 +40,10 @@ public class PayRefundExcelVO {
@ExcelProperty("异步通知商户地址")
private String notifyUrl;
@DictFormat(DictTypeConstants.ORDER_NOTIFY_STATUS)
@ExcelProperty(value = "商户退款结果回调状态", converter = DictConvert.class)
private Integer notifyStatus;
@DictFormat(DictTypeConstants.REFUND_ORDER_STATUS)
@DictFormat(DictTypeConstants.REFUND_STATUS)
@ExcelProperty(value = "退款状态", converter = DictConvert.class)
private Integer status;
@DictFormat(DictTypeConstants.REFUND_ORDER_TYPE)
@ExcelProperty(value = "退款类型", converter = DictConvert.class)
private Integer type;
@ExcelProperty("支付金额,单位:元")
private String payPrice;

View File

@ -36,9 +36,6 @@ public class PayRefundExportReqVO {
@Schema(description = "异步通知商户地址")
private String notifyUrl;
@Schema(description = "通知商户退款结果的回调状态")
private Integer notifyStatus;
@Schema(description = "退款状态")
private Integer status;

View File

@ -41,9 +41,6 @@ public class PayRefundPageReqVO extends PageParam {
@Schema(description = "异步通知商户地址")
private String notifyUrl;
@Schema(description = "通知商户退款结果的回调状态")
private Integer notifyStatus;
@Schema(description = "退款状态")
private Integer status;
@ -85,10 +82,6 @@ public class PayRefundPageReqVO extends PageParam {
@Schema(description = "退款成功时间")
private LocalDateTime[] successTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "退款通知时间")
private LocalDateTime[] notifyTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;

View File

@ -1,6 +1,8 @@
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;
@ -13,9 +15,8 @@ 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
@ -42,56 +43,21 @@ public interface PayOrderConvert {
PayOrderDetailsRespVO convertDetail(PayOrderDO bean);
PayOrderDetailsRespVO.PayOrderExtension convert(PayOrderExtensionDO bean);
List<PayOrderRespVO> convertList(List<PayOrderDO> list);
PageResult<PayOrderRespVO> convertPage(PageResult<PayOrderDO> page);
/**
* 订单 DO 转自定义分页对象
*
* @param bean 订单DO
* @return 分页对象
*/
PayOrderPageItemRespVO pageConvertItemPage(PayOrderDO bean);
// TODO 芋艿优化下 convert 逻辑
default PayOrderExcelVO excelConvert(PayOrderDO bean) {
if (bean == null) {
return null;
default PageResult<PayOrderPageItemRespVO> convertPage(PageResult<PayOrderDO> page, Map<Long, PayAppDO> appMap) {
PageResult<PayOrderPageItemRespVO> result = convertPage(page);
result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName())));
return result;
}
PageResult<PayOrderPageItemRespVO> convertPage(PageResult<PayOrderDO> page);
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;
default List<PayOrderExcelVO> convertList(List<PayOrderDO> list, Map<Long, PayAppDO> 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);

View File

@ -62,7 +62,6 @@ public interface PayRefundConvert {
// 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());

View File

@ -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;
/**
* 退款总金额单位
*/

View File

@ -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;
// ========== 退款相关字段 ==========
/**

View File

@ -1,47 +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<PayOrderDO> {
default PageResult<PayOrderDO> selectPage(PayOrderPageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<PayOrderDO>()
.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<PayOrderDO>()
.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<PayOrderDO> selectList(PayOrderExportReqVO reqVO) {
return selectList(new QueryWrapperX<PayOrderDO>()
.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 selectList(new LambdaQueryWrapperX<PayOrderDO>()
.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) {
@ -49,13 +43,13 @@ public interface PayOrderMapper extends BaseMapperX<PayOrderDO> {
}
default PayOrderDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) {
return selectOne(new QueryWrapper<PayOrderDO>().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<PayOrderDO>()
.eq("id", id).eq("status", status));
return update(update, new LambdaQueryWrapper<PayOrderDO>()
.eq(PayOrderDO::getId, id).eq(PayOrderDO::getStatus, status));
}
}

View File

@ -50,7 +50,6 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> {
.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"));
}
@ -62,7 +61,6 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> {
.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"));
}

View File

@ -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];
}
}

View File

@ -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];
}
}

View File

@ -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<PayAppDO> getAppList(Collection<Long> ids);
/**
* 获得支付应用信息分页
* 获得支付应用列表
*
* @return 支付应用列表
*/
List<PayAppDO> getAppList();
/**
* 获得支付应用分页
*
* @param pageReqVO 分页查询
* @return 支付应用信息分页
* @return 支付应用分页
*/
PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO);
@ -91,7 +98,7 @@ public interface PayAppService {
* 如果不合法抛出 {@link ServiceException} 业务异常
*
* @param id 应用编号
* @return 应用信息
* @return 应用
*/
PayAppDO validPayApp(Long id);

View File

@ -99,6 +99,11 @@ public class PayAppServiceImpl implements PayAppService {
return appMapper.selectBatchIds(ids);
}
@Override
public List<PayAppDO> getAppList() {
return appMapper.selectList();
}
@Override
public PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO) {
return appMapper.selectPage(pageReqVO);

View File

@ -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<PayOrderExtensionDO> getOrderExtensionList(Collection<Long> ids);
/**
* 根据订单成功的 扩展订单ID 查询所有的扩展订单转 map 返回
*
* @param successExtensionIdList 订单 ID 集合
* @return 订单扩展 map 集合
*/
default Map<Long, PayOrderExtensionDO> getOrderExtensionMap(Collection<Long> successExtensionIdList) {
List<PayOrderExtensionDO> list = getOrderExtensionList(successExtensionIdList);
return CollectionUtils.convertMap(list, PayOrderExtensionDO::getId);
}
}

View File

@ -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<PayOrderExtensionDO> getOrderExtensionList(Collection<Long> ids) {
return orderExtensionMapper.selectBatchIds(ids);
}
}

View File

@ -9,6 +9,7 @@ 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;
@ -118,4 +119,12 @@ public interface PayOrderService {
*/
void updateOrderRefundPrice(Long id, Integer incrRefundPrice);
/**
* 获得支付订单
*
* @param id 编号
* @return 支付订单
*/
PayOrderExtensionDO getOrderExtension(Long id);
}

View File

@ -28,13 +28,12 @@ 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.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.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;
@ -125,11 +124,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();
}
@ -255,6 +254,8 @@ public class PayOrderServiceImpl implements PayOrderService {
if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) {
notifyOrderClosed(channel, notify);
}
// 情况三WAITING无需处理
// 情况四REFUND通过退款回调处理
}
private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
@ -318,7 +319,7 @@ public class PayOrderServiceImpl implements PayOrderService {
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);
}
@ -330,9 +331,10 @@ public class PayOrderServiceImpl implements PayOrderService {
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(ORDER_STATUS_IS_NOT_WAITING);
}
@ -379,8 +381,8 @@ public class PayOrderServiceImpl implements PayOrderService {
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
if (!PayOrderStatusEnum.isSuccess(order.getStatus())) {
throw exception(ORDER_STATUS_IS_NOT_SUCCESS);
if (!PayOrderStatusEnum.isSuccessOrRefund(order.getStatus())) {
throw exception(ORDER_REFUND_FAIL_STATUS_ERROR);
}
if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) {
throw exception(REFUND_PRICE_EXCEED);
@ -389,20 +391,18 @@ public class PayOrderServiceImpl implements PayOrderService {
// 更新订单
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());
}
int updateCount = orderMapper.updateByIdAndStatus(id, PayOrderStatusEnum.SUCCESS.getStatus(), updateObj);
.setStatus(PayOrderStatusEnum.REFUND.getStatus());
int updateCount = orderMapper.updateByIdAndStatus(id, order.getStatus(), updateObj);
if (updateCount == 0) {
throw exception(ORDER_STATUS_IS_NOT_SUCCESS);
throw exception(ORDER_REFUND_FAIL_STATUS_ERROR);
}
}
@Override
public PayOrderExtensionDO getOrderExtension(Long id) {
return orderExtensionMapper.selectById(id);
}
/**
* 获得自身的代理对象解决 AOP 生效问题
*

View File

@ -21,16 +21,13 @@ 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.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;
@ -67,8 +64,6 @@ public class PayRefundServiceImpl implements PayRefundService {
@Resource
private PayOrderService orderService;
@Resource
private PayOrderExtensionService orderExtensionService;
@Resource
private PayAppService appService;
@Resource
private PayChannelService channelService;
@ -121,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())
// 退款相关字段
@ -129,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())
@ -161,10 +156,6 @@ public class PayRefundServiceImpl implements PayRefundService {
throw exception(ErrorCodeConstants.ORDER_STATUS_IS_NOT_SUCCESS);
}
// 是否已经全额退款
if (PayOrderRefundStatusEnum.ALL.getStatus().equals(order.getRefundStatus())) {
throw exception(ErrorCodeConstants.REFUND_ALL_REFUNDED);
}
// 校验金额 退款金额不能大于原定的金额
if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){
throw exception(ErrorCodeConstants.REFUND_PRICE_EXCEED);

View File

@ -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();
}
}

View File

@ -1,16 +1,12 @@
package cn.iocoder.yudao.module.pay.service.order;
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.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.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
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;
@ -18,27 +14,23 @@ import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderSubmitReqVO
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.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.enums.order.PayOrderRefundStatusEnum;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
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 org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.equalsAny;
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;
@ -83,40 +75,36 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
// mock 数据
PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到
o.setAppId(1L);
o.setChannelId(10L);
o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
o.setMerchantOrderId("110");
o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
o.setChannelOrderNo("220");
o.setNo("330");
o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
o.setCreateTime(buildTime(2018, 1, 15));
o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
});
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(randomString())));
// 测试 notifyStatus 不匹配
orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
// 测试 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(buildTime(2019, 1, 1))));
// 准备参数
PayOrderPageReqVO reqVO = new PayOrderPageReqVO();
reqVO.setAppId(1L);
reqVO.setChannelId(10L);
reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
reqVO.setMerchantOrderId("110");
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(buildBetweenTime(2018, 1, 10, 2018, 1, 30));
// 调用
@ -132,40 +120,36 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
// mock 数据
PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> { // 等会查询到
o.setAppId(1L);
o.setChannelId(10L);
o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
o.setMerchantOrderId("110");
o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
o.setChannelOrderNo("220");
o.setNo("330");
o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
o.setCreateTime(buildTime(2018, 1, 15));
o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
});
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(randomString())));
// 测试 notifyStatus 不匹配
orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
// 测试 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(buildTime(2019, 1, 1))));
// 准备参数
PayOrderExportReqVO reqVO = new PayOrderExportReqVO();
reqVO.setAppId(1L);
reqVO.setChannelId(10L);
reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
reqVO.setMerchantOrderId("110");
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(buildBetweenTime(2018, 1, 10, 2018, 1, 30));
// 调用
@ -193,8 +177,6 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
assertEquals(order.getAppId(), 1L);
assertEquals(order.getNotifyUrl(), "http://127.0.0.1");
assertEquals(order.getStatus(), PayOrderStatusEnum.WAITING.getStatus());
assertEquals(order.getRefundStatus(), PayOrderRefundStatusEnum.NO.getStatus());
assertEquals(order.getRefundTimes(), 0);
assertEquals(order.getRefundPrice(), 0);
}

View File

@ -9,13 +9,11 @@ import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReq
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.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;
@ -46,8 +44,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
@MockBean
private PayOrderService orderService;
@MockBean
private PayOrderExtensionService orderExtensionService;
@MockBean
private PayAppService appService;
@MockBean
private PayChannelService channelService;
@ -66,8 +62,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 +82,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))));
@ -100,7 +92,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
reqVO.setAppId(1L);
reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
reqVO.setMerchantRefundNo("MRF0000001");
reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
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 +115,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 +135,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))));
@ -159,9 +146,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
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)}));
// 调用

View File

@ -39,7 +39,6 @@ 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,
`price` bigint(20) NOT NULL,
`channel_fee_rate` double DEFAULT 0,
`channel_fee_price` bigint(20) DEFAULT 0,
@ -48,9 +47,8 @@ CREATE TABLE IF NOT EXISTS `pay_order` (
`expire_time` datetime(0) 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` int 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,

View File

@ -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'
})
}

View File

@ -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: '全部退款'
}
}
/**
* 支付退款订单状态枚举
*/

View File

@ -54,12 +54,11 @@ export const DICT_TYPE = {
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_NOTIFY_STATUS: 'pay_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', // 退款订单类别
// ========== MP 模块 ==========
MP_AUTO_REPLY_REQUEST_MATCH: 'mp_auto_reply_request_match', // 自动回复请求匹配类型

View File

@ -8,18 +8,22 @@
<el-option v-for="item in appList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="渠道编码" prop="channelCode">
<el-select v-model="queryParams.channelCode" placeholder="请输入渠道编码" clearable
<el-form-item label="支付渠道" prop="channelCode">
<el-select v-model="queryParams.channelCode" placeholder="请选择支付渠道" clearable
@clear="()=>{queryParams.channelCode = null}">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE)" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="商户号" prop="merchantOrderId">
<el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户号" clearable
<el-form-item label="商户单号" prop="merchantOrderId">
<el-input v-model="queryParams.merchantOrderId" placeholder="请输入商户单号" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="渠道订单号" prop="channelOrderNo">
<el-input v-model="queryParams.channelOrderNo" placeholder="请输入渠道订单号" clearable
<el-form-item label="支付单号" prop="no">
<el-input v-model="queryParams.no" placeholder="请输入支付单号" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="渠道单号" prop="channelOrderNo">
<el-input v-model="queryParams.channelOrderNo" placeholder="请输入渠道单号" clearable
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="支付状态" prop="status">
@ -28,18 +32,6 @@
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="退款状态" prop="refundStatus">
<el-select v-model="queryParams.refundStatus" placeholder="请选择退款状态" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_ORDER_REFUND_STATUS)" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="回调商户状态" prop="notifyStatus">
<el-select v-model="queryParams.notifyStatus" placeholder="请选择订单回调商户状态" clearable>
<el-option v-for="dict in payOrderNotifyDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
@ -62,67 +54,55 @@
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="订单编号" align="center" prop="id" width="80"/>
<el-table-column label="支付渠道" align="center" width="130">
<el-table-column label="编号" align="center" prop="id" width="80"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<el-popover trigger="hover" placement="top">
<p>应用名称: {{ scope.row.appName }}</p>
<p>渠道名称: {{ scope.row.channelCodeName }}</p>
<div slot="reference" class="name-wrapper">
{{ scope.row.channelCodeName }}
</div>
</el-popover>
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="支付订单" align="left" width="280">
<template v-slot="scope">
<p class="order-font"><el-tag size="mini">商户单号</el-tag> {{scope.row.merchantOrderId}}</p>
<p class="order-font"><el-tag size="mini" type="warning">支付单号</el-tag> {{scope.row.channelOrderNo}}</p>
</template>
</el-table-column>
<el-table-column label="商品标题" align="center" prop="subject" width="180" :show-overflow-tooltip="true"/>
<el-table-column label="支付金额" align="center" prop="price" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.price / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="手续金额" align="center" prop="channelFeePrice" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.channelFeePrice / 100).toFixed(2) }}
{{ (scope.row.price / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="退款金额" align="center" prop="refundPrice" width="100">
<template v-slot="scope">
{{ parseFloat(scope.row.refundPrice / 100).toFixed(2) }}
{{ (scope.row.refundPrice / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="手续金额" align="center" prop="channelFeePrice" width="100">
<template v-slot="scope">
{{ (scope.row.channelFeePrice / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="订单号" align="left" width="300">
<template v-slot="scope">
<p class="order-font"><el-tag size="mini">商户单号</el-tag> {{scope.row.merchantOrderId}}</p>
<p class="order-font" v-if="scope.row.no"><el-tag size="mini" type="warning">支付单号</el-tag> {{scope.row.no}}</p>
<p class="order-font" v-if="scope.row.channelOrderNo"><el-tag size="mini" type="success">渠道单号</el-tag> {{scope.row.channelOrderNo}}</p>
</template>
</el-table-column>
<el-table-column label="支付状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<!-- TODO 芋艿要放开 -->
<!-- <el-table-column label="退款状态" align="center" prop="refundStatus">-->
<!-- <template v-slot="scope">-->
<!-- <span>{{ getDictDataLabel(DICT_TYPE.PAY_ORDER_REFUND_STATUS, scope.row.refundStatus) }}</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="回调状态" align="center" prop="notifyStatus" width="100">
<el-table-column label="支付渠道" align="center" width="140">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="scope.row.channelCode" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="支付时间" align="center" prop="successTime" width="100">
<el-table-column label="支付时间" align="center" prop="successTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.successTime) }}</span>
</template>
</el-table-column>
<el-table-column label="支付应用" align="center" prop="successTime" width="100">
<template v-slot="scope">
<span>{{ scope.row.appName }}</span>
</template>
</el-table-column>
<el-table-column label="商品标题" align="center" prop="subject" width="180" :show-overflow-tooltip="true"/>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-search" @click="handleDetail(scope.row)"
@ -138,64 +118,66 @@
<!-- 对话框(详情) -->
<el-dialog title="订单详情" :visible.sync="open" width="50%">
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item>
<el-descriptions-item label="商品名称">{{ orderDetail.subject }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="商户订单号">
<el-descriptions-item label="商户单号">
<el-tag size="small">{{ orderDetail.merchantOrderId }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="渠道订单号">
<el-tag class="tag-purple" size="small">{{ orderDetail.channelOrderNo }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="支付订单号">
<el-tag v-if="orderDetail.extension.no !== ''" class="tag-pink" size="small">
{{ orderDetail.extension.no }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="金额">
<el-tag type="success" size="small">{{ parseFloat(orderDetail.price / 100, 2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手续费">
<el-tag type="warning" size="small">{{ parseFloat(orderDetail.channelFeePrice / 100, 2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手续费比例">
{{ parseFloat(orderDetail.channelFeeRate / 100, 2) }}%
<el-descriptions-item label="支付单号">
<el-tag type="warning" v-if="orderDetail.no">{{ orderDetail.no }}</el-tag>
</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="应用编号">{{ orderDetail.appId }}</el-descriptions-item>
<el-descriptions-item label="应用名称">{{ orderDetail.appName }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="orderDetail.status" />
<dict-tag :type="DICT_TYPE.PAY_ORDER_STATUS" :value="orderDetail.status" size="small" />
</el-descriptions-item>
<el-descriptions-item label="回调状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="orderDetail.notifyStatus" />
<el-descriptions-item label="支付金额">
<el-tag type="success" size="small">{{ (orderDetail.price / 100.0).toFixed(2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="回调地址">{{ orderDetail.notifyUrl }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ parseTime(orderDetail.createTime) }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="手续费">
<el-tag type="warning" size="small">{{ (orderDetail.channelFeePrice / 100.0).toFixed(2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手续费比例">{{ (orderDetail.channelFeeRate / 100.0).toFixed(2) }}%</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付时间">{{ parseTime(orderDetail.successTime) }}</el-descriptions-item>
<el-descriptions-item label="失效时间">{{ parseTime(orderDetail.expireTime) }}</el-descriptions-item>
<el-descriptions-item label="通知时间">{{ parseTime(orderDetail.notifyTime) }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="创建时间">{{ parseTime(orderDetail.createTime) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ parseTime(orderDetail.updateTime) }}</el-descriptions-item>
</el-descriptions>
<!-- 分割线 -->
<el-divider/>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="商品标题">{{ orderDetail.subject }}</el-descriptions-item>
<el-descriptions-item label="商品描述">{{ orderDetail.body }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="支付渠道">
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="orderDetail.channelCode" />
</el-descriptions-item>
<el-descriptions-item label="支付IP">{{ orderDetail.userIp }}</el-descriptions-item>
<el-descriptions-item label="退款状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_REFUND_STATUS" :value="orderDetail.refundStatus" />
</el-descriptions-item>
<el-descriptions-item label="退款次数">{{ orderDetail.refundTimes }}</el-descriptions-item>
<el-descriptions-item label="退款金额">
<el-tag type="warning">
{{ parseFloat(orderDetail.refundPrice / 100, 2) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="支付 IP">{{ orderDetail.userIp }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
<el-descriptions-item label="商品描述">
{{ orderDetail.body }}
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="渠道单号">
<el-tag size="mini" type="success" v-if="orderDetail.channelOrderNo">{{ orderDetail.channelOrderNo }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="渠道用户">{{ orderDetail.channelUserId }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="2" label-class-name="desc-label">
<el-descriptions-item label="退款金额">
<el-tag size="mini" type="danger">{{ (orderDetail.refundPrice / 100.0).toFixed(2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="通知 URL">{{ orderDetail.notifyUrl }}</el-descriptions-item>
</el-descriptions>
<!-- 分割线 -->
<el-divider />
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
<el-descriptions-item label="支付通道异步回调内容">
{{ orderDetail.extension.channelNotifyData }}
</el-descriptions-item>
@ -206,7 +188,7 @@
<script>
import { getOrderDetail, getOrderPage, exportOrderExcel } from "@/api/pay/order";
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
import { getAppList } from "@/api/pay/app";
export default {
name: "PayOrder",
@ -225,40 +207,21 @@ export default {
title: "",
//
open: false,
// TODO
//
queryParams: {
pageNo: 1,
pageSize: 10,
appId: null,
channelId: null,
channelCode: null,
merchantOrderId: null,
subject: null,
body: null,
notifyUrl: null,
notifyStatus: null,
price: null,
channelFeeRate: null,
channelFeePrice: null,
status: null,
userIp: null,
successExtensionId: null,
refundStatus: null,
refundTimes: null,
refundPrice: null,
channelUserId: null,
channelOrderNo: null,
expireTime: [],
successTime: [],
notifyTime: [],
createTime: []
no: null,
status: null,
createTime: [],
},
//
appList: [],
//
payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
//
orderDetail: {
extension: {}
@ -267,6 +230,10 @@ export default {
},
created() {
this.getList();
//
getAppList().then(response => {
this.appList = response.data;
})
},
methods: {
/** 查询列表 */

View File

@ -29,12 +29,6 @@
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="退款回调状态" prop="notifyStatus">
<el-select v-model="queryParams.notifyStatus" placeholder="请选择通知商户退款结果的回调状态" clearable>
<el-option v-for="dict in payOrderNotifyDictDatum" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
@ -105,21 +99,11 @@
{{ parseFloat(scope.row.refundPrice / 100).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="退款类型" align="center" prop="type" width="80">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="退款状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="回调状态" align="center" prop="notifyStatus">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
</template>
</el-table-column>
<el-table-column label="退款原因" align="center" prop="reason" width="140" :show-overflow-tooltip="true"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template v-slot="scope">
@ -165,11 +149,6 @@
<el-descriptions-item label="退款金额" size="mini">
<el-tag class="tag-purple" size="mini">{{ parseFloat(refundDetail.refundPrice / 100).toFixed(2) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="退款类型">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="refundDetail.type" />
</template>
</el-descriptions-item>
<el-descriptions-item label="退款状态">
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="refundDetail.status" />
</el-descriptions-item>
@ -187,10 +166,6 @@
{{refundDetail.userIp}}
</el-descriptions-item>
<el-descriptions-item label="回调地址">{{ refundDetail.notifyUrl }}</el-descriptions-item>
<el-descriptions-item label="回调状态">
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="refundDetail.notifyStatus" />
</el-descriptions-item>
<el-descriptions-item label="回调时间">{{ parseTime(refundDetail.notifyTime) }}</el-descriptions-item>
</el-descriptions>
<el-divider></el-divider>
<el-descriptions :column="2" label-class-name="desc-label">
@ -211,7 +186,6 @@
<script>
import {getRefundPage, exportRefundExcel, getRefund} from "@/api/pay/refund";
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
import { PayOrderRefundStatusEnum, PayRefundStatusEnum } from "@/utils/constants";
import { getNowDateTime } from "@/utils/ruoyi";
const defaultRefundDetail = {
@ -230,8 +204,6 @@ const defaultRefundDetail = {
expireTime: null,
merchantOrderId: '',
merchantRefundNo: '',
notifyStatus: null,
notifyTime: null,
notifyUrl: '',
orderId: null,
payPrice: null,
@ -274,7 +246,6 @@ export default {
merchantOrderId: null,
merchantRefundNo: null,
notifyUrl: null,
notifyStatus: null,
status: null,
type: null,
payPrice: null,
@ -288,7 +259,6 @@ export default {
channelExtras: null,
expireTime: [],
successTime: [],
notifyTime: [],
createTime: []
},
//
@ -301,12 +271,8 @@ export default {
payChannelCodeDictDatum: getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE),
// 退
payRefundOrderDictDatum: getDictDatas(DICT_TYPE.PAY_REFUND_ORDER_STATUS),
// 退
payRefundOrderTypeDictDatum: getDictDatas(DICT_TYPE.PAY_REFUND_ORDER_TYPE),
//
payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_ORDER_NOTIFY_STATUS),
// el-tag退type
refundStatusType: '',
payOrderNotifyDictDatum: getDictDatas(DICT_TYPE.PAY_NOTIFY_STATUS),
// 退
refundDetail: JSON.parse(JSON.stringify(defaultRefundDetail)),
};
@ -379,40 +345,6 @@ export default {
this.merchantLoading = false;
});
},
/**
* 根据退款类别得到样式名称
* @param refundType 退款类别
*/
findByRefundTypeGetStyle(refundType) {
switch (refundType) {
case PayOrderRefundStatusEnum.NO.status:
return "success";
case PayOrderRefundStatusEnum.SOME.status:
return "warning";
case PayOrderRefundStatusEnum.ALL.status:
return "danger";
}
},
/**
* 根据退款状态得到样式名称
* @param refundStatus 退款状态
*/
findByRefundStatusGetStyle(refundStatus) {
switch (refundStatus) {
case PayRefundStatusEnum.CREATE.status:
return "info";
case PayRefundStatusEnum.SUCCESS.status:
return "success";
case PayRefundStatusEnum.FAILURE.status:
case PayRefundStatusEnum.CLOSE.status:
return "danger";
case PayRefundStatusEnum.PROCESSING_NOTIFY.status:
case PayRefundStatusEnum.PROCESSING_QUERY.status:
case PayRefundStatusEnum.UNKNOWN_RETRY.status:
case PayRefundStatusEnum.UNKNOWN_QUERY.status:
return "warning";
}
},
/**
* 查看订单详情
*/