diff --git a/_doc/Task.md b/_doc/Task.md index 121e055d0..6efa99704 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -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 版本内容 - [ ] 对账回退及退款 diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayChannelEnum.java b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayChannelEnum.java index 16e7bc57e..600a0e8b5 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayChannelEnum.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayChannelEnum.java @@ -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; - /** * 根据字符编码获取 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java index 9e9886819..2730d38a5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java @@ -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); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java index c64fa2fca..2b1cc9272 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java @@ -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 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); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WechatPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WechatPayReconcileService.java index 49cfdc32a..119ed106a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WechatPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WechatPayReconcileService.java @@ -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); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/reconcile/entity/ReconcileOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/reconcile/entity/ReconcileOrder.java index 42bfad286..20b2852a2 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/reconcile/entity/ReconcileOrder.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/reconcile/entity/ReconcileOrder.java @@ -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; /** * 对账结果 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/service/ReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/service/ReconcileService.java index 23b414c8d..71ec68b76 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/service/ReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/service/ReconcileService.java @@ -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("请先下载对账单"); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java index 3a06d9398..9ae3030fb 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java @@ -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); } - - }