feat 订单增加分账状态

This commit is contained in:
xxm1995
2024-04-08 17:33:40 +08:00
parent ccbc09cb84
commit 979b94e3de
29 changed files with 233 additions and 46 deletions

View File

@@ -0,0 +1,58 @@
@startuml
map PayOrder {
名称 => 支付订单
id => 主键
}
map PayChannelOrder {
名称 => 通道支付订单
paymentId => 支付id
}
map PayOrderExtra {
名称 => 支付订单扩展
id => 主键
}
map RefundOrder {
名称 => 退款订单
id => 主键
paymentId => 支付id
}
map RefundOrderExtra {
名称 => 退款订单扩展
id => 主键
}
map RefundChannelOrder {
名称 => 通道退款订单
refundId => 退款id
payChannelId => 通道支付订单id
}
map AllocationOrder {
名称 => 分账订单
paymentId => 支付id
}
map AllocationOrderDetail {
名称 => 分账订单明细
allocationId => 分账id
}
'支付订单关联
PayOrder::id <-- PayOrderExtra::id: 一对一关联
PayOrder::id <-- PayChannelOrder::paymentId: 多对一关联
'退款订单
RefundOrder::id <-- RefundOrderExtra::id: 一对一关联
RefundOrder::id <-- RefundChannelOrder::refundId: 多对一关联
RefundOrder::paymentId <-- PayOrder::id: 多对一关联
RefundChannelOrder::payChannelId --> PayChannelOrder::id: 多对一关联
'分账订单
AllocationOrder::paymentId --> PayOrder::id: 多对一关联
AllocationOrder::id --> AllocationOrderDetail::allocationId: 多对一关联
@enduml

View File

@@ -0,0 +1,19 @@
@startuml
progress : 支付中
success : 成功
close : 支付关闭
refunding : 退款中
partial_refund : 部分退款
refunded : 全部退款
fail : 失败
[*] --> progress
progress --> success
progress --> close
progress --> fail
success --> refunding
refunding --> partial_refund
refunding --> refunded
@enduml

View File

@@ -0,0 +1,45 @@
@startuml
object PayOrder
object RefundOrder
map CallbackRecord{
名称 => 回调记录
orderId => 订单Id
}
map CloseRecord{
名称 => 支付订单关闭记录
paymentId => 支付id
}
map RepairRecord{
名称 => 修复记录
orderId => 订单Id(支付/退款)
repairNo => 修复号(不唯一)
}
map SyncRecord{
名称 => 同步记录
orderId => 订单Id(支付/退款)
repairOrderNo => 修复号
}
'回调记录
CallbackRecord::orderId --> PayOrder::id: 多对一关联
CallbackRecord::orderId --> RefundOrder::id: 多对一关联
'支付订单关闭记录
CloseRecord::paymentId --> PayOrder::id: 一对一关联
'修复记录
RepairRecord::orderId --> PayOrder::id: 多对一关联
RepairRecord::orderId --> RefundOrder::id: 多对一关联
'同步记录
SyncRecord::orderId --> PayOrder::id: 多对一关联
SyncRecord::orderId --> RefundOrder::id: 多对一关联
SyncRecord::repairNo --> RepairRecord::repairNo: 一对一关联
@enduml

View File

@@ -0,0 +1,13 @@
@startuml
progress : 退款中
success : 成功
close : 关闭
fail : 失败
[*] --> progress
progress --> success
progress --> close
progress --> fail
@enduml

View File

@@ -0,0 +1,11 @@
@startuml
'https://plantuml.com/activity-diagram-beta
start
:发起支付;
:基础参数校验;
switch (订单类型?)
stop
@enduml

View File

