ref 对账单逻辑调整

This commit is contained in:
bootx
2024-05-03 01:37:53 +08:00
parent ec353e0e69
commit 6dbee90a85
32 changed files with 260 additions and 218 deletions

View File

@@ -16,10 +16,15 @@
- [ ] 金额显示统一使用元
- [x] 使用切面统一处理API调用异常, 做统一包装返回
- [x] 支付接口优化
- [ ] 前端增加对应的枚举
- [] 前端增加对应的枚举
- [ ] 优化自动分账逻辑
- [ ] 前端查询条件适配
- [ ] paymentId替换
- [x] paymentId替换
- [ ] 对账单改造
- [ ] 去除对账单明细按钮
- [ ] 增加对账结果显示
- [ ] 增加下载原始对账单功能
- [ ] 增加下载系统对账单功能
2.0.7: 分账完善和基础架构优化
- [ ] 资金流水优化

View File

@@ -36,8 +36,8 @@ public class CashierController {
@Operation(summary = "查询支付订单")
@GetMapping("/queryPayOrderSuccess")
public ResResult<Boolean> queryPayOrder(String businessNo){
return Res.ok(cashierService.queryPayOrderSuccess(businessNo));
public ResResult<Boolean> queryPayOrder(String bizOrderNoeNo){
return Res.ok(cashierService.queryPayOrderSuccess(bizOrderNoeNo));
}
@Operation(summary = "获取支付环境")

View File

@@ -114,9 +114,9 @@ public class CashierService {
/**
* 查询订单是否支付成功
*/
public boolean queryPayOrderSuccess(String businessNo){
public boolean queryPayOrderSuccess(String bizOrderNoeNo){
QueryPayParam queryPayOrderParam = new QueryPayParam();
queryPayOrderParam.setBizOrderNoeNo(businessNo);
queryPayOrderParam.setBizOrderNoeNo(bizOrderNoeNo);
DaxPayResult<PayOrderModel> execute = DaxPayKit.execute(queryPayOrderParam);
// 未查询到订单
if (execute.getCode() == 10010){

View File

@@ -17,14 +17,14 @@ import lombok.Setter;
@Setter
public class AllocationParam extends DaxPayRequest<AllocationModel> {
/** 支付单ID */
private Long paymentId;
/** 支付订单号 */
private String orderNo;
/** 业务号 */
private String businessNo;
/** 商户订单号 */
private String bizOrderNo;
/** 分账单号(保证唯一) */
private String allocationNo;
/** 商户分账单号(保证唯一) */
private String bizAllocationNo;
/** 分账描述 */
private String description;

View File

@@ -35,11 +35,11 @@ public class PayAllocationTest {
public void allocation() {
// 分账参数
AllocationParam param = new AllocationParam();
param.setAllocationNo("A"+ RandomUtil.randomNumbers(5));
param.setBizAllocationNo("A"+ RandomUtil.randomNumbers(5));
param.setDescription("测试分账");
param.setAllocationGroupId(1L);
param.setClientIp("127.0.0.1");
param.setPaymentId(1L);
param.setBizOrderNo("112324");
DaxPayResult<AllocationModel> execute = DaxPayKit.execute(param);
System.out.println(execute);

View File

@@ -34,8 +34,8 @@ public class PayCloseRecordController {
@Operation(summary = "查询单条")
@GetMapping("/findById")
public ResResult<PayCloseRecordDto> findById(Long paymentId){
return Res.ok(service.findById(paymentId));
public ResResult<PayCloseRecordDto> findById(Long id){
return Res.ok(service.findById(id));
}
}

View File

@@ -34,8 +34,8 @@ public class PaySyncRecordController {
@Operation(summary = "查询单条")
@GetMapping("/findById")
public ResResult<PaySyncRecordDto> findById(Long paymentId){
return Res.ok(service.findById(paymentId));
public ResResult<PaySyncRecordDto> findById(Long id){
return Res.ok(service.findById(id));
}
}

View File

@@ -8,7 +8,7 @@ import java.util.Arrays;
import java.util.Objects;
/**
* 支付同步结果
* 支付同步状态
*
* @author xxm
* @since 2021/4/21

View File

@@ -10,6 +10,7 @@ import lombok.Getter;
*/
@Getter
@AllArgsConstructor
@Deprecated
public enum AliPayRecordTypeEnum {
/** 支付 */

View File

@@ -20,7 +20,7 @@ import lombok.EqualsAndHashCode;
public class AliReconcileBillDetail extends MpIdEntity {
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
private Long reconcileId;
@Alias("支付宝交易号")
@DbColumn(comment = "支付宝交易号")
private String tradeNo;

View File

@@ -129,7 +129,7 @@ public class AliPayReconcileService {
*/
private void saveBillDetail(List<AliReconcileBillDetail> billDetails){
Long recordOrderId = PaymentContextLocal.get().getReconcileInfo().getReconcileOrder().getId();
billDetails.forEach(o->o.setRecordOrderId(recordOrderId));
billDetails.forEach(o->o.setReconcileId(recordOrderId));
reconcileBillDetailManager.saveAll(billDetails);
}
@@ -165,23 +165,23 @@ public class AliPayReconcileService {
// 默认为支付对账记录
ReconcileDetail reconcileDetail = new ReconcileDetail()
.setRecordOrderId(billDetail.getRecordOrderId())
.setOrderId(billDetail.getOutTradeNo())
.setReconcileId(billDetail.getReconcileId())
.setTradeNo(billDetail.getOutTradeNo())
.setType(ReconcileTradeEnum.PAY.getCode())
.setAmount(amount)
.setTitle(billDetail.getSubject())
.setGatewayOrderNo(billDetail.getTradeNo());
.setOutTradeNo(billDetail.getTradeNo());
// 时间
String endTime = billDetail.getEndTime();
if (StrUtil.isNotBlank(endTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATETIME_PATTERN);
reconcileDetail.setOrderTime(time);
reconcileDetail.setTradeTime(time);
}
// 退款覆盖更新对应的字段
if (Objects.equals(billDetail.getTradeType(), "退款")){
reconcileDetail.setOrderId(billDetail.getBatchNo())
reconcileDetail.setTradeNo(billDetail.getBatchNo())
.setType(ReconcileTradeEnum.REFUND.getCode());
}
return reconcileDetail;

View File

@@ -19,7 +19,7 @@ public class UnionReconcileBillDetail {
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
private Long reconcileId;
/** 交易代码 */
@DbColumn(comment = "交易代码")
private String tradeType;

View File

@@ -163,11 +163,11 @@ public class UnionPayReconcileService {
// 默认为支付对账记录
ReconcileDetail reconcileDetail = new ReconcileDetail()
.setTitle("未知")
.setRecordOrderId(billDetail.getRecordOrderId())
.setOrderId(billDetail.getOrderId())
.setReconcileId(billDetail.getReconcileId())
.setTradeNo(billDetail.getOrderId())
.setType(ReconcileTradeEnum.PAY.getCode())
.setAmount(amount)
.setGatewayOrderNo(billDetail.getQueryId());
.setOutTradeNo(billDetail.getQueryId());
// 时间, 从对账订单获取年份
LocalDate date = reconcileOrder.getDate();
@@ -176,7 +176,7 @@ public class UnionPayReconcileService {
String txnTime = year + billDetail.getTxnTime();
if (StrUtil.isNotBlank(txnTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(txnTime, DatePattern.PURE_DATETIME_PATTERN);
reconcileDetail.setOrderTime(time);
reconcileDetail.setTradeTime(time);
}
// 退款覆盖更新对应的字段
@@ -191,7 +191,7 @@ public class UnionPayReconcileService {
*/
private void save(List<UnionReconcileBillDetail> billDetails){
Long recordOrderId = PaymentContextLocal.get().getReconcileInfo().getReconcileOrder().getId();
billDetails.forEach(o->o.setRecordOrderId(recordOrderId));
billDetails.forEach(o->o.setReconcileId(recordOrderId));
unionReconcileBillDetailManager.saveAll(billDetails);
}
}

View File

@@ -1,9 +1,7 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.convert;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord;
import cn.bootx.platform.daxpay.service.dto.channel.wechat.WeChatPayConfigDto;
import cn.bootx.platform.daxpay.service.dto.channel.wechat.WeChatPayRecordDto;
import cn.bootx.platform.daxpay.service.param.channel.wechat.WeChatPayConfigParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -21,10 +19,6 @@ public interface WeChatConvert {
WeChatPayConfig convert(WeChatPayConfigParam in);
WeChatPayRecordDto convert(WeChatPayRecord in);
GeneralReconcileRecord convertReconcileRecord(WeChatPayRecord in);
WeChatPayConfigDto convert(WeChatPayConfig in);
}

View File

@@ -54,8 +54,6 @@ public class WechatPayReconcileService{
private final WxReconcileBillTotalManger reconcileBillTotalManger;
private final WxReconcileBillDetailManager reconcileBillDetailManager;
private final WeChatPayRecordManager recordManager;
/**
* 下载对账单并保存
* @param date 对账日期 yyyyMMdd 格式
@@ -165,15 +163,15 @@ public class WechatPayReconcileService{
public ReconcileDetail convert(WxReconcileBillDetail billDetail){
// 默认为支付对账记录
ReconcileDetail reconcileDetail = new ReconcileDetail()
.setRecordOrderId(billDetail.getRecordOrderId())
.setOrderId(billDetail.getMchOrderNo())
.setReconcileId(billDetail.getRecordOrderId())
.setTradeNo(billDetail.getMchOrderNo())
.setTitle(billDetail.getSubject())
.setGatewayOrderNo(billDetail.getWeiXinOrderNo());
.setOutTradeNo(billDetail.getWeiXinOrderNo());
// 交易时间
String transactionTime = billDetail.getTransactionTime();
if (StrUtil.isNotBlank(transactionTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(transactionTime, DatePattern.NORM_DATETIME_PATTERN);
reconcileDetail.setOrderTime(time);
reconcileDetail.setTradeTime(time);
}
// 支付
@@ -193,7 +191,7 @@ public class WechatPayReconcileService{
int amount = Math.abs(((int) v));
reconcileDetail.setType(ReconcileTradeEnum.REFUND.getCode())
.setAmount(amount)
.setOrderId(billDetail.getMchRefundNo());
.setTradeNo(billDetail.getMchRefundNo());
}
// TODO 已撤销, 暂时未处理
if (Objects.equals(billDetail.getStatus(), "REVOKED")){

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.param.order.PayOrderQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -12,7 +13,11 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 支付订单
@@ -50,10 +55,16 @@ public class PayOrderManager extends BaseManager<PayOrderMapper, PayOrder> {
}
/**
* 强制更新 (忽略版本号)
* 查询对账用订单记录(指定时间和状态的订单)
*/
public void updateForceById(PayOrder payOrder) {
payOrder.setVersion(null);
this.updateById(payOrder);
public List<PayOrder> findReconcile(LocalDateTime startTime, LocalDateTime endTime, PayStatusEnum...statusEnum) {
List<String> status = Arrays.stream(statusEnum)
.map(PayStatusEnum::getCode)
.collect(Collectors.toList());
return this.lambdaQuery()
.between(PayOrder::getPayTime, startTime, endTime)
.in(PayOrder::getStatus, status)
.list();
}
}

View File

@@ -21,6 +21,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
@@ -47,8 +49,8 @@ public class PayOrderQueryService {
/**
* 根据id查询
*/
public Optional<PayOrder> findById(Long paymentId) {
return payOrderManager.findById(paymentId);
public Optional<PayOrder> findById(Long orderId) {
return payOrderManager.findById(orderId);
}
/**
@@ -88,26 +90,14 @@ public class PayOrderQueryService {
if (StrUtil.isBlank(param.getBizOrderNoeNo()) && Objects.isNull(param.getOrderNo())){
throw new ValidationFailedException("业务号或支付单ID不能都为空");
}
// 查询支付单
PayOrder payOrder = null;
if (Objects.nonNull(param.getOrderNo())){
payOrder = payOrderManager.findByOrderNo(param.getOrderNo())
.orElseThrow(() -> new DataNotExistException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderManager.findByBizOrderNo(param.getBizOrderNoeNo())
.orElseThrow(() -> new DataNotExistException("未查询到支付订单"));
}
PayOrder payOrder = this.findByBizOrOrderNo(param.getBizOrderNoeNo(), param.getOrderNo())
.orElseThrow(() -> new DataNotExistException("未查询到支付订单"));
// 查询扩展数据
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(payOrder.getId())
.orElseThrow(() -> new PayFailureException("支付订单不完整"));
PayOrderResult payOrderResult = new PayOrderResult();
BeanUtil.copyProperties(payOrder, payOrderResult);
return payOrderResult;
}
}

View File

@@ -28,8 +28,8 @@ public class ReconcileDetailManager extends BaseManager<ReconcileDetailMapper, R
/**
* 根据订单id查询
*/
public List<ReconcileDetail> findAllByOrderId(Long orderId){
return this.findAllByField(ReconcileDetail::getRecordOrderId, orderId);
public List<ReconcileDetail> findAllByReconcileId(Long reconcileId){
return this.findAllByField(ReconcileDetail::getReconcileId, reconcileId);
}
/**

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.order.reconcile.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.core.order.reconcile.conver.ReconcileConvert;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDetailDto;
import cn.bootx.table.modify.annotation.DbColumn;
@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 通用支付对账记录
* 通用支付对账记录, 从三方系统下载的交易记录
* @author xxm
* @since 2024/1/18
*/
@@ -28,7 +28,7 @@ public class ReconcileDetail extends MpCreateEntity implements EntityBaseFunctio
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
private Long reconcileId;
/** 商品名称 */
@DbColumn(comment = "商品名称")
@@ -40,22 +40,22 @@ public class ReconcileDetail extends MpCreateEntity implements EntityBaseFunctio
/**
* 交易类型
* @see ReconcileTradeEnum
* @see PaymentTypeEnum
*/
@DbColumn(comment = "交易类型")
private String type;
/** 本地订单ID */
/** 本地交易号 */
@DbColumn(comment = "本地订单ID")
private String orderId;
private String tradeNo;
/** 网关订单号 - 支付宝/微信的订单号 */
@DbColumn(comment = "网关订单")
private String gatewayOrderNo;
/** 外部交易号 - 支付宝/微信的订单号 */
@DbColumn(comment = "外部交易")
private String outTradeNo;
/** 订单时间 */
@DbColumn(comment = "订单时间")
private LocalDateTime orderTime;
/** 交易时间 */
@DbColumn(comment = "交易时间")
private LocalDateTime tradeTime;
@Override

View File

@@ -35,15 +35,23 @@ public class ReconcileDiffRecord extends MpBaseEntity implements EntityBaseFunct
/** 对账单ID */
@DbColumn(comment = "对账单ID")
private Long recordId;
private Long ReconcileId;
/** 对账单明细ID */
@DbColumn(comment = "对账单明细ID")
private Long detailId;
/** 本地订单id */
@DbColumn(comment = "本地订单id")
private Long orderId;
/** 本地交易号 */
@DbColumn(comment = "本地交易号")
private String tradeNo;
/** 外部交易号 */
@DbColumn(comment = "外部交易号")
private String outOrderNo;
/** 交易时间 */
@DbColumn(comment = "交易时间")
private LocalDateTime tradeTime;
/** 订单标题 */
@DbColumn(comment = "订单标题")
@@ -73,17 +81,10 @@ public class ReconcileDiffRecord extends MpBaseEntity implements EntityBaseFunct
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
private List<ReconcileDiff> diffs;
/** 网关订单号 */
@DbColumn(comment = "网关订单号")
private String gatewayOrderNo;
/** 交易金额 */
@DbColumn(comment = "交易金额")
private Integer amount;
/** 订单时间 */
@DbColumn(comment = "订单时间")
private LocalDateTime orderTime;
@Override
public ReconcileDiffRecordDto toDto() {

View File

@@ -6,7 +6,6 @@ import cn.bootx.platform.daxpay.service.core.order.reconcile.conver.ReconcileCon
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileOrderDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -30,12 +29,9 @@ public class ReconcileOrder extends MpCreateEntity implements EntityBaseFunction
/**
* 批次号
* 规则:通道简称 + yyyyMMdd + 两位流水号
* 例子wx2024012001、ali2024012002
*/
@DbMySqlIndex(name="批次号索引")
@DbColumn(comment = "批次号")
private String batchNo;
@DbColumn(comment = "对账号")
private String reconcileNo;
/** 日期 */
@DbColumn(comment = "日期")
@@ -46,13 +42,22 @@ public class ReconcileOrder extends MpCreateEntity implements EntityBaseFunction
private String channel;
/** 是否下载成功 */
@DbColumn(comment = "是否下载成功")
private boolean down;
@DbColumn(comment = "是否下载或上传")
private boolean downOrUpload;
/** 是否比对完成 */
@DbColumn(comment = "是否比对完成")
private boolean compare;
/** 比对结果 */
@DbColumn(comment = "比对结果")
private String result;
/** 错误码 */
@DbColumn(comment = "错误码")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String errorCode;
/** 错误信息 */
@DbColumn(comment = "错误信息")
@TableField(updateStrategy = FieldStrategy.ALWAYS)

View File

@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.param.order.RefundOrderQuery;
@@ -14,8 +15,10 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 支付退款订单管理
@@ -68,4 +71,18 @@ public class RefundOrderManager extends BaseManager<RefundOrderMapper, RefundOrd
.eq(RefundOrder::getStatus, RefundStatusEnum.PROGRESS.getCode())
.list();
}
/**
* 查询对账用订单记录(指定时间和状态的订单)
*/
public List<RefundOrder> findReconcile(LocalDateTime startTime, LocalDateTime endTime, PayStatusEnum...statusEnum) {
List<String> status = Arrays.stream(statusEnum)
.map(PayStatusEnum::getCode)
.collect(Collectors.toList());
return this.lambdaQuery()
.between(RefundOrder::getFinishTime, startTime, endTime)
.in(RefundOrder::getStatus, status)
.list();
}
}

View File

@@ -27,9 +27,9 @@ public class PayExpiredTimeRepository {
/**
* 根据 token 存储对应的 ExpiredTokenKey
*/
public void store(Long paymentId, LocalDateTime expiredTime) {
public void store(Long payOderId, LocalDateTime expiredTime) {
long time = LocalDateTimeUtil.timestamp(expiredTime);
redisClient.zadd(KEY, String.valueOf(paymentId), time);
redisClient.zadd(KEY, String.valueOf(payOderId), time);
}
/**

View File

@@ -1,17 +1,17 @@
package cn.bootx.platform.daxpay.service.core.payment.reconcile.domain;
import cn.bootx.platform.daxpay.service.code.AliPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 通用对账记录对象用于与网关进行对账
* 通用交易对象对象用于与网关进行对账
* @author xxm
* @since 2024/3/1
*/
@Data
public class GeneralReconcileRecord {
public class GeneralTradeInfo {
/** 标题 */
private String title;
@@ -20,18 +20,16 @@ public class GeneralReconcileRecord {
/**
* 业务类型
* @see AliPayRecordTypeEnum
* @see PaymentTypeEnum
*/
private String type;
/** 本地订单号 */
private Long orderId;
/** 本地交易号 */
private String tradeNo;
/** 网关订单号 */
private String gatewayOrderNo;
/** 网关交易号 */
private String outTradeNo;
/** 网关完成时间 */
private LocalDateTime gatewayTime;
private LocalDateTime finishTime;
}

View File

@@ -18,6 +18,6 @@ public class ReconcileDiff {
/** 本地订单字段值 */
private String localValue;
/** 网关订单字段值 */
private String gatewayValue;
/** 对账单订单字段值 */
private String outValue;
}

View File

@@ -6,8 +6,6 @@ import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy;
import cn.hutool.extra.spring.SpringUtil;
import lombok.experimental.UtilityClass;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -19,14 +17,6 @@ import java.util.Objects;
@UtilityClass
public class ReconcileStrategyFactory {
/**
* 根据传入的支付类型批量创建策略
*/
public List<AbsReconcileStrategy> create() {
Map<String, AbsReconcileStrategy> beansOfType = SpringUtil.getBeansOfType(AbsReconcileStrategy.class);
return new ArrayList<>(beansOfType.values());
}
/**
* 根据传入的支付类型批量创建策略
*/

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileDiffTypeEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileDetailManager;
@@ -15,7 +15,7 @@ import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDif
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.core.order.reconcile.service.ReconcileDiffService;
import cn.bootx.platform.daxpay.service.core.order.reconcile.service.ReconcileOrderService;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralTradeInfo;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.factory.ReconcileStrategyFactory;
import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy;
@@ -57,7 +57,7 @@ public class ReconcileService {
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public ReconcileOrder create(LocalDate date, String channel) {
ReconcileOrder order = new ReconcileOrder()
.setBatchNo(OrderNoGenerateUtil.reconciliation())
.setReconcileNo(OrderNoGenerateUtil.reconciliation())
.setChannel(channel)
.setDate(date);
reconcileOrderManager.save(order);
@@ -88,7 +88,7 @@ public class ReconcileService {
reconcileStrategy.doBeforeHandler();
try {
reconcileStrategy.upload(file);
reconcileOrder.setDown(true)
reconcileOrder.setDownOrUpload(true)
.setErrorMsg(null);
reconcileOrderService.update(reconcileOrder);
} catch (Exception e) {
@@ -109,7 +109,7 @@ public class ReconcileService {
*/
public void downAndSave(ReconcileOrder reconcileOrder) {
// 如果对账单已经存在
if (reconcileOrder.isDown()){
if (reconcileOrder.isDownOrUpload()){
throw new PayFailureException("对账单文件已经下载或上传");
}
// 将对账订单写入到上下文中
@@ -120,12 +120,13 @@ public class ReconcileService {
reconcileStrategy.doBeforeHandler();
try {
reconcileStrategy.downAndSave();
reconcileOrder.setDown(true)
reconcileOrder.setDownOrUpload(true)
.setErrorMsg(null);
reconcileOrderService.update(reconcileOrder);
} catch (Exception e) {
log.error("下载对账单异常", e);
reconcileOrder.setErrorMsg("原因: " + e.getMessage());
// 本方法无事务, 更新信息不会被回滚
reconcileOrderService.update(reconcileOrder);
throw new RuntimeException(e);
}
@@ -150,7 +151,7 @@ public class ReconcileService {
*/
public void compare(ReconcileOrder reconcileOrder){
// 判断是否已经下载了对账单明细
if (!reconcileOrder.isDown()){
if (!reconcileOrder.isDownOrUpload()){
throw new PayFailureException("请先下载对账单");
}
// 是否对比完成
@@ -159,7 +160,7 @@ public class ReconcileService {
}
// 查询对账单
List<ReconcileDetail> reconcileDetails = reconcileDetailManager.findAllByOrderId(reconcileOrder.getId());
List<ReconcileDetail> reconcileDetails = reconcileDetailManager.findAllByReconcileId(reconcileOrder.getId());
// 构建对账策略
AbsReconcileStrategy reconcileStrategy = ReconcileStrategyFactory.create(reconcileOrder.getChannel());
// 初始化参数
@@ -167,8 +168,8 @@ public class ReconcileService {
reconcileStrategy.setReconcileDetails(reconcileDetails);
try {
// 执行比对任务, 获取对账差异记录并保存
List<GeneralReconcileRecord> generalReconcileRecord = reconcileStrategy.getGeneralReconcileRecord();
List<ReconcileDiffRecord> diffRecords = this.generateDiffRecord(reconcileOrder,generalReconcileRecord,reconcileDetails);
List<GeneralTradeInfo> generalTradeInfo = reconcileStrategy.getGeneralReconcileRecord();
List<ReconcileDiffRecord> diffRecords = this.generateDiffRecord(reconcileOrder, generalTradeInfo,reconcileDetails);
reconcileOrder.setCompare(true);
reconcileOrderService.update(reconcileOrder);
reconcileDiffService.saveAll(diffRecords);
@@ -186,72 +187,77 @@ public class ReconcileService {
* 2. 远程无, 本地有
* 3. 远程有, 本地有, 但状态不一致
*
* @param reconcileOrder 对账单
* @param localTrades 本地交易订单
* @param outDetails 远程对账明细
*/
private List<ReconcileDiffRecord> generateDiffRecord(ReconcileOrder reconcileOrder,
List<GeneralReconcileRecord> reconcileRecords,
List<ReconcileDetail> localDetails){
if (CollUtil.isEmpty(localDetails) || CollUtil.isEmpty(reconcileRecords)){
List<GeneralTradeInfo> localTrades,
List<ReconcileDetail> outDetails){
if (CollUtil.isEmpty(outDetails) || CollUtil.isEmpty(localTrades)){
return new ArrayList<>();
}
Map<String, ReconcileDetail> detailMap = localDetails.stream()
.collect(Collectors.toMap(ReconcileDetail::getOrderId, Function.identity(), CollectorsFunction::retainLatest));
// 差异内容
List<ReconcileDiffRecord> diffRecords = new ArrayList<>();
Map<Long, GeneralReconcileRecord> recordMap = reconcileRecords.stream()
.collect(Collectors.toMap(GeneralReconcileRecord::getOrderId, Function.identity(), CollectorsFunction::retainLatest));
// 对账与流水比对
for (ReconcileDetail detail : localDetails) {
// 判断本地流水有没有记录, 流水没有记录查询本地订单
GeneralReconcileRecord record = recordMap.get(Long.valueOf(detail.getOrderId()));
if (Objects.isNull(record)){
log.info("本地订单不存在: {}", detail.getOrderId());
// 三方对账订单
Map<String, ReconcileDetail> outDetailMap = outDetails.stream()
.collect(Collectors.toMap(ReconcileDetail::getTradeNo, Function.identity(), CollectorsFunction::retainLatest));
// 本地订单记录
Map<String, GeneralTradeInfo> localTradeMap = localTrades.stream()
.collect(Collectors.toMap(GeneralTradeInfo::getTradeNo, Function.identity(), CollectorsFunction::retainLatest));
// 对账与比对
for (ReconcileDetail outDetail : outDetails) {
// 判断本地有没有记录, 流水没有记录查询本地订单
GeneralTradeInfo localTrade = localTradeMap.get(outDetail.getTradeNo());
if (Objects.isNull(localTrade)){
log.info("本地订单不存在: {}", outDetail.getTradeNo());
ReconcileDiffRecord diffRecord = new ReconcileDiffRecord()
.setDiffType(ReconcileDiffTypeEnum.LOCAL_NOT_EXISTS.getCode())
.setOrderId(Long.valueOf(detail.getOrderId()))
.setDetailId(detail.getId())
.setRecordId(reconcileOrder.getId())
.setTitle(detail.getTitle())
.setOrderType(detail.getType())
.setGatewayOrderNo(detail.getGatewayOrderNo())
.setAmount(detail.getAmount())
.setOrderTime(detail.getOrderTime());
.setTradeNo(outDetail.getTradeNo())
.setDetailId(outDetail.getId())
.setReconcileId(reconcileOrder.getId())
.setTitle(outDetail.getTitle())
.setOrderType(outDetail.getType())
.setOutOrderNo(outDetail.getOutTradeNo())
.setAmount(outDetail.getAmount())
.setTradeTime(outDetail.getTradeTime());
diffRecords.add(diffRecord);
continue;
}
// 如果远程和本地都存在, 比对差异
List<ReconcileDiff> reconcileDiffs = this.reconcileDiff(detail, record);
List<ReconcileDiff> reconcileDiffs = this.reconcileDiff(outDetail, localTrade);
if (CollUtil.isNotEmpty(reconcileDiffs)) {
ReconcileDiffRecord diffRecord = new ReconcileDiffRecord()
.setReconcileId(reconcileOrder.getId())
.setDetailId(outDetail.getId())
.setDiffType(ReconcileDiffTypeEnum.NOT_MATCH.getCode())
.setOrderId(Long.valueOf(detail.getOrderId()))
.setDetailId(detail.getId())
.setRecordId(reconcileOrder.getId())
.setTitle(detail.getTitle())
.setOrderType(detail.getType())
.setGatewayOrderNo(detail.getGatewayOrderNo())
.setAmount(detail.getAmount())
.setTradeNo(outDetail.getTradeNo())
.setTitle(outDetail.getTitle())
.setOrderType(outDetail.getType())
.setOutOrderNo(outDetail.getOutTradeNo())
.setAmount(outDetail.getAmount())
.setDiffs(reconcileDiffs)
.setOrderTime(detail.getOrderTime());
.setTradeTime(outDetail.getTradeTime());
diffRecords.add(diffRecord);
}
}
// 流水与对账单比对, 找出本地有, 远程没有的记录
for (GeneralReconcileRecord gateway : reconcileRecords) {
ReconcileDetail detail = detailMap.get(String.valueOf(gateway.getOrderId()));
if (Objects.isNull(detail)){
// 本地与对账单比对, 找出本地有, 远程没有的记录
for (GeneralTradeInfo localTrade : localTrades) {
ReconcileDetail outDetail = outDetailMap.get(localTrade.getTradeNo());
if (Objects.isNull(outDetail)){
ReconcileDiffRecord diffRecord = new ReconcileDiffRecord()
.setDiffType(ReconcileDiffTypeEnum.LOCAL_NOT_EXISTS.getCode())
.setOrderId(gateway.getOrderId())
.setRecordId(reconcileOrder.getId())
.setTradeNo(localTrade.getTradeNo())
.setReconcileId(reconcileOrder.getId())
.setDetailId(null)
.setTitle(gateway.getTitle())
.setOrderType(gateway.getType())
.setGatewayOrderNo(gateway.getGatewayOrderNo())
.setAmount(gateway.getAmount())
.setOrderTime(gateway.getGatewayTime());
.setTitle(localTrade.getTitle())
.setOrderType(localTrade.getType())
.setOutOrderNo(localTrade.getOutTradeNo())
.setAmount(localTrade.getAmount())
.setTradeTime(localTrade.getFinishTime());
diffRecords.add(diffRecord);
log.info("远程订单不存在: {}", gateway.getOrderId());
log.info("远程订单不存在: {}", localTrade.getTradeNo());
continue;
}
}
@@ -260,34 +266,36 @@ public class ReconcileService {
/**
* 判断订单之间存在哪些差异
* @param outDetail 下载的对账订单
* @param localTrade 本地交易订单
*/
public List<ReconcileDiff> reconcileDiff(ReconcileDetail detail, GeneralReconcileRecord record){
public List<ReconcileDiff> reconcileDiff(ReconcileDetail outDetail, GeneralTradeInfo localTrade){
List<ReconcileDiff> diffs = new ArrayList<>();
// 判断类型是否相同
if (Objects.equals(detail.getType(), ReconcileTradeEnum.PAY.getCode())
&& !Objects.equals(record.getType(), AliPayRecordTypeEnum.PAY.getCode())){
log.warn("订单类型不一致: {},{}", detail.getType(), record.getType());
diffs.add(new ReconcileDiff().setFieldName("订单类型").setLocalValue(detail.getType()).setGatewayValue(record.getType()));
if (Objects.equals(outDetail.getType(), ReconcileTradeEnum.PAY.getCode())
&& !Objects.equals(localTrade.getType(), PaymentTypeEnum.PAY.getCode())){
log.warn("订单类型不一致: {},{}", outDetail.getType(), localTrade.getType());
diffs.add(new ReconcileDiff().setFieldName("订单类型").setLocalValue(outDetail.getType()).setOutValue(localTrade.getType()));
}
if (Objects.equals(detail.getType(), ReconcileTradeEnum.REFUND.getCode())
&& !Objects.equals(record.getType(), AliPayRecordTypeEnum.REFUND.getCode())){
log.warn("订单类型不一致: {},{}", detail.getType(), record.getType());
diffs.add(new ReconcileDiff().setFieldName("订单类型").setLocalValue(detail.getType()).setGatewayValue(record.getType()));
if (Objects.equals(outDetail.getType(), ReconcileTradeEnum.REFUND.getCode())
&& !Objects.equals(localTrade.getType(), PaymentTypeEnum.REFUND.getCode())){
log.warn("订单类型不一致: {},{}", outDetail.getType(), localTrade.getType());
diffs.add(new ReconcileDiff().setFieldName("订单类型").setLocalValue(outDetail.getType()).setOutValue(localTrade.getType()));
}
// 判断名称是否一致
if (!Objects.equals(detail.getTitle(), record.getTitle())){
log.warn("订单名称不一致: {},{}", detail.getTitle(), record.getTitle());
diffs.add(new ReconcileDiff().setFieldName("订单名称").setLocalValue(detail.getTitle()).setGatewayValue(record.getTitle()));
if (!Objects.equals(outDetail.getTitle(), localTrade.getTitle())){
log.warn("订单名称不一致: {},{}", outDetail.getTitle(), localTrade.getTitle());
diffs.add(new ReconcileDiff().setFieldName("订单名称").setLocalValue(outDetail.getTitle()).setOutValue(localTrade.getTitle()));
}
// 判断金额是否一致
if (!Objects.equals(detail.getAmount(), record.getAmount())){
log.warn("订单金额不一致: {},{}", detail.getAmount(), record.getAmount());
if (!Objects.equals(outDetail.getAmount(), localTrade.getAmount())){
log.warn("订单金额不一致: {},{}", outDetail.getAmount(), localTrade.getAmount());
diffs.add(new ReconcileDiff().setFieldName("订单金额")
.setLocalValue(String.valueOf(detail.getAmount()))
.setGatewayValue(String.valueOf(record.getAmount())));
.setLocalValue(String.valueOf(outDetail.getAmount()))
.setOutValue(String.valueOf(localTrade.getAmount())));
}
return diffs;
}

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayReconcileService;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralTradeInfo;
import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy;
import cn.hutool.core.date.DatePattern;
import lombok.RequiredArgsConstructor;
@@ -76,7 +76,7 @@ public class AlipayReconcileStrategy extends AbsReconcileStrategy {
* 获取通用对账对象, 将流水记录转换为对账对象
*/
@Override
public List<GeneralReconcileRecord> getGeneralReconcileRecord() {
public List<GeneralTradeInfo> getGeneralReconcileRecord() {
// 查询流水
LocalDateTime localDateTime = LocalDateTimeUtil.date2DateTime(this.getRecordOrder().getDate());
LocalDateTime start = LocalDateTimeUtil.beginOfDay(localDateTime);

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayReconcileService;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralTradeInfo;
import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.hutool.core.date.DateUtil;
@@ -74,7 +74,7 @@ public class UnionPayReconcileStrategy extends AbsReconcileStrategy {
* 获取通用对账对象, 将流水记录转换为对账对象
*/
@Override
public List<GeneralReconcileRecord> getGeneralReconcileRecord() {
public List<GeneralTradeInfo> getGeneralReconcileRecord() {
// 查询流水
LocalDateTime localDateTime = LocalDateTimeUtil.date2DateTime(this.getRecordOrder().getDate());
LocalDateTime start = LocalDateTimeUtil.beginOfDay(localDateTime);

View File

@@ -2,10 +2,13 @@ package cn.bootx.platform.daxpay.service.core.payment.reconcile.strategy;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WechatPayReconcileService;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralTradeInfo;
import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy;
import cn.hutool.core.date.DatePattern;
import lombok.RequiredArgsConstructor;
@@ -35,6 +38,8 @@ public class WechatPayReconcileStrategy extends AbsReconcileStrategy {
private final WeChatPayConfigService weChatPayConfigService;
private final PayOrderManager payOrderManager;
private WeChatPayConfig config;
/**
@@ -77,11 +82,18 @@ public class WechatPayReconcileStrategy extends AbsReconcileStrategy {
* 获取通用对账对象, 将流水记录转换为对账对象
*/
@Override
public List<GeneralReconcileRecord> getGeneralReconcileRecord() {
public List<GeneralTradeInfo> getGeneralReconcileRecord() {
// 查询流水
LocalDateTime localDateTime = LocalDateTimeUtil.date2DateTime(this.getRecordOrder().getDate());
LocalDateTime start = LocalDateTimeUtil.beginOfDay(localDateTime);
LocalDateTime end = LocalDateTimeUtil.endOfDay(localDateTime);
// 下载支付订单
List<PayOrder> payOrders = payOrderManager.findReconcile(start, end, PayStatusEnum.SUCCESS, PayStatusEnum.PARTIAL_REFUND, PayStatusEnum.REFUNDING, PayStatusEnum.REFUNDED);
// 下载退款订单
return null;
}

View File

@@ -23,13 +23,31 @@ public class AllocationOrderDto extends BaseDto {
* 分账订单号
*/
@Schema(description = "分账订单号")
private String orderNo;
private String allocationNo;
/**
* 支付订单ID
*/
@Schema(description = "支付订单ID")
private Long paymentId;
private Long orderId;
/**
* 支付订单号
*/
@Schema(description = "支付订单号")
private String orderNo;
/**
* 商户支付订单号
*/
@Schema(description = "商户支付订单号")
private String bizOrderNo;
/**
* 外部订单号
*/
@Schema(description = "外部订单号")
private String outOrderNo;
/**
* 支付订单标题
@@ -38,22 +56,10 @@ public class AllocationOrderDto extends BaseDto {
private String title;
/**
* 网关支付订单号
* 外部分账单号
*/
@Schema(description = "网关支付订单号")
private String gatewayPayOrderNo;
/**
* 网关分账单号
*/
@Schema(description = "网关分账单号")
private String gatewayAllocationNo;
/**
* 分账单号
*/
@Schema(description = "分账单号")
private String allocationNo;
@Schema(description = "外部分账单号")
private String outAllocationNo;
/**
* 所属通道
@@ -86,6 +92,13 @@ public class AllocationOrderDto extends BaseDto {
*/
@Schema(description = "分账处理结果")
private String result;
/**
* 错误码
*/
@Schema(description = "错误码")
private String errorCode;
/**
* 错误原因
*/

View File

@@ -2,12 +2,11 @@ package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralTradeInfo;
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.util.List;
/**
@@ -44,6 +43,6 @@ public abstract class AbsReconcileStrategy implements PayStrategy {
/**
* 获取通用对账对象, 将流水记录转换为对账对象
*/
public abstract List<GeneralReconcileRecord> getGeneralReconcileRecord();
public abstract List<GeneralTradeInfo> getGeneralReconcileRecord();
}