ref 对账改造

This commit is contained in:
DaxPay
2024-05-04 18:53:03 +08:00
parent ba1c89b6a6
commit 21ee9424bd
8 changed files with 119 additions and 35 deletions

View File

@@ -13,11 +13,9 @@
- [x] 支付和退款回调
- [x] 去除现金支付和储值卡支付方式
- [x] 三方支付外部订单号规则优化: 支付P、退款R、分账A根据环境加前缀DEV_、DEMO_、PRE_
- [ ] 金额显示统一使用元
- [x] 金额显示统一使用元
- [x] 使用切面统一处理API调用异常, 做统一包装返回
- [x] 支付接口优化
- [] 前端增加对应的枚举
- [ ] 优化自动分账逻辑
- [x] 前端查询条件适配
- [x] paymentId替换
- [ ] 对账单改造
@@ -25,8 +23,16 @@
- [x] 增加对账结果计算和显示
- [ ] 增加下载原始对账单功能
- [ ] 保存原始的文件
- [ ] 微信
- [x] 支付宝
- [x] 云闪付
- [ ] 解析原有文件, 可以转换为指定格式进行下载
- [ ] 增加下载系统对账单功能
- [ ] 自动分账改造
- [ ] SDK新增对账接收放添加接口
- [ ] 创建定时任务, 自动对待分账订单进行分账
- [ ] 增加定时同步分账状态任务
- [ ] 增加自动完结功能
2.0.7: 分账完善和基础架构优化
- [ ] 资金流水优化
- [ ] 数据加密方式改为类型处理器模式
@@ -37,7 +43,7 @@
- [ ] 分账回调处理
- [ ] 分账组管理提供接口调用
- [ ] 分账通知
- [ ] 增加收银台
- [ ] 增加收银台功能
2.0.x 版本内容
- [ ] 对账回退及退款

View File

