ref 对账改造,支持原始对账单保存和下载

This commit is contained in:
bootx
2024-05-05 01:37:40 +08:00
parent 21ee9424bd
commit 0fa7b41a20
29 changed files with 504 additions and 368 deletions

View File

@@ -18,12 +18,13 @@
- [x] 支付接口优化
- [x] 前端查询条件适配
- [x] paymentId替换
- [ ] 支付订单和退款订单页面添加实时金额汇总展示
- [ ] 对账单改造
- [x] 去除对账单明细按钮
- [x] 增加对账结果计算和显示
- [ ] 增加下载原始对账单功能
- [ ] 保存原始的文件
- [ ] 微信
- [ ] 增加下载原始对账单功能(保证将明细文件进行储存)
- [x] 保存原始的文件
- [x] 微信
- [x] 支付宝
- [x] 云闪付
- [ ] 解析原有文件, 可以转换为指定格式进行下载

View File

@@ -8,16 +8,14 @@ import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.service.core.order.reconcile.service.ReconcileDiffService;
import cn.bootx.platform.daxpay.service.core.order.reconcile.service.ReconcileQueryService;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.service.ReconcileService;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDetailDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffRecordDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileTradeDetailDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileOrderDto;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileDetailQuery;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileDiffQuery;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileOrderCreate;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileOrderQuery;
import cn.bootx.platform.daxpay.service.param.reconcile.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -50,27 +48,34 @@ public class ReconcileOrderController {
return Res.ok();
}
@Operation(summary = "手动上传对账单文件")
@Operation(summary = "手动上传交易对账单文件")
@PostMapping("/upload")
public ResResult<Void> upload(Long id, MultipartFile file){
reconcileService.upload(id,file);
public ResResult<Void> upload(ReconcileUploadParam param, MultipartFile file){
ValidationUtil.validateParam(param);
reconcileService.upload(param,file);
return Res.ok();
}
@Operation(summary = "手动触发对账单比对")
@Operation(summary = "手动触发交易对账单比对")
@PostMapping("/compare")
public ResResult<Void> compare(Long id){
reconcileService.compare(id);
return Res.ok();
}
@Operation(summary = "订单分页")
@Operation(summary = "下载原始交易对账单文件")
@GetMapping("/downOriginal")
public ResponseEntity<byte[]> downOriginal(Long id){
return reconcileService.downOriginal(id);
}
@Operation(summary = "对账单分页")
@GetMapping("/page")
public ResResult<PageResult<ReconcileOrderDto>> page(PageParam pageParam, ReconcileOrderQuery query){
return Res.ok(reconcileQueryService.page(pageParam, query));
}
@Operation(summary = "单详情")
@Operation(summary = "对账单详情")
@GetMapping("/findById")
public ResResult<ReconcileOrderDto> findById(Long id){
return Res.ok(reconcileQueryService.findById(id));
@@ -78,25 +83,25 @@ public class ReconcileOrderController {
@Operation(summary = "对账明细分页")
@GetMapping("/detail/page")
public ResResult<PageResult<ReconcileDetailDto>> pageDetail(PageParam pageParam, ReconcileDetailQuery query){
public ResResult<PageResult<ReconcileTradeDetailDto>> pageDetail(PageParam pageParam, ReconcileDetailQuery query){
return Res.ok(reconcileQueryService.pageDetail(pageParam, query));
}
@Operation(summary = "对账明细详情")
@GetMapping("/detail/findById")
public ResResult<ReconcileDetailDto> findDetailById(Long id){
public ResResult<ReconcileTradeDetailDto> findDetailById(Long id){
return Res.ok(reconcileQueryService.findDetailById(id));
}
@Operation(summary = "对账差异分页")
@GetMapping("/diff/page")
public ResResult<PageResult<ReconcileDiffRecordDto>> pageDiff(PageParam pageParam, ReconcileDiffQuery query){
public ResResult<PageResult<ReconcileDiffDto>> pageDiff(PageParam pageParam, ReconcileDiffQuery query){
return Res.ok(reconcileDiffService.page(pageParam, query));
}
@Operation(summary = "对账差异详情")
@GetMapping("/diff/findById")
public ResResult<ReconcileDiffRecordDto> findDiffById(Long id){
public ResResult<ReconcileDiffDto> findDiffById(Long id){
return Res.ok(reconcileDiffService.findById(id));
}
}

View File

@@ -1,8 +1,12 @@
package cn.bootx.platform.daxpay.service.code;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Objects;
/**
* 对账单文件
* @author xxm
@@ -12,11 +16,18 @@ import lombok.Getter;
@AllArgsConstructor
public enum ReconcileFileTypeEnum {
TOTAL("total", "汇总"),
DETAIL("detail", "明细"),
FUNDS("funds", "资金对账"),
TRADE("trade", "交易对账"),
ZIP("zip", "压缩包"),
;
private final String code;
private final String name;
public static ReconcileFileTypeEnum findByCode(String code){
return Arrays.stream(values())
.filter(value -> Objects.equals(value.getCode(), code))
.findFirst()
.orElseThrow(() -> new DataNotExistException("未知的对账文件类型"));
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.common.context;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -20,6 +20,6 @@ public class ReconcileLocal {
private ReconcileOrder reconcileOrder;
/** 通用支付对账记录 */
private List<ReconcileDetail> reconcileDetails;
private List<ReconcileTradeDetail> reconcileTradeDetails;
}

View File

@@ -12,7 +12,7 @@ import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliReconcileBill
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillDetail;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillTotal;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileFileManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileFile;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.hutool.core.date.DatePattern;
@@ -87,14 +87,15 @@ public class AliPayReconcileService {
// 获取对账单下载地址并下载
String url = response.getBillDownloadUrl();
byte[] bytes = HttpUtil.downloadBytes(url);
byte[] zipBytes = HttpUtil.downloadBytes(url);
// 使用 Apache commons-compress 包装流, 读取返回的对账CSV文件
ZipArchiveInputStream zipArchiveInputStream = new ZipArchiveInputStream(new ByteArrayInputStream(bytes),"GBK");
ZipArchiveInputStream zipArchiveInputStream = new ZipArchiveInputStream(new ByteArrayInputStream(zipBytes),"GBK");
ZipArchiveEntry entry;
List<AliReconcileBillDetail> billDetails = new ArrayList<>();
List<AliReconcileBillTotal> billTotals = new ArrayList<>();
while ((entry= zipArchiveInputStream.getNextZipEntry()) != null){
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zipArchiveInputStream,"GBK"));
byte[] bytes = IoUtil.readBytes(zipArchiveInputStream);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes),"GBK"));
List<String> strings = IoUtil.readLines(bufferedReader, new ArrayList<>());
String name = entry.getName();
if (StrUtil.endWith(name,"_业务明细(汇总).csv")){
@@ -104,12 +105,11 @@ public class AliPayReconcileService {
// 明细
billDetails = this.parseDetail(strings);
}
// 保存原始对账文件
this.saveOriginalFile(recordOrder, bytes);
}
// 保存原始对账文件
this.saveOriginalFile(recordOrder, bytes);
// 保存原始对账记录
this.save(billDetails, billTotals);
// 将原始交易明细对账记录转换通用结构并保存到上下文中
this.convertAndSave(billDetails);
@@ -162,25 +162,24 @@ public class AliPayReconcileService {
* 转换为通用对账记录对象
*/
private void convertAndSave(List<AliReconcileBillDetail> billDetails){
List<ReconcileDetail> collect = billDetails.stream()
List<ReconcileTradeDetail> collect = billDetails.stream()
.map(this::convert)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcileInfo().setReconcileDetails(collect);
PaymentContextLocal.get().getReconcileInfo().setReconcileTradeDetails(collect);
}
/**
* 转换为通用对账记录对象
*/
private ReconcileDetail convert(AliReconcileBillDetail billDetail){
private ReconcileTradeDetail convert(AliReconcileBillDetail billDetail){
// 金额
String orderAmount = billDetail.getOrderAmount();
double v = Double.parseDouble(orderAmount) * 100;
int amount = Math.abs(((int) v));
// 默认为支付对账记录
ReconcileDetail reconcileDetail = new ReconcileDetail()
ReconcileTradeDetail reconcileTradeDetail = new ReconcileTradeDetail()
.setReconcileId(billDetail.getReconcileId())
.setTradeNo(billDetail.getOutTradeNo())
.setType(ReconcileTradeEnum.PAY.getCode())
@@ -192,15 +191,15 @@ public class AliPayReconcileService {
String endTime = billDetail.getEndTime();
if (StrUtil.isNotBlank(endTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(endTime, DatePattern.NORM_DATETIME_PATTERN);
reconcileDetail.setTradeTime(time);
reconcileTradeDetail.setTradeTime(time);
}
// 退款覆盖更新对应的字段
if (Objects.equals(billDetail.getTradeType(), "退款")){
reconcileDetail.setTradeNo(billDetail.getBatchNo())
reconcileTradeDetail.setTradeNo(billDetail.getBatchNo())
.setType(ReconcileTradeEnum.REFUND.getCode());
}
return reconcileDetail;
return reconcileTradeDetail;
}
@@ -243,7 +242,7 @@ public class AliPayReconcileService {
PayChannelEnum channelEnum = PayChannelEnum.findByCode(reconcileOrder.getChannel());
String date = LocalDateTimeUtil.format(reconcileOrder.getDate(), DatePattern.PURE_DATE_PATTERN);
// 将原始文件进行保存 通道-日期
String fileName = StrUtil.format("{}-{}.zip", channelEnum.getName(),date);
String fileName = StrUtil.format("交易对账单-{}-{}.csv", channelEnum.getName(),date);
UploadPretreatment uploadPretreatment = fileStorageService.of(bytes);
if (StrUtil.isNotBlank(fileName)) {
uploadPretreatment.setOriginalFilename(fileName);
@@ -252,7 +251,7 @@ public class AliPayReconcileService {
String fileId = upload.getId();
ReconcileFile reconcileFile = new ReconcileFile().setFileId(Long.valueOf(fileId))
.setReconcileId(reconcileOrder.getId())
.setType(ReconcileFileTypeEnum.ZIP.getCode());
.setType(ReconcileFileTypeEnum.TRADE.getCode());
reconcileFileManager.save(reconcileFile);
}

View File

@@ -11,7 +11,7 @@ import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionReconcileBillDetailManager;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionReconcileBillDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileFileManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileFile;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
@@ -81,17 +81,18 @@ public class UnionPayReconcileService {
ZipArchiveEntry entry;
List<UnionReconcileBillDetail> billDetails = new ArrayList<>();
while ((entry= zipArchiveInputStream.getNextZipEntry()) != null){
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zipArchiveInputStream,"GBK"));
if (StrUtil.startWith(entry.getName(), RECONCILE_FILE_PREFIX)){
byte[] bytes = IoUtil.readBytes(zipArchiveInputStream);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes),"GBK"));
// 明细解析
List<String> strings = IoUtil.readLines(bufferedReader, new ArrayList<>());
billDetails = this.parseDetail(strings);
// 保存原始对账记录文件
this.saveOriginalFile(reconcileOrder,bytes);
} else {
// 汇总目前不进行处理
}
}
// 保存原始对账记录文件
this.saveOriginalFile(reconcileOrder,zipBytes);
// 保存原始对账记录
this.save(billDetails);
// 转换为通用对账记录对象
@@ -151,19 +152,19 @@ public class UnionPayReconcileService {
* 转换为通用对账记录对象
*/
private void convertAndSave(List<UnionReconcileBillDetail> billDetails){
List<ReconcileDetail> collect = billDetails.stream()
List<ReconcileTradeDetail> collect = billDetails.stream()
.map(this::convert)
// 只处理支付和退款的对账记录
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcileInfo().setReconcileDetails(collect);
PaymentContextLocal.get().getReconcileInfo().setReconcileTradeDetails(collect);
}
/**
* 转换为通用对账记录对象
*/
private ReconcileDetail convert(UnionReconcileBillDetail billDetail){
private ReconcileTradeDetail convert(UnionReconcileBillDetail billDetail){
ReconcileOrder reconcileOrder = PaymentContextLocal.get()
.getReconcileInfo()
.getReconcileOrder();
@@ -172,7 +173,7 @@ public class UnionPayReconcileService {
int amount = Integer.parseInt(orderAmount);
// 默认为支付对账记录
ReconcileDetail reconcileDetail = new ReconcileDetail()
ReconcileTradeDetail reconcileTradeDetail = new ReconcileTradeDetail()
.setTitle("未知")
.setReconcileId(billDetail.getReconcileId())
.setTradeNo(billDetail.getOrderId())
@@ -187,14 +188,14 @@ public class UnionPayReconcileService {
String txnTime = year + billDetail.getTxnTime();
if (StrUtil.isNotBlank(txnTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(txnTime, DatePattern.PURE_DATETIME_PATTERN);
reconcileDetail.setTradeTime(time);
reconcileTradeDetail.setTradeTime(time);
}
// 退款覆盖更新对应的字段
if (Objects.equals(billDetail.getTradeType(), UnionPayCode.RECONCILE_TYPE_REFUND)){
reconcileDetail.setType(ReconcileTradeEnum.REFUND.getCode());
reconcileTradeDetail.setType(ReconcileTradeEnum.REFUND.getCode());
}
return reconcileDetail;
return reconcileTradeDetail;
}
/**
@@ -213,7 +214,7 @@ public class UnionPayReconcileService {
PayChannelEnum channelEnum = PayChannelEnum.findByCode(reconcileOrder.getChannel());
String date = LocalDateTimeUtil.format(reconcileOrder.getDate(), DatePattern.PURE_DATE_PATTERN);
// 将原始文件进行保存 通道-日期
String fileName = StrUtil.format("{}-{}.zip", channelEnum.getName(),date);
String fileName = StrUtil.format("交易对账单-{}-{}.txt", channelEnum.getName(),date);
UploadPretreatment uploadPretreatment = fileStorageService.of(bytes);
if (StrUtil.isNotBlank(fileName)) {
uploadPretreatment.setOriginalFilename(fileName);
@@ -222,7 +223,7 @@ public class UnionPayReconcileService {
String fileId = upload.getId();
ReconcileFile reconcileFile = new ReconcileFile().setFileId(Long.valueOf(fileId))
.setReconcileId(reconcileOrder.getId())
.setType(ReconcileFileTypeEnum.ZIP.getCode());
.setType(ReconcileFileTypeEnum.TRADE.getCode());
reconcileFileManager.save(reconcileFile);
}
}

View File

@@ -14,7 +14,7 @@ import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileBi
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileBillTotal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileFundFlowDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileFileManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileFile;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.hutool.core.codec.Base64;
@@ -66,23 +66,24 @@ public class WechatPayReconcileService{
/**
* 下载对账单并保存
* @param date 对账日期 yyyyMMdd 格式
*
* @param recordOrder 对账订单
* @param date 对账日期 yyyyMMdd 格式
*/
@Transactional(rollbackFor = Exception.class)
public void downAndSave(String date, WeChatPayConfig config) {
// TODO 修改为现将对账单全部下载下来, 然后再进行保存和解析
this.downBill(date, config);
public void downAndSave(ReconcileOrder recordOrder, String date, WeChatPayConfig config) {
this.downBill(recordOrder, date, config);
// 资金对账单先不启用
this.downFundFlow(date, config);
// this.downFundFlow(date, config);
}
/**
* 下载交易账单
*
* @param date 对账日期 yyyyMMdd 格式
* @param recordOrder 对账订单
* @param date 对账日期 yyyyMMdd 格式
*/
public void downBill(String date, WeChatPayConfig config){
public void downBill(ReconcileOrder recordOrder, String date, WeChatPayConfig config){
// 下载交易账单
Map<String, String> params = DownloadBillModel.builder()
.mch_id(config.getWxMchId())
@@ -94,6 +95,9 @@ public class WechatPayReconcileService{
.createSign(config.getApiKeyV2(), SignType.HMACSHA256);
String result = WxPayApi.downloadBill(config.isSandbox(), params);
// 保存原始对账单文件
this.saveOriginalFile(recordOrder, result);
// 验证返回数据是否成功
this.verifyBillMsg(result);
@@ -153,30 +157,30 @@ public class WechatPayReconcileService{
.collect(Collectors.toList());
billDetails.forEach(o->o.setRecordOrderId(reconcileOrder.getId()));
// 保存原始对账单明细
reconcileBillDetailManager.saveAll(billDetails);
// 将原始交易明细对账记录转换通用结构并保存到上下文中
this.convertAndSave(billDetails);
// 保存原始对账单明细
reconcileBillDetailManager.saveAll(billDetails);
}
/**
* 转换为通用对账记录对象
*/
public void convertAndSave(List<WxReconcileBillDetail> billDetails){
List<ReconcileDetail> collect = billDetails.stream()
List<ReconcileTradeDetail> collect = billDetails.stream()
.map(this::convert)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcileInfo().setReconcileDetails(collect);
PaymentContextLocal.get().getReconcileInfo().setReconcileTradeDetails(collect);
}
/**
* 转换为通用对账记录对象
*/
public ReconcileDetail convert(WxReconcileBillDetail billDetail){
public ReconcileTradeDetail convert(WxReconcileBillDetail billDetail){
// 默认为支付对账记录
ReconcileDetail reconcileDetail = new ReconcileDetail()
ReconcileTradeDetail reconcileTradeDetail = new ReconcileTradeDetail()
.setReconcileId(billDetail.getRecordOrderId())
.setTradeNo(billDetail.getMchOrderNo())
.setTitle(billDetail.getSubject())
@@ -185,7 +189,7 @@ public class WechatPayReconcileService{
String transactionTime = billDetail.getTransactionTime();
if (StrUtil.isNotBlank(transactionTime)) {
LocalDateTime time = LocalDateTimeUtil.parse(transactionTime, DatePattern.NORM_DATETIME_PATTERN);
reconcileDetail.setTradeTime(time);
reconcileTradeDetail.setTradeTime(time);
}
// 支付
@@ -194,7 +198,7 @@ public class WechatPayReconcileService{
String orderAmount = billDetail.getOrderAmount();
double v = Double.parseDouble(orderAmount) * 100;
int amount = Math.abs(((int) v));
reconcileDetail.setType(ReconcileTradeEnum.PAY.getCode())
reconcileTradeDetail.setType(ReconcileTradeEnum.PAY.getCode())
.setAmount(amount);
}
// 退款
@@ -203,7 +207,7 @@ public class WechatPayReconcileService{
String refundAmount = billDetail.getApplyRefundAmount();
double v = Double.parseDouble(refundAmount) * 100;
int amount = Math.abs(((int) v));
reconcileDetail.setType(ReconcileTradeEnum.REFUND.getCode())
reconcileTradeDetail.setType(ReconcileTradeEnum.REFUND.getCode())
.setAmount(amount)
.setTradeNo(billDetail.getMchRefundNo());
}
@@ -211,7 +215,7 @@ public class WechatPayReconcileService{
if (Objects.equals(billDetail.getStatus(), "REVOKED")){
log.warn("对账出现已撤销记录, 后续进行处理");
}
return reconcileDetail;
return reconcileTradeDetail;
}
@@ -314,12 +318,12 @@ public class WechatPayReconcileService{
/**
* 保存下载的原始对账文件
*/
private void saveOriginalFile(ReconcileOrder reconcileOrder,String text, ReconcileFileTypeEnum fileType) {
private void saveOriginalFile(ReconcileOrder reconcileOrder, String text) {
// 微信接收结果转换为 byte[]
PayChannelEnum channelEnum = PayChannelEnum.findByCode(reconcileOrder.getChannel());
String date = LocalDateTimeUtil.format(reconcileOrder.getDate(), DatePattern.PURE_DATE_PATTERN);
// 将原始文件进行保存 通道-日期
String fileName = StrUtil.format("{}-{}-{}.text", channelEnum.getName(),date,fileType.getName());
String fileName = StrUtil.format("交易对账单-{}-{}.txt", channelEnum.getName(),date);
byte[] bytes = StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8);
UploadPretreatment uploadPretreatment = fileStorageService.of(bytes);
if (StrUtil.isNotBlank(fileName)) {
@@ -329,7 +333,7 @@ public class WechatPayReconcileService{
String fileId = upload.getId();
ReconcileFile reconcileFile = new ReconcileFile().setFileId(Long.valueOf(fileId))
.setReconcileId(reconcileOrder.getId())
.setType(fileType.getCode());
.setType(ReconcileFileTypeEnum.TRADE.getCode());
reconcileFileManager.save(reconcileFile);
}
}

View File

@@ -1,10 +1,10 @@
package cn.bootx.platform.daxpay.service.core.order.reconcile.conver;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiffRecord;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiff;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDetailDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffRecordDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileTradeDetailDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileOrderDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -18,9 +18,9 @@ import org.mapstruct.factory.Mappers;
public interface ReconcileConvert {
ReconcileConvert CONVERT = Mappers.getMapper(ReconcileConvert.class);
ReconcileDetailDto convert(ReconcileDetail in);
ReconcileTradeDetailDto convert(ReconcileTradeDetail in);
ReconcileDiffRecordDto convert(ReconcileDiffRecord in);
ReconcileDiffDto convert(ReconcileDiff in);
ReconcileOrderDto convert(ReconcileOrder in);
}

View File

@@ -4,7 +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.service.core.order.reconcile.entity.ReconcileDiffRecord;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiff;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileDiffQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -20,14 +20,14 @@ import org.springframework.stereotype.Repository;
@Slf4j
@Repository
@RequiredArgsConstructor
public class ReconcileDiffRecordManager extends BaseManager<ReconcileDiffRecordMapper, ReconcileDiffRecord> {
public class ReconcileDiffManager extends BaseManager<ReconcileDiffMapper, ReconcileDiff> {
/**
* 分页
*/
public Page<ReconcileDiffRecord> page(PageParam pageParam, ReconcileDiffQuery query){
Page<ReconcileDiffRecord> mpPage = MpUtil.getMpPage(pageParam, ReconcileDiffRecord.class);
QueryWrapper<ReconcileDiffRecord> generator = QueryGenerator.generator(query);
public Page<ReconcileDiff> page(PageParam pageParam, ReconcileDiffQuery query){
Page<ReconcileDiff> mpPage = MpUtil.getMpPage(pageParam, ReconcileDiff.class);
QueryWrapper<ReconcileDiff> generator = QueryGenerator.generator(query);
return this.page(mpPage,generator);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.order.reconcile.dao;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiffRecord;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiff;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2024/2/28
*/
@Mapper
public interface ReconcileDiffRecordMapper extends BaseMapper<ReconcileDiffRecord> {
public interface ReconcileDiffMapper extends BaseMapper<ReconcileDiff> {
}

View File

@@ -5,6 +5,8 @@ import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileFil
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
*
* @author xxm
@@ -13,4 +15,11 @@ import org.springframework.stereotype.Repository;
@Slf4j
@Repository
public class ReconcileFileManager extends BaseManager<ReconcileFileMapper, ReconcileFile> {
/**
* 根据对账单ID查询对账单文件
*/
public List<ReconcileFile> findAllByReconcileId(Long reconcileId) {
return findAllByField(ReconcileFile::getReconcileId, reconcileId);
}
}

View File

@@ -4,7 +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.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileDetailQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -22,22 +22,23 @@ import java.util.List;
@Slf4j
@Repository
@RequiredArgsConstructor
public class ReconcileDetailManager extends BaseManager<ReconcileDetailMapper, ReconcileDetail> {
public class ReconcileTradeDetailManager extends BaseManager<ReconcileTradeDetailMapper, ReconcileTradeDetail> {
/**
* 根据订单id查询
*/
public List<ReconcileDetail> findAllByReconcileId(Long reconcileId){
return this.findAllByField(ReconcileDetail::getReconcileId, reconcileId);
public List<ReconcileTradeDetail> findAllByReconcileId(Long reconcileId){
return this.findAllByField(ReconcileTradeDetail::getReconcileId, reconcileId);
}
/**
* 分页
*/
public Page<ReconcileDetail> page(PageParam pageParam, ReconcileDetailQuery query){
Page<ReconcileDetail> mpPage = MpUtil.getMpPage(pageParam, ReconcileDetail.class);
QueryWrapper<ReconcileDetail> generator = QueryGenerator.generator(query);
public Page<ReconcileTradeDetail> page(PageParam pageParam, ReconcileDetailQuery query){
Page<ReconcileTradeDetail> mpPage = MpUtil.getMpPage(pageParam, ReconcileTradeDetail.class);
QueryWrapper<ReconcileTradeDetail> generator = QueryGenerator.generator(query);
return this.page(mpPage,generator);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.order.reconcile.dao;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2024/1/20
*/
@Mapper
public interface ReconcileDetailMapper extends BaseMapper<ReconcileDetail> {
public interface ReconcileTradeDetailMapper extends BaseMapper<ReconcileTradeDetail> {
}

View File

@@ -6,8 +6,7 @@ import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileDiffTypeEnum;
import cn.bootx.platform.daxpay.service.common.typehandler.ReconcileDiffTypeHandler;
import cn.bootx.platform.daxpay.service.core.order.reconcile.conver.ReconcileConvert;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffRecordDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
@@ -31,16 +30,24 @@ import java.util.List;
@Accessors(chain = true)
@DbTable(comment = "对账差异单")
@TableName(value = "pay_reconcile_diff_record",autoResultMap = true)
public class ReconcileDiffRecord extends MpBaseEntity implements EntityBaseFunction<ReconcileDiffRecordDto> {
public class ReconcileDiff extends MpBaseEntity implements EntityBaseFunction<ReconcileDiffDto> {
/** 对账单ID */
@DbColumn(comment = "对账单ID")
private Long reconcileId;
/** 对账号 */
@DbColumn(comment = "对账号")
private String reconcileNo;
/** 对账单明细ID */
@DbColumn(comment = "对账单明细ID")
private Long detailId;
/** 对账日期 */
@DbColumn(comment = "对账日期")
private LocalDateTime reconcileDate;
/** 本地交易号 */
@DbColumn(comment = "本地交易号")
private String tradeNo;
@@ -67,11 +74,11 @@ public class ReconcileDiffRecord extends MpBaseEntity implements EntityBaseFunct
private Integer outAmount;
/**
* 订单类型
* 交易类型
* @see ReconcileTradeEnum
*/
@DbColumn(comment = "订单类型")
private String orderType;
@DbColumn(comment = "交易类型")
private String tradeType;
/**
* 差异类型
@@ -83,15 +90,15 @@ public class ReconcileDiffRecord extends MpBaseEntity implements EntityBaseFunct
/**
* 差异内容, 存储json字符串, 格式为
* {属性: '标题', 本地字段值:'标题1', 网关字段值: '标题2'}
* @see ReconcileDiff
* @see cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff
*/
@DbColumn(comment = "差异内容")
@TableField(typeHandler = ReconcileDiffTypeHandler.class)
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
private List<ReconcileDiff> diffs;
private List<cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff> diffs;
@Override
public ReconcileDiffRecordDto toDto() {
public ReconcileDiffDto toDto() {
return ReconcileConvert.CONVERT.convert(this);
}
}

View File

@@ -10,14 +10,14 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
*
* 原始对账单文件
* @author xxm
* @since 2024/5/3
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@DbTable(comment = "对账单文件")
@DbTable(comment = "原始对账单文件")
@TableName("pay_reconcile_file")
public class ReconcileFile extends MpIdEntity {

View File

@@ -18,7 +18,7 @@ import lombok.experimental.Accessors;
import java.time.LocalDate;
/**
* 支付对账单订单
* 支付对账单订单(交易对账单, 不包括资金对账)
* @author xxm
* @since 2024/1/18
*/
@@ -44,24 +44,16 @@ public class ReconcileOrder extends MpCreateEntity implements EntityBaseFunction
@DbColumn(comment = "通道")
private String channel;
/** 明细对账文件是否下载成功 */
/** 交易对账文件是否下载成功 */
@DbColumn(comment = "明细对账单下载")
private boolean detailDown;
private boolean downOrUpload;
/** 明细是否比对完成 */
/** 交易对账文件是否比对完成 */
@DbColumn(comment = "明细对账单比对")
private boolean detailCompare;
/** 汇总对账文件是否下载成功 */
@DbColumn(comment = "汇总对账单下载")
private boolean totalDown;
/** 汇总是否比对完成 */
@DbColumn(comment = "汇总对账单比对")
private boolean totalCompare;
private boolean compare;
/**
* 对账结果
* 交易对账结果
* @see ReconcileResultEnum
*/
@DbColumn(comment = "对账结果")

View File

@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
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.platform.daxpay.service.dto.order.reconcile.ReconcileTradeDetailDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -15,16 +15,16 @@ import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 通用支付对账记录, 从三方系统下载的交易记录
* 对账-第三方交易明细, 从三方系统下载的交易记录
* @author xxm
* @since 2024/1/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@DbTable(comment = "支付对账记录")
@TableName("pay_reconcile_detail")
public class ReconcileDetail extends MpCreateEntity implements EntityBaseFunction<ReconcileDetailDto> {
@DbTable(comment = "对账-第三方交易明细")
@TableName("pay_reconcile_trade_detail")
public class ReconcileTradeDetail extends MpCreateEntity implements EntityBaseFunction<ReconcileTradeDetailDto> {
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
@@ -59,7 +59,7 @@ public class ReconcileDetail extends MpCreateEntity implements EntityBaseFunctio
@Override
public ReconcileDetailDto toDto() {
public ReconcileTradeDetailDto toDto() {
return ReconcileConvert.CONVERT.convert(this);
}
}

View File

@@ -4,9 +4,9 @@ import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileDiffRecordManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiffRecord;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffRecordDto;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileDiffManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiff;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDiffDto;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileDiffQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -23,27 +23,26 @@ import java.util.List;
@Service
@RequiredArgsConstructor
public class ReconcileDiffService {
private final ReconcileDiffRecordManager recordManager;
private final ReconcileDiffManager recordManager;
/**
* 批量保存
*/
public void saveAll(List<ReconcileDiffRecord> diffRecords) {
public void saveAll(List<ReconcileDiff> diffRecords) {
recordManager.saveAll(diffRecords);
}
/**
* 分页查询
*/
public PageResult<ReconcileDiffRecordDto> page(PageParam pageParam, ReconcileDiffQuery query) {
public PageResult<ReconcileDiffDto> page(PageParam pageParam, ReconcileDiffQuery query) {
return MpUtil.convert2DtoPageResult(recordManager.page(pageParam, query));
}
/**
* 详情查询
*/
public ReconcileDiffRecordDto findById(Long id) {
return recordManager.findById(id).map(ReconcileDiffRecord::toDto).orElseThrow(() -> new DataNotExistException("不存在的对账差异单"));
public ReconcileDiffDto findById(Long id) {
return recordManager.findById(id).map(ReconcileDiff::toDto).orElseThrow(() -> new DataNotExistException("不存在的对账差异单"));
}
}

View File

@@ -4,12 +4,12 @@ import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileOrderManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileTradeDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileDetailDto;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileOrderDto;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileTradeDetailDto;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileDetailQuery;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileOrderQuery;
import lombok.RequiredArgsConstructor;
@@ -26,7 +26,7 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class ReconcileQueryService {
private final ReconcileOrderManager orderManager;
private final ReconcileDetailManager detailManager;
private final ReconcileTradeDetailManager detailManager;
/**
* 分页
@@ -46,16 +46,15 @@ public class ReconcileQueryService {
/**
* 明细分页
*/
public PageResult<ReconcileDetailDto> pageDetail(PageParam pageParam, ReconcileDetailQuery query){
public PageResult<ReconcileTradeDetailDto> pageDetail(PageParam pageParam, ReconcileDetailQuery query){
return MpUtil.convert2DtoPageResult(detailManager.page(pageParam, query));
}
/**
* 明细详情
*/
public ReconcileDetailDto findDetailById(Long id){
return detailManager.findById(id).map(ReconcileDetail::toDto)
public ReconcileTradeDetailDto findDetailById(Long id){
return detailManager.findById(id).map(ReconcileTradeDetail::toDto)
.orElseThrow(()->new DataNotExistException("对账详情不存在"));
}
}

View File

@@ -0,0 +1,201 @@
package cn.bootx.platform.daxpay.service.core.payment.reconcile.service;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileDiffTypeEnum;
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.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiff;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralTradeInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 对账支撑服务
* @author xxm
* @since 2024/5/4
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ReconcileAssistService {
private final PayOrderManager payOrderManager;
private final RefundOrderManager refundOrderManager;
/**
* 获取通用对账对象, 将支付/退款订单转换为对账对象
*/
public List<GeneralTradeInfo> getGeneralTradeInfoList(ReconcileOrder reconcileOrder){
List<GeneralTradeInfo> generalTradeInfoList = new ArrayList<>();
// 查询流水
LocalDateTime localDateTime = LocalDateTimeUtil.date2DateTime(reconcileOrder.getDate());
LocalDateTime start = LocalDateTimeUtil.beginOfDay(localDateTime);
LocalDateTime end = LocalDateTimeUtil.endOfDay(localDateTime);
// 下载支付订单
List<PayOrder> payOrders = payOrderManager.findReconcile(reconcileOrder.getChannel(), start, end, PayStatusEnum.SUCCESS, PayStatusEnum.PARTIAL_REFUND, PayStatusEnum.REFUNDING, PayStatusEnum.REFUNDED);
List<RefundOrder> refundOrders = refundOrderManager.findReconcile(reconcileOrder.getChannel(), start, end, RefundStatusEnum.SUCCESS);
for (PayOrder payOrder : payOrders) {
generalTradeInfoList.add(new GeneralTradeInfo()
.setTitle(payOrder.getTitle())
.setTradeNo(payOrder.getOrderNo())
.setOutTradeNo(payOrder.getOutOrderNo())
.setFinishTime(payOrder.getPayTime())
.setType(PaymentTypeEnum.PAY.getCode())
.setAmount(payOrder.getAmount())
);
}
for (RefundOrder refundOrder : refundOrders) {
generalTradeInfoList.add(new GeneralTradeInfo()
.setTitle(refundOrder.getTitle())
.setTradeNo(refundOrder.getOrderNo())
.setOutTradeNo(refundOrder.getOutOrderNo())
.setFinishTime(refundOrder.getFinishTime())
.setType(PaymentTypeEnum.REFUND.getCode())
.setAmount(refundOrder.getAmount()));
}
return generalTradeInfoList;
}
/**
* 比对生成对账差异单
* 1. 远程有, 本地无
* 2. 远程无, 本地有
* 3. 远程有, 本地有, 但状态不一致
*
* @param reconcileOrder 对账单
* @param localTrades 本地交易订单
* @param outDetails 远程对账明细
*/
public List<ReconcileDiff> generateDiffRecord(ReconcileOrder reconcileOrder,
List<GeneralTradeInfo> localTrades,
List<ReconcileTradeDetail> outDetails){
if (CollUtil.isEmpty(outDetails) || CollUtil.isEmpty(localTrades)){
return new ArrayList<>();
}
// 差异内容
List<ReconcileDiff> diffRecords = new ArrayList<>();
// 三方对账订单
Map<String, ReconcileTradeDetail> outDetailMap = outDetails.stream()
.collect(Collectors.toMap(ReconcileTradeDetail::getTradeNo, Function.identity(), CollectorsFunction::retainLatest));
// 本地订单记录
Map<String, GeneralTradeInfo> localTradeMap = localTrades.stream()
.collect(Collectors.toMap(GeneralTradeInfo::getTradeNo, Function.identity(), CollectorsFunction::retainLatest));
// 对账与比对
for (ReconcileTradeDetail outDetail : outDetails) {
// 判断本地有没有记录, 流水没有记录查询本地订单
GeneralTradeInfo localTrade = localTradeMap.get(outDetail.getTradeNo());
if (Objects.isNull(localTrade)){
log.info("本地订单不存在: {}", outDetail.getTradeNo());
ReconcileDiff diffRecord = new ReconcileDiff()
.setDiffType(ReconcileDiffTypeEnum.LOCAL_NOT_EXISTS.getCode())
.setTradeNo(outDetail.getTradeNo())
.setDetailId(outDetail.getId())
.setReconcileId(reconcileOrder.getId())
.setTitle(outDetail.getTitle())
.setOrderType(outDetail.getType())
.setOutOrderNo(outDetail.getOutTradeNo())
.setOutAmount(outDetail.getAmount())
.setTradeTime(outDetail.getTradeTime());
diffRecords.add(diffRecord);
continue;
}
// 如果远程和本地都存在, 比对差异
List<cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff> reconcileDiffs = this.reconcileDiff(outDetail, localTrade);
if (CollUtil.isNotEmpty(reconcileDiffs)) {
ReconcileDiff diffRecord = new ReconcileDiff()
.setReconcileId(reconcileOrder.getId())
.setDetailId(outDetail.getId())
.setDiffType(ReconcileDiffTypeEnum.NOT_MATCH.getCode())
.setTradeNo(outDetail.getTradeNo())
.setTitle(outDetail.getTitle())
.setOrderType(outDetail.getType())
.setOutOrderNo(outDetail.getOutTradeNo())
.setOutAmount(outDetail.getAmount())
.setAmount(localTrade.getAmount())
.setDiffs(reconcileDiffs)
.setTradeTime(outDetail.getTradeTime());
diffRecords.add(diffRecord);
}
}
// 本地与对账单比对, 找出本地有, 远程没有的记录
for (GeneralTradeInfo localTrade : localTrades) {
ReconcileTradeDetail outDetail = outDetailMap.get(localTrade.getTradeNo());
if (Objects.isNull(outDetail)){
ReconcileDiff diffRecord = new ReconcileDiff()
.setDiffType(ReconcileDiffTypeEnum.LOCAL_NOT_EXISTS.getCode())
.setTradeNo(localTrade.getTradeNo())
.setReconcileId(reconcileOrder.getId())
.setDetailId(null)
.setTitle(localTrade.getTitle())
.setOrderType(localTrade.getType())
.setOutOrderNo(localTrade.getOutTradeNo())
.setAmount(localTrade.getAmount())
.setTradeTime(localTrade.getFinishTime());
diffRecords.add(diffRecord);
log.info("远程订单不存在: {}", localTrade.getTradeNo());
continue;
}
}
return diffRecords;
}
/**
* 判断订单之间存在哪些差异
* @param outDetail 下载的对账订单
* @param localTrade 本地交易订单
*/
private List<cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff> reconcileDiff(ReconcileTradeDetail outDetail, GeneralTradeInfo localTrade){
List<cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff> diffs = new ArrayList<>();
// 判断类型是否相同
if (Objects.equals(outDetail.getType(), ReconcileTradeEnum.PAY.getCode())
&& !Objects.equals(localTrade.getType(), PaymentTypeEnum.PAY.getCode())){
log.warn("订单类型不一致: {},{}", outDetail.getType(), localTrade.getType());
diffs.add(new cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff().setFieldName("订单类型").setLocalValue(outDetail.getType()).setOutValue(localTrade.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 cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff().setFieldName("订单类型").setLocalValue(outDetail.getType()).setOutValue(localTrade.getType()));
}
// 判断名称是否一致
if (!Objects.equals(outDetail.getTitle(), localTrade.getTitle())){
log.warn("订单名称不一致: {},{}", outDetail.getTitle(), localTrade.getTitle());
diffs.add(new cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff().setFieldName("订单名称").setLocalValue(outDetail.getTitle()).setOutValue(localTrade.getTitle()));
}
// 判断金额是否一致
if (!Objects.equals(outDetail.getAmount(), localTrade.getAmount())){
log.warn("订单金额不一致: {},{}", outDetail.getAmount(), localTrade.getAmount());
diffs.add(new cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.ReconcileDiff().setFieldName("订单金额")
.setLocalValue(String.valueOf(outDetail.getAmount()))
.setOutValue(String.valueOf(localTrade.getAmount())));
}
return diffs;
}
}

View File

@@ -1,51 +1,46 @@
package cn.bootx.platform.daxpay.service.core.payment.reconcile.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileDiffTypeEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileFileTypeEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileResultEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
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.order.reconcile.dao.ReconcileDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileFileManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileTradeDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.ReconcileOrderManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiffRecord;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileFile;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileTradeDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDiff;
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.order.refund.dao.RefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
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;
import cn.bootx.platform.daxpay.service.param.reconcile.ReconcileUploadParam;
import cn.bootx.platform.daxpay.util.OrderNoGenerateUtil;
import cn.hutool.core.util.CharsetUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 对账服务
* 对账服务,
* @author xxm
* @since 2024/1/17
*/
@@ -59,11 +54,13 @@ public class ReconcileService {
private final ReconcileOrderManager reconcileOrderManager;
private final ReconcileDetailManager reconcileDetailManager;
private final ReconcileTradeDetailManager reconcileTradeDetailManager;
private final PayOrderManager payOrderManager;
private final ReconcileAssistService reconcileAssistService;
private final RefundOrderManager refundOrderManager;
private final ReconcileFileManager reconcileFileManager;
private final FileStorageService fileStorageService;
/**
* 创建对账订单
@@ -88,11 +85,11 @@ public class ReconcileService {
}
/**
* 下载对账单并进行保存
* 下载交易对账单并进行保存
*/
public void downAndSave(ReconcileOrder reconcileOrder) {
// 如果对账单已经存在
if (reconcileOrder.isDetailDown() && reconcileOrder.isTotalDown()){
if (reconcileOrder.isDownOrUpload()){
throw new PayFailureException("对账单文件已经下载或上传");
}
// 将对账订单写入到上下文中
@@ -103,8 +100,7 @@ public class ReconcileService {
reconcileStrategy.doBeforeHandler();
try {
reconcileStrategy.downAndSave();
reconcileOrder.setDetailDown(true)
.setTotalDown(true)
reconcileOrder.setDownOrUpload(true)
.setErrorMsg(null);
reconcileOrderService.update(reconcileOrder);
} catch (Exception e) {
@@ -115,29 +111,29 @@ public class ReconcileService {
throw new RuntimeException(e);
}
// 保存转换后的通用结构对账单
List<ReconcileDetail> reconcileDetails = PaymentContextLocal.get()
List<ReconcileTradeDetail> reconcileTradeDetails = PaymentContextLocal.get()
.getReconcileInfo()
.getReconcileDetails();
reconcileDetailManager.saveAll(reconcileDetails);
.getReconcileTradeDetails();
reconcileTradeDetailManager.saveAll(reconcileTradeDetails);
}
/**
* 手动传输对账单
* @param id 对账单ID
* @param file 文件
* 手动传输交易对账单
*/
public void upload(Long id, MultipartFile file) {
ReconcileOrder reconcileOrder = reconcileOrderService.findById(id)
public void upload(ReconcileUploadParam param, MultipartFile file) {
ReconcileOrder reconcileOrder = reconcileOrderService.findById(param.getId())
.orElseThrow(() -> new DataNotExistException("未找到对账订单"));
// 将对账订单写入到上下文中
PaymentContextLocal.get().getReconcileInfo().setReconcileOrder(reconcileOrder);
AbsReconcileStrategy reconcileStrategy = ReconcileStrategyFactory.create(reconcileOrder.getChannel());
reconcileStrategy.setRecordOrder(reconcileOrder);
reconcileStrategy.doBeforeHandler();
// 上传类型
ReconcileFileTypeEnum fileType = ReconcileFileTypeEnum.findByCode(param.getFileType());
try {
reconcileStrategy.upload(file);
reconcileOrder.setDownOrUpload(true)
.setErrorMsg(null);
reconcileStrategy.upload(file, fileType);
reconcileOrder.setErrorMsg(null);
reconcileOrderService.update(reconcileOrder);
} catch (Exception e) {
log.error("上传对账单异常", e);
@@ -146,25 +142,25 @@ public class ReconcileService {
throw new RuntimeException(e);
}
// 保存转换后的通用结构对账单
List<ReconcileDetail> reconcileDetails = PaymentContextLocal.get()
List<ReconcileTradeDetail> reconcileTradeDetails = PaymentContextLocal.get()
.getReconcileInfo()
.getReconcileDetails();
reconcileDetailManager.saveAll(reconcileDetails);
.getReconcileTradeDetails();
reconcileTradeDetailManager.saveAll(reconcileTradeDetails);
}
/**
* 对账单比对
* 对账单明细比对
*/
@Transactional(rollbackFor = Exception.class)
public void compareDetail(Long reconcileOrderId){
public void compare(Long reconcileOrderId){
ReconcileOrder reconcileOrder = reconcileOrderService.findById(reconcileOrderId)
.orElseThrow(() -> new DataNotExistException("未找到对账订单"));
this.compareDetail(reconcileOrder);
this.compare(reconcileOrder);
}
/**
* 对账比对
* 交易对账比对
*/
public void compareDetail(ReconcileOrder reconcileOrder){
public void compare(ReconcileOrder reconcileOrder){
// 判断是否已经下载了对账单明细
if (!reconcileOrder.isDownOrUpload()){
throw new PayFailureException("请先下载对账单");
@@ -174,7 +170,7 @@ public class ReconcileService {
throw new PayFailureException("对账单比对已经完成");
}
// 查询对账单
List<ReconcileDetail> reconcileDetails = reconcileDetailManager.findAllByReconcileId(reconcileOrder.getId());
List<ReconcileTradeDetail> reconcileTradeDetails = reconcileTradeDetailManager.findAllByReconcileId(reconcileOrder.getId());
// 构建对账策略
AbsReconcileStrategy reconcileStrategy = ReconcileStrategyFactory.create(reconcileOrder.getChannel());
// 初始化参数
@@ -182,8 +178,8 @@ public class ReconcileService {
try {
// 执行比对任务, 获取对账差异记录并保存
List<GeneralTradeInfo> generalTradeInfo = this.getGeneralTradeInfoList(reconcileOrder);
List<ReconcileDiffRecord> diffRecords = this.generateDiffRecord(reconcileOrder, generalTradeInfo,reconcileDetails);
List<GeneralTradeInfo> generalTradeInfo = reconcileAssistService.getGeneralTradeInfoList(reconcileOrder);
List<ReconcileDiff> diffRecords = reconcileAssistService.generateDiffRecord(reconcileOrder, generalTradeInfo, reconcileTradeDetails);
// 判断是否有差异
if (CollUtil.isNotEmpty(diffRecords)){
reconcileOrder.setResult(ReconcileResultEnum.INCONSISTENT.getCode());
@@ -200,174 +196,46 @@ public class ReconcileService {
}
/**
* 下载原始对账单文件
* 下载原始对交易对账单文件
*/
public void downOriginal(Long id){
@SneakyThrows
public ResponseEntity<byte[]> downOriginal(Long id){
if (!reconcileOrderManager.existedById(id)) {
throw new DataNotExistException("未找到对账订单");
}
// 获取对账单文件ID
List<ReconcileFile> reconcileFiles = reconcileFileManager.findAllByReconcileId(id);
if (CollUtil.isEmpty(reconcileFiles)){
throw new DataNotExistException("未找到对账文件");
}
// 查询文件
FileInfo fileInfo = fileStorageService.getFileInfoByUrl(String.valueOf(reconcileFiles.get(0).getFileId()));
byte[] bytes = fileStorageService.download(fileInfo).bytes();
// 设置header信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
String fileName = fileInfo.getOriginalFilename();
headers.setContentDispositionFormData("attachment", URLEncoder.encode(fileName, CharsetUtil.UTF_8));
return new ResponseEntity<>(bytes,headers, HttpStatus.OK);
}
/**
* 下载基于原始对账单数据转换的对账单文件, 例如csv, json方式
* 下载基于原始交易对账单数据转换的对账单文件, 例如csv, json方式
*/
public void downTransfer(Long id){
ReconcileOrder reconcileOrderOriginal = reconcileOrderService.findById(id)
public void downOriginal2Csv(Long id){
ReconcileOrder reconcileOrder = reconcileOrderService.findById(id)
.orElseThrow(() -> new DataNotExistException("未找到对账订单"));
// 获取
// 查询对账-第三方交易明细
List<ReconcileTradeDetail> reconcileTradeDetails = reconcileTradeDetailManager.findAllByReconcileId(reconcileOrder.getId());
// 转换为csv文件
}
/**
* 获取通用对账对象, 将支付/退款订单转换为对账对象
* 下载对账单(本系统中的订单)
*/
private List<GeneralTradeInfo> getGeneralTradeInfoList(ReconcileOrder reconcileOrder){
List<GeneralTradeInfo> generalTradeInfoList = new ArrayList<>();
// 查询流水
LocalDateTime localDateTime = LocalDateTimeUtil.date2DateTime(reconcileOrder.getDate());
LocalDateTime start = LocalDateTimeUtil.beginOfDay(localDateTime);
LocalDateTime end = LocalDateTimeUtil.endOfDay(localDateTime);
// 下载支付订单
List<PayOrder> payOrders = payOrderManager.findReconcile(reconcileOrder.getChannel(), start, end, PayStatusEnum.SUCCESS, PayStatusEnum.PARTIAL_REFUND, PayStatusEnum.REFUNDING, PayStatusEnum.REFUNDED);
List<RefundOrder> refundOrders = refundOrderManager.findReconcile(reconcileOrder.getChannel(), start, end, RefundStatusEnum.SUCCESS);
for (PayOrder payOrder : payOrders) {
generalTradeInfoList.add(new GeneralTradeInfo()
.setTitle(payOrder.getTitle())
.setTradeNo(payOrder.getOrderNo())
.setOutTradeNo(payOrder.getOutOrderNo())
.setFinishTime(payOrder.getPayTime())
.setType(PaymentTypeEnum.PAY.getCode())
.setAmount(payOrder.getAmount())
);
}
for (RefundOrder refundOrder : refundOrders) {
generalTradeInfoList.add(new GeneralTradeInfo()
.setTitle(refundOrder.getTitle())
.setTradeNo(refundOrder.getOrderNo())
.setOutTradeNo(refundOrder.getOutOrderNo())
.setFinishTime(refundOrder.getFinishTime())
.setType(PaymentTypeEnum.REFUND.getCode())
.setAmount(refundOrder.getAmount()));
}
return generalTradeInfoList;
}
/**
* 比对生成对账差异单
* 1. 远程有, 本地无
* 2. 远程无, 本地有
* 3. 远程有, 本地有, 但状态不一致
*
* @param reconcileOrder 对账单
* @param localTrades 本地交易订单
* @param outDetails 远程对账明细
*/
private List<ReconcileDiffRecord> generateDiffRecord(ReconcileOrder reconcileOrder,
List<GeneralTradeInfo> localTrades,
List<ReconcileDetail> outDetails){
if (CollUtil.isEmpty(outDetails) || CollUtil.isEmpty(localTrades)){
return new ArrayList<>();
}
// 差异内容
List<ReconcileDiffRecord> diffRecords = new ArrayList<>();
// 三方对账订单
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())
.setTradeNo(outDetail.getTradeNo())
.setDetailId(outDetail.getId())
.setReconcileId(reconcileOrder.getId())
.setTitle(outDetail.getTitle())
.setOrderType(outDetail.getType())
.setOutOrderNo(outDetail.getOutTradeNo())
.setOutAmount(outDetail.getAmount())
.setTradeTime(outDetail.getTradeTime());
diffRecords.add(diffRecord);
continue;
}
// 如果远程和本地都存在, 比对差异
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())
.setTradeNo(outDetail.getTradeNo())
.setTitle(outDetail.getTitle())
.setOrderType(outDetail.getType())
.setOutOrderNo(outDetail.getOutTradeNo())
.setOutAmount(outDetail.getAmount())
.setAmount(localTrade.getAmount())
.setDiffs(reconcileDiffs)
.setTradeTime(outDetail.getTradeTime());
diffRecords.add(diffRecord);
}
}
// 本地与对账单比对, 找出本地有, 远程没有的记录
for (GeneralTradeInfo localTrade : localTrades) {
ReconcileDetail outDetail = outDetailMap.get(localTrade.getTradeNo());
if (Objects.isNull(outDetail)){
ReconcileDiffRecord diffRecord = new ReconcileDiffRecord()
.setDiffType(ReconcileDiffTypeEnum.LOCAL_NOT_EXISTS.getCode())
.setTradeNo(localTrade.getTradeNo())
.setReconcileId(reconcileOrder.getId())
.setDetailId(null)
.setTitle(localTrade.getTitle())
.setOrderType(localTrade.getType())
.setOutOrderNo(localTrade.getOutTradeNo())
.setAmount(localTrade.getAmount())
.setTradeTime(localTrade.getFinishTime());
diffRecords.add(diffRecord);
log.info("远程订单不存在: {}", localTrade.getTradeNo());
continue;
}
}
return diffRecords;
}
/**
* 判断订单之间存在哪些差异
* @param outDetail 下载的对账订单
* @param localTrade 本地交易订单
*/
private List<ReconcileDiff> reconcileDiff(ReconcileDetail outDetail, GeneralTradeInfo localTrade){
List<ReconcileDiff> diffs = new ArrayList<>();
// 判断类型是否相同
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(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(outDetail.getTitle(), localTrade.getTitle())){
log.warn("订单名称不一致: {},{}", outDetail.getTitle(), localTrade.getTitle());
diffs.add(new ReconcileDiff().setFieldName("订单名称").setLocalValue(outDetail.getTitle()).setOutValue(localTrade.getTitle()));
}
// 判断金额是否一致
if (!Objects.equals(outDetail.getAmount(), localTrade.getAmount())){
log.warn("订单金额不一致: {},{}", outDetail.getAmount(), localTrade.getAmount());
diffs.add(new ReconcileDiff().setFieldName("订单金额")
.setLocalValue(String.valueOf(outDetail.getAmount()))
.setOutValue(String.valueOf(localTrade.getAmount())));
}
return diffs;
public void downLocal(Long id){
}
}

View File

@@ -2,6 +2,7 @@ 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.service.code.ReconcileFileTypeEnum;
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;
@@ -55,7 +56,7 @@ public class AlipayReconcileStrategy extends AbsReconcileStrategy {
*/
@SneakyThrows
@Override
public void upload(MultipartFile file) {
public void upload(MultipartFile file, ReconcileFileTypeEnum fileType) {
reconcileService.upload(file.getBytes());
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.payment.reconcile.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileFileTypeEnum;
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;
@@ -61,7 +62,7 @@ public class UnionPayReconcileStrategy extends AbsReconcileStrategy {
*/
@SneakyThrows
@Override
public void upload(MultipartFile file) {
public void upload(MultipartFile file, ReconcileFileTypeEnum fileType) {
reconcileService.upload(file.getBytes());
}
@@ -71,6 +72,6 @@ public class UnionPayReconcileStrategy extends AbsReconcileStrategy {
@Override
public void downAndSave() {
Date date = DateUtil.date(this.getRecordOrder().getDate());
reconcileService.downAndSave(this.getRecordOrder(),date, this.unionPayKit);
reconcileService.downAndSave(this.getRecordOrder(), date, this.unionPayKit);
}
}

View File

@@ -2,10 +2,10 @@ 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.service.code.ReconcileFileTypeEnum;
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.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy;
import cn.hutool.core.date.DatePattern;
import lombok.RequiredArgsConstructor;
@@ -32,8 +32,6 @@ public class WechatPayReconcileStrategy extends AbsReconcileStrategy {
private final WeChatPayConfigService weChatPayConfigService;
private final PayOrderManager payOrderManager;
private WeChatPayConfig config;
/**
@@ -59,7 +57,7 @@ public class WechatPayReconcileStrategy extends AbsReconcileStrategy {
*/
@SneakyThrows
@Override
public void upload(MultipartFile file) {
public void upload(MultipartFile file, ReconcileFileTypeEnum fileType) {
reconcileService.uploadBill(file.getBytes(),this.config);
}
@@ -68,8 +66,8 @@ public class WechatPayReconcileStrategy extends AbsReconcileStrategy {
*/
@Override
public void downAndSave() {
String format = LocalDateTimeUtil.format(this.getRecordOrder().getDate(), DatePattern.PURE_DATE_PATTERN);
reconcileService.downAndSave(format, this.config);
String date = LocalDateTimeUtil.format(this.getRecordOrder().getDate(), DatePattern.PURE_DATE_PATTERN);
reconcileService.downAndSave(this.getRecordOrder(), date, this.config);
}
}

View File

@@ -22,16 +22,24 @@ import java.util.List;
@Data
@Accessors(chain = true)
@Schema(title = "对账差异单")
public class ReconcileDiffRecordDto extends BaseDto {
public class ReconcileDiffDto extends BaseDto {
/** 对账单ID */
@Schema(description = "对账单ID")
private Long ReconcileId;
private Long reconcileId;
/** 对账号 */
@Schema(description = "对账号")
private String reconcileNo;
/** 对账单明细ID */
@Schema(description = "对账单明细ID")
private Long detailId;
/** 对账日期 */
@Schema(description = "对账日期")
private LocalDateTime reconcileDate;
/** 本地交易号 */
@Schema(description = "本地交易号")
private String tradeNo;
@@ -61,7 +69,7 @@ public class ReconcileDiffRecordDto extends BaseDto {
* @see ReconcileTradeEnum
*/
@Schema(description = "订单类型")
private String orderType;
private String tradeType;
/**
* 差异类型

View File

@@ -18,7 +18,7 @@ import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@Schema(title = "对账订单详情")
public class ReconcileDetailDto extends BaseDto {
public class ReconcileTradeDetailDto extends BaseDto {
/** 关联对账订单ID */
@Schema(description = "关联对账订单ID")

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.service.code.ReconcileFileTypeEnum;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import lombok.Getter;
import lombok.Setter;
@@ -26,7 +27,7 @@ public abstract class AbsReconcileStrategy implements PayStrategy {
/**
* 上传对账单解析并保存
*/
public abstract void upload(MultipartFile file);
public abstract void upload(MultipartFile file, ReconcileFileTypeEnum fileType);
/**
* 下载对账单到本地进行保存

View File

@@ -0,0 +1,30 @@
package cn.bootx.platform.daxpay.service.param.reconcile;
import cn.bootx.platform.daxpay.service.code.ReconcileFileTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
/**
* 对账文件上传参数
* @author xxm
* @since 2024/5/4
*/
@Data
@Accessors(chain = true)
@Schema(title = "对账文件上传参数")
public class ReconcileUploadParam {
@Schema(description = "对账订单ID")
@NotNull(message = "对账订单ID不能为空")
private Long id;
/**
* @see ReconcileFileTypeEnum
*/
@Schema(description = "文件类型")
@NotNull(message = "文件类型不能为空")
private String fileType;
}

View File

@@ -30,7 +30,7 @@ public class ReconcileTaskService {
// 2. 执行对账任务, 下载对账单并解析, 分别存储为原始数据和通用对账数据
reconcileService.downAndSave(reconcileOrder);
// 3. 执行账单比对, 生成差异单
// 3. 执行账单明细比对, 生成差异单
reconcileService.compare(reconcileOrder);
}