@@ -40,7 +40,7 @@ public class PayAllocationTest {
param.setBusinessNo("P"+ RandomUtil.randomNumbers(5));
param.setAmount(10);
param.setTitle("测试分账支付");
param.setChannel(PayChannelEnum.ALI.getCode());
param.setChannel(PayChannelEnum.WECHAT.getCode());
param.setPayWay(PayWayEnum.QRCODE.getCode());
param.setClientIp("127.0.0.1");
param.setNotNotify(true);

View File

@@ -5,7 +5,6 @@ import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.dto.LabelValue;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.param.pay.allocation.AllocationStartParam;
import cn.bootx.platform.daxpay.service.core.order.allocation.service.AllocationOrderService;
import cn.bootx.platform.daxpay.service.core.payment.allocation.service.AllocationService;
import cn.bootx.platform.daxpay.service.dto.order.allocation.AllocationOrderDetailDto;
@@ -15,7 +14,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -28,7 +26,7 @@ import java.util.List;
*/
@Tag(name = "对账订单控制器")
@RestController
@RequestMapping("/allocation/order")
@RequestMapping("/order/allocation")
@RequiredArgsConstructor
public class AllocationOrderController {
@@ -67,13 +65,4 @@ public class AllocationOrderController {
return Res.ok(allocationOrderService.findChannels());
}
@Operation(summary = "发起分账")
@PostMapping("/start")
public ResResult<Void> start(Long paymentId){
AllocationStartParam param = new AllocationStartParam();
param.setPaymentId(paymentId);
allocationService.allocation(param);
return Res.ok();
}
}

View File

@@ -7,11 +7,13 @@ import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.param.pay.PayCloseParam;
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
import cn.bootx.platform.daxpay.param.pay.allocation.AllocationStartParam;
import cn.bootx.platform.daxpay.result.pay.SyncResult;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderExtraService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.payment.allocation.service.AllocationService;
import cn.bootx.platform.daxpay.service.core.payment.close.service.PayCloseService;
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayChannelOrderDto;
@@ -44,6 +46,7 @@ public class PayOrderController {
private final PayCloseService PayCloseService;
private final PaySyncService paySyncService;
private final AllocationService allocationService;
@Operation(summary = "分页查询")
@GetMapping("/page")
@@ -93,4 +96,13 @@ public class PayOrderController {
PayCloseService.close(param);
return Res.ok();
}
@Operation(summary = "发起分账")
@PostMapping("/allocation")
public ResResult<Void> allocation(Long id){
AllocationStartParam param = new AllocationStartParam();
param.setPaymentId(id);
allocationService.allocation(param);
return Res.ok();
}
}

View File

@@ -33,7 +33,7 @@ public class AliPayAllocationService {
// 分账主体参数
AlipayTradeOrderSettleModel model = new AlipayTradeOrderSettleModel();
model.setOutRequestNo(String.valueOf(allocationOrder.getOutReqNo()));
model.setOutRequestNo(String.valueOf(allocationOrder.getOrderNo()));
model.setTradeNo(allocationOrder.getGatewayPayOrderNo());
model.setRoyaltyMode("async");
@@ -59,7 +59,7 @@ public class AliPayAllocationService {
public void finish(AllocationOrder allocationOrder){
// 分账主体参数
AlipayTradeOrderSettleModel model = new AlipayTradeOrderSettleModel();
model.setOutRequestNo(String.valueOf(allocationOrder.getOutReqNo()));
model.setOutRequestNo(String.valueOf(allocationOrder.getOrderNo()));
model.setTradeNo(allocationOrder.getGatewayPayOrderNo());
// 分账完结参数
SettleExtendParams extendParams = new SettleExtendParams();

View File

@@ -80,7 +80,7 @@ public class WeChatPayAllocationService {
.appid(config.getWxAppId())
.nonce_str(WxPayKit.generateStr())
.transaction_id(allocationOrder.getGatewayPayOrderNo())
.out_order_no(allocationOrder.getOutReqNo())
.out_order_no(allocationOrder.getOrderNo())
.description("分账完成")
.build()
.createSign(config.getApiKeyV2(), SignType.HMACSHA256);

View File

@@ -22,6 +22,6 @@ public class AllocationOrderDetailManager extends BaseManager<AllocationOrderDet
* 根据订单ID查询
*/
public List<AllocationOrderDetail> findAllByOrderId(Long orderId) {
return findAllByField(AllocationOrderDetail::getOrderId, orderId);
return findAllByField(AllocationOrderDetail::getAllocationId, orderId);
}
}

View File

@@ -32,6 +32,12 @@ import java.time.LocalDateTime;
@TableName("pay_allocation_order")
public class AllocationOrder extends MpBaseEntity implements EntityBaseFunction<AllocationOrderDto> {
/**
* 分账订单号
*/
@DbColumn(comment = "分账订单号")
private String orderNo;
/**
* 支付订单ID
*/
@@ -64,12 +70,6 @@ public class AllocationOrder extends MpBaseEntity implements EntityBaseFunction<
private String allocationNo;
/**
* 外部请求号
*/
@DbColumn(comment = "外部请求号")
private String outReqNo;
/**
* 所属通道
* @see PayChannelEnum

View File

@@ -27,7 +27,7 @@ public class AllocationOrderDetail extends MpBaseEntity implements EntityBaseFun
/** 分账订单ID */
@DbColumn(comment = "分账订单ID")
private Long orderId;
private Long allocationId;
/** 接收者ID */
@DbColumn(comment = "接收者ID")

View File

@@ -19,6 +19,7 @@ import cn.bootx.platform.daxpay.service.dto.allocation.AllocationGroupReceiverRe
import cn.bootx.platform.daxpay.service.dto.order.allocation.AllocationOrderDetailDto;
import cn.bootx.platform.daxpay.service.dto.order.allocation.AllocationOrderDto;
import cn.bootx.platform.daxpay.service.param.order.AllocationOrderQuery;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
@@ -87,7 +88,7 @@ public class AllocationOrderService {
* 生成分账订单
*/
@Transactional(rollbackFor = Exception.class)
public OrderAndDetail create(AllocationStartParam param, PayOrder payOrder, int orderAmount, List<AllocationGroupReceiverResult> receiversByGroups){
public OrderAndDetail createAndUpdate(AllocationStartParam param, PayOrder payOrder, int orderAmount, List<AllocationGroupReceiverResult> receiversByGroups){
long orderId = IdUtil.getSnowflakeNextId();
// 请求号不存在使用订单ID
@@ -103,7 +104,7 @@ public class AllocationOrderService {
Integer rate = o.getRate();
Integer amount = orderAmount * rate / 10000;
AllocationOrderDetail detail = new AllocationOrderDetail();
detail.setOrderId(orderId)
detail.setAllocationId(orderId)
.setReceiverId(o.getId())
.setStatus(AllocationStatusEnum.WAITING.getCode())
.setAmount(amount)
@@ -125,16 +126,23 @@ public class AllocationOrderService {
.setAllocationNo(allocationNo)
.setChannel(payOrder.getAsyncChannel())
.setGatewayPayOrderNo(payOrder.getGatewayOrderNo())
.setOutReqNo(String.valueOf(orderId))
.setOrderNo(String.valueOf(orderId))
.setDescription(param.getDescription())
.setStatus(AllocationStatusEnum.WAITING.getCode())
.setAmount(sumAmount);
allocationOrder.setId(orderId);
// 保存
// 因为加密后字段值会发生变更, 所以在保存前备份一下
List<AllocationOrderDetail> detailsBack = details.stream()
.map(o -> {
AllocationOrderDetail allocationOrderDetail = new AllocationOrderDetail();
BeanUtil.copyProperties(o, allocationOrderDetail);
return allocationOrderDetail;
})
.collect(Collectors.toList());
allocationOrderDetailManager.saveAll(details);
allocationOrderManager.save(allocationOrder);
return new OrderAndDetail().setOrder(allocationOrder).setDetails(details);
return new OrderAndDetail().setOrder(allocationOrder).setDetails(detailsBack);
}
}

View File

@@ -56,6 +56,7 @@ public class PayBuilder {
.setBusinessNo(payParam.getBusinessNo())
.setTitle(payParam.getTitle())
.setStatus(PayStatusEnum.PROGRESS.getCode())
.setAllocation(payParam.isAllocation())
.setAmount(sumAmount)
.setExpiredTime(expiredTime)
.setCombinationPay(payParam.getPayChannels().size() > 1)

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.code.AllocationStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.convert.PayOrderConvert;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderDto;
import cn.bootx.table.modify.annotation.DbColumn;
@@ -80,6 +81,13 @@ public class PayOrder extends MpBaseEntity implements EntityBaseFunction<PayOrde
@DbColumn(comment = "支付状态")
private String status;
/**
* 分账状态
* @see AllocationStatusEnum
*/
@DbColumn(comment = "分账状态")
private String allocationStatus;
/** 支付时间 */
@DbColumn(comment = "支付时间")
@TableField(updateStrategy = FieldStrategy.ALWAYS)

View File

@@ -2,6 +2,8 @@ package cn.bootx.platform.daxpay.service.core.payment.allocation.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.allocation.AliPayAllocationStrategy;
import cn.bootx.platform.daxpay.service.core.payment.allocation.strategy.allocation.WeChatPayAllocationStrategy;
import cn.bootx.platform.daxpay.service.func.AbsAllocationStrategy;
import cn.hutool.extra.spring.SpringUtil;
import lombok.experimental.UtilityClass;
@@ -24,10 +26,10 @@ public class AllocationFactory {
AbsAllocationStrategy strategy;
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AbsAllocationStrategy.class);
strategy = SpringUtil.getBean(AliPayAllocationStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(AbsAllocationStrategy.class);
strategy = SpringUtil.getBean(WeChatPayAllocationStrategy.class);
break;
default:
throw new PayUnsupportedMethodException();

View File

@@ -70,8 +70,8 @@ public class AllocationService {
}
List<AllocationGroupReceiverResult> receiversByGroups = allocationGroupService.findReceiversByGroups(allocationGroup.getId());
// 创建分账单和明细并保存, 使用事务
OrderAndDetail orderAndDetail = allocationOrderService.create(param ,payOrder, channelOrder.getAmount(), receiversByGroups);
// 创建分账单和明细并保存, 同时更新支付订单状态 使用事务
OrderAndDetail orderAndDetail = allocationOrderService.createAndUpdate(param ,payOrder, channelOrder.getAmount(), receiversByGroups);
// 创建分账策略并初始化
AbsAllocationStrategy allocationStrategy = AllocationFactory.create(payOrder.getAsyncChannel());
@@ -120,7 +120,7 @@ public class AllocationService {
allocationStrategy.doBeforeHandler();
try {
// 分账处理
allocationStrategy.allocation();
allocationStrategy.finish();
// 执行中
allocationOrder.setStatus(AllocationStatusEnum.FINISH_PROCESSING.getCode())
.setErrorMsg(null);
@@ -151,6 +151,10 @@ public class AllocationService {
if (!payOrder.isAllocation()){
throw new PayFailureException("该订单不允许分账");
}
// 判断分账状态
if (Objects.equals(AllocationStatusEnum.FINISH_SUCCESS.getCode(), payOrder.getAllocationStatus())){
throw new PayFailureException("该订单已分账完成");
}
return payOrder;
}

View File

@@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.SimplePayParam;
import cn.bootx.platform.daxpay.result.pay.PayResult;
import cn.bootx.platform.daxpay.service.code.AllocationStatusEnum;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.builder.PayBuilder;
@@ -270,6 +271,10 @@ public class PayService {
payOrderExtraManager.update(payOrderExtra);
// 如果支付完成 发送通知
if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){
// 如果是是允许分账的订单, 设置为待分装
if (payOrder.isAllocation()){
payOrder.setAllocationStatus(AllocationStatusEnum.WAITING.getCode());
}
clientNoticeService.registerPayNotice(payOrder, payOrderExtra, payInfo.getPayChannelOrders());
}
return PayBuilder.buildPayResultByPayOrder(payOrder);
@@ -368,6 +373,10 @@ public class PayService {
payOrderExtraManager.update(payOrderExtra);
// 如果支付完成 发送通知
if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){
// 如果是是允许分账的订单, 设置为待分装
if (payOrder.isAllocation()){
payOrder.setAllocationStatus(AllocationStatusEnum.WAITING.getCode());
}
clientNoticeService.registerPayNotice(payOrder, payOrderExtra, payInfo.getPayChannelOrders());
}
return PayBuilder.buildPayResultByPayOrder(payOrder);

View File

@@ -8,14 +8,14 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
* 分账组
* @author xxm
* @since 2024/4/1
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "")
@Schema(title = "分账组")
public class AllocationGroupDto extends BaseDto {
@Schema(description = "名称")

View File

@@ -11,14 +11,14 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 分账接收方参数
* 分账接收方
* @author xxm
* @since 2024/3/28
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "分账接收方参数")
@Schema(title = "分账接收方")
public class AllocationReceiverDto extends BaseDto {
@Schema(description = "主键")

View File

@@ -22,7 +22,7 @@ public class AllocationOrderDetailDto extends BaseDto {
/** 分账订单ID */
@Schema(description = "分账订单ID")
private Long orderId;
private Long allocationId;
/** 接收者ID */
@Schema(description = "接收者ID")

View File

@@ -20,6 +20,13 @@ import java.time.LocalDateTime;
@Accessors(chain = true)
@Schema(title = "分账订单")
public class AllocationOrderDto extends BaseDto {
/**
* 分账订单号
*/
@Schema(description = "分账订单号")
private String orderNo;
/**
* 支付订单ID
*/
@@ -50,13 +57,6 @@ public class AllocationOrderDto extends BaseDto {
@Schema(description = "分账单号")
private String allocationNo;
/**
* 外部请求号
*/
@Schema(description = "外部请求号")
private String outReqNo;
/**
* 所属通道
*/

View File

@@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.dto.order.pay;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.code.AllocationStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -68,6 +69,13 @@ public class PayOrderDto extends BaseDto {
@Schema(description = "支付状态")
private String status;
/**
* 分账状态
* @see AllocationStatusEnum
*/
@Schema(description = "分账状态")
private String allocationStatus;
/** 支付时间 */
@Schema(description = "支付时间")
private LocalDateTime payTime;