@@ -17,10 +17,10 @@ import java.util.Objects;
@RequiredArgsConstructor
public enum PayChannelEnum {
ALI("ali_pay", "支付宝", "ali"),
WECHAT("wechat_pay", "微信支付", "wx"),
UNION_PAY("union_pay", "云闪付", "uni"),
WALLET("wallet_pay", "钱包支付",null),
ALI("ali_pay", "支付宝"),
WECHAT("wechat_pay", "微信支付"),
UNION_PAY("union_pay", "云闪付"),
WALLET("wallet_pay", "钱包支付"),
;
/** 支付通道编码 */
@@ -29,9 +29,6 @@ public enum PayChannelEnum {
/** 支付通道名称 */
private final String name;
/** 对账前缀 */
private final String reconcilePrefix;
/**
* 根据字符编码获取
*/

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayCode;
@@ -104,10 +105,10 @@ public class AliPayReconcileService {
billDetails = this.parseDetail(strings);
}
}
// 保存原始对账文件
this.saveOriginalFile(recordOrder, bytes);
// 保存原始对账记录
this.save(billDetails, billTotals);
// 保存文件
this.saveOriginalFile(recordOrder, bytes);
// 将原始交易明细对账记录转换通用结构并保存到上下文中
this.convertAndSave(billDetails);
@@ -239,7 +240,10 @@ public class AliPayReconcileService {
*/
private void saveOriginalFile(ReconcileOrder reconcileOrder, byte[] bytes) {
// 将原始文件进行保存
String fileName = "";
PayChannelEnum channelEnum = PayChannelEnum.findByCode(reconcileOrder.getChannel());
String date = LocalDateTimeUtil.format(reconcileOrder.getDate(), DatePattern.PURE_DATE_PATTERN);
// 将原始文件进行保存 通道-日期
String fileName = StrUtil.format("{}-{}.zip", channelEnum.getName(),date);
UploadPretreatment uploadPretreatment = fileStorageService.of(bytes);
if (StrUtil.isNotBlank(fileName)) {
uploadPretreatment.setOriginalFilename(fileName);

View File

@@ -1,14 +1,18 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.ReconcileFileTypeEnum;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.code.UnionReconcileFieldEnum;
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.ReconcileFile;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.hutool.core.bean.BeanUtil;
@@ -23,6 +27,9 @@ import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.UploadPretreatment;
import org.springframework.stereotype.Service;
import java.io.*;
@@ -44,11 +51,13 @@ import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
public class UnionPayReconcileService {
private final UnionReconcileBillDetailManager unionReconcileBillDetailManager;
private final FileStorageService fileStorageService;
private final ReconcileFileManager reconcileFileManager;
/**
* 下载对账单
*/
public void downAndSave(Date date, UnionPayKit unionPayKit){
public void downAndSave(ReconcileOrder reconcileOrder, Date date, UnionPayKit unionPayKit){
// 下载对账单
Map<String, Object> map = unionPayKit.downloadBill(date, RECONCILE_BILL_TYPE);
String fileContent = map.get(FILE_CONTENT).toString();
@@ -81,6 +90,8 @@ public class UnionPayReconcileService {
// 汇总目前不进行处理
}
}
// 保存原始对账记录文件
this.saveOriginalFile(reconcileOrder,zipBytes);
// 保存原始对账记录
this.save(billDetails);
// 转换为通用对账记录对象
@@ -194,4 +205,24 @@ public class UnionPayReconcileService {
billDetails.forEach(o->o.setReconcileId(recordOrderId));
unionReconcileBillDetailManager.saveAll(billDetails);
}
/**
* 保存下载的原始对账文件
*/
private void saveOriginalFile(ReconcileOrder reconcileOrder, byte[] bytes) {
PayChannelEnum channelEnum = PayChannelEnum.findByCode(reconcileOrder.getChannel());
String date = LocalDateTimeUtil.format(reconcileOrder.getDate(), DatePattern.PURE_DATE_PATTERN);
// 将原始文件进行保存 通道-日期
String fileName = StrUtil.format("{}-{}.zip", channelEnum.getName(),date);
UploadPretreatment uploadPretreatment = fileStorageService.of(bytes);
if (StrUtil.isNotBlank(fileName)) {
uploadPretreatment.setOriginalFilename(fileName);
}
FileInfo upload = uploadPretreatment.upload();
String fileId = upload.getId();
ReconcileFile reconcileFile = new ReconcileFile().setFileId(Long.valueOf(fileId))
.setReconcileId(reconcileOrder.getId())
.setType(ReconcileFileTypeEnum.ZIP.getCode());
reconcileFileManager.save(reconcileFile);
}
}

View File

@@ -1,8 +1,10 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.ReconcileTradeEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.ReconcileFileTypeEnum;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.dao.WxReconcileBillDetailManager;
@@ -11,13 +13,16 @@ import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConf
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileBillDetail;
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.ReconcileFile;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileOrder;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
@@ -27,6 +32,9 @@ import com.ijpay.wxpay.model.DownloadFundFlowModel;
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.dromara.x.file.storage.core.UploadPretreatment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -53,16 +61,20 @@ import static cn.bootx.platform.daxpay.service.code.WeChatPayCode.ACCOUNT_TYPE_B
public class WechatPayReconcileService{
private final WxReconcileBillTotalManger reconcileBillTotalManger;
private final WxReconcileBillDetailManager reconcileBillDetailManager;
private final FileStorageService fileStorageService;
private final ReconcileFileManager reconcileFileManager;
/**
* 下载对账单并保存
* @param date 对账日期 yyyyMMdd 格式
*/
@Transactional(rollbackFor = Exception.class)
public void downAndSave(String date, WeChatPayConfig config) {
public void downAndSave(String date, WeChatPayConfig config) {
// TODO 修改为现将对账单全部下载下来, 然后再进行保存和解析
this.downBill(date, config);
// 资金对账单先不启用
// this.downFundFlow(date, recordOrderId, config);
this.downFundFlow(date, config);
}
/**
@@ -111,7 +123,8 @@ public class WechatPayReconcileService{
}
/**
* 上传对账单
* 上传对账单明细
* todo 增加校验, 增加类型识别
*/
@SneakyThrows
public void uploadBill(byte[] bytes, WeChatPayConfig config){
@@ -140,6 +153,7 @@ public class WechatPayReconcileService{
.collect(Collectors.toList());
billDetails.forEach(o->o.setRecordOrderId(reconcileOrder.getId()));
// 保存原始对账单明细
reconcileBillDetailManager.saveAll(billDetails);
// 将原始交易明细对账记录转换通用结构并保存到上下文中
@@ -207,9 +221,8 @@ public class WechatPayReconcileService{
* 更换一下最新的JDK即可, 例如 corretto Jdk
*
* @param date 对账日期 yyyyMMdd 格式
* @param recordOrderId 对账订单ID
*/
public void downFundFlow(String date, Long recordOrderId, WeChatPayConfig config){
public void downFundFlow(String date, WeChatPayConfig config){
if (StrUtil.isBlank(config.getP12())){
return;
}
@@ -297,4 +310,26 @@ public class WechatPayReconcileService{
throw new PayFailureException("微信资金对账失败: " + errorMsg);
}
/**
* 保存下载的原始对账文件
*/
private void saveOriginalFile(ReconcileOrder reconcileOrder,String text, ReconcileFileTypeEnum fileType) {
// 微信接收结果转换为 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());
byte[] bytes = StrUtil.bytes(text, CharsetUtil.CHARSET_UTF_8);
UploadPretreatment uploadPretreatment = fileStorageService.of(bytes);
if (StrUtil.isNotBlank(fileName)) {
uploadPretreatment.setOriginalFilename(fileName);
}
FileInfo upload = uploadPretreatment.upload();
String fileId = upload.getId();
ReconcileFile reconcileFile = new ReconcileFile().setFileId(Long.valueOf(fileId))
.setReconcileId(reconcileOrder.getId())
.setType(fileType.getCode());
reconcileFileManager.save(reconcileFile);
}
}

View File

@@ -2,6 +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.PayChannelEnum;
import cn.bootx.platform.daxpay.service.code.ReconcileResultEnum;
import cn.bootx.platform.daxpay.service.core.order.reconcile.conver.ReconcileConvert;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.ReconcileOrderDto;
@@ -36,17 +37,28 @@ public class ReconcileOrder extends MpCreateEntity implements EntityBaseFunction
@DbColumn(comment = "日期")
private LocalDate date;
/** 通道 */
/**
* 通道
* @see PayChannelEnum
*/
@DbColumn(comment = "通道")
private String channel;
/** 是否下载成功 */
@DbColumn(comment = "是否下载或上传")
private boolean downOrUpload;
/** 明细对账文件是否下载成功 */
@DbColumn(comment = "明细对账单下载")
private boolean detailDown;
/** 是否比对完成 */
@DbColumn(comment = "是否比对完成")
private boolean compare;
/** 明细是否比对完成 */
@DbColumn(comment = "明细对账单比对")
private boolean detailCompare;
/** 汇总对账文件是否下载成功 */
@DbColumn(comment = "汇总对账单下载")
private boolean totalDown;
/** 汇总是否比对完成 */
@DbColumn(comment = "汇总对账单比对")
private boolean totalCompare;
/**
* 对账结果

View File

@@ -92,7 +92,7 @@ public class ReconcileService {
*/
public void downAndSave(ReconcileOrder reconcileOrder) {
// 如果对账单已经存在
if (reconcileOrder.isDownOrUpload()){
if (reconcileOrder.isDetailDown() && reconcileOrder.isTotalDown()){
throw new PayFailureException("对账单文件已经下载或上传");
}
// 将对账订单写入到上下文中
@@ -103,7 +103,8 @@ public class ReconcileService {
reconcileStrategy.doBeforeHandler();
try {
reconcileStrategy.downAndSave();
reconcileOrder.setDownOrUpload(true)
reconcileOrder.setDetailDown(true)
.setTotalDown(true)
.setErrorMsg(null);
reconcileOrderService.update(reconcileOrder);
} catch (Exception e) {
@@ -154,16 +155,16 @@ public class ReconcileService {
* 对账单比对
*/
@Transactional(rollbackFor = Exception.class)
public void compare(Long reconcileOrderId){
public void compareDetail(Long reconcileOrderId){
ReconcileOrder reconcileOrder = reconcileOrderService.findById(reconcileOrderId)
.orElseThrow(() -> new DataNotExistException("未找到对账订单"));
this.compare(reconcileOrder);
this.compareDetail(reconcileOrder);
}
/**
* 对账单比对
*/
public void compare(ReconcileOrder reconcileOrder){
public void compareDetail(ReconcileOrder reconcileOrder){
// 判断是否已经下载了对账单明细
if (!reconcileOrder.isDownOrUpload()){
throw new PayFailureException("请先下载对账单");

View File

@@ -71,8 +71,6 @@ public class UnionPayReconcileStrategy extends AbsReconcileStrategy {
@Override
public void downAndSave() {
Date date = DateUtil.date(this.getRecordOrder().getDate());
reconcileService.downAndSave(date, this.unionPayKit);
reconcileService.downAndSave(this.getRecordOrder(),date, this.unionPayKit);
}
}