feat 支付对账单订单创建和一些转换处理

This commit is contained in:
bootx
2024-01-22 09:19:02 +08:00
parent 97ed6b7e25
commit 22a81487f6
29 changed files with 501 additions and 60 deletions

View File

@@ -36,4 +36,7 @@ public class PaymentContext {
/** 支付请求相关信息 */
private final RequestLocal request = new RequestLocal();
/** 支付对账相关信息 */
private final ReconcileLocal reconcile = new ReconcileLocal();
}

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.daxpay.service.common.context;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.PayReconcileDetail;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 支付对账上下文信息
* @author xxm
* @since 2024/1/21
*/
@Data
@Accessors(chain = true)
public class ReconcileLocal {
/** */
private List<PayReconcileDetail> reconcileDetails;
}

View File

@@ -23,6 +23,18 @@ public final class PaymentContextLocal {
THREAD_LOCAL.set(paymentContext);
}
/**
* 不存在则进行赋值
*/
public static boolean setIfAbsent(PaymentContext paymentContext){
if (THREAD_LOCAL.get() == null){
set(paymentContext);
return true;
}
return false;
}
/**
* 获取
*/

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillDetail;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class AliReconcileBillDetailManager extends BaseManager<AliReconcileBillDetailMapper, AliReconcileBillDetail> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.dao;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Mapper
public interface AliReconcileBillDetailMapper extends BaseMapper<AliReconcileBillDetail> {
}

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillTotal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class AliReconcileBillTotalManager extends BaseManager<AliReconcileBillTotalMapper,AliReconcileBillTotal> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.dao;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillTotal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Mapper
public interface AliReconcileBillTotalMapper extends BaseMapper<AliReconcileBillTotal> {
}

View File

@@ -1,61 +1,96 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.entity;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 支付宝业务明细对账单
* @author xxm
* @since 2024/1/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AliReconcileBillDetail {
@DbTable(comment = "支付宝业务明细对账单")
@TableName("pay_ali_reconcile_bill_detail")
public class AliReconcileBillDetail extends MpIdEntity {
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
@Alias("支付宝交易号")
@DbColumn(comment = "支付宝交易号")
private String tradeNo;
@Alias("商户订单号")
@DbColumn(comment = "商户订单号")
private String outTradeNo;
@Alias("业务类型")
@DbColumn(comment = "业务类型")
private String tradeType;
@Alias("商品名称")
@DbColumn(comment = "商品名称")
private String subject;
@Alias("完成时间")
@DbColumn(comment = "完成时间")
private String endTime;
@Alias("门店编号")
@DbColumn(comment = "门店编号")
private String storeId;
@Alias("门店名称")
@DbColumn(comment = "门店名称")
private String storeName;
@Alias("操作员")
@DbColumn(comment = "操作员")
private String operator;
@Alias("终端号")
@DbColumn(comment = "终端号")
private String terminalId;
@Alias("对方账户")
@DbColumn(comment = "对方账户")
private String otherAccount;
@Alias("订单金额(元)")
@DbColumn(comment = "订单金额(元)")
private String orderAmount;
@Alias("商家实收(元)")
@DbColumn(comment = "商家实收(元)")
private String realAmount;
@Alias("支付宝红包(元)")
@DbColumn(comment = "支付宝红包(元)")
private String alipayAmount;
@Alias("集分宝(元)")
@DbColumn(comment = "集分宝(元)")
private String jfbAmount;
@Alias("支付宝优惠(元)")
@DbColumn(comment = "支付宝优惠(元)")
private String alipayDiscountAmount;
@Alias("商家优惠(元)")
@DbColumn(comment = "商家优惠(元)")
private String discountAmount;
@Alias("券核销金额(元)")
@DbColumn(comment = "券核销金额(元)")
private String couponDiscountAmount;
@Alias("券名称")
@DbColumn(comment = "券名称")
private String couponName;
@Alias("商家红包消费金额(元)")
@DbColumn(comment = "商家红包消费金额(元)")
private String couponAmount;
@Alias("卡消费金额(元)")
@DbColumn(comment = "卡消费金额(元)")
private String cardAmount;
@Alias("退款批次号/请求号")
@DbColumn(comment = "退款批次号/请求号")
private String batchNo;
@Alias("服务费(元)")
@DbColumn(comment = "服务费(元)")
private String serviceAmount;
@Alias("分润(元)")
@DbColumn(comment = "分润(元)")
private String splitAmount;
@Alias("备注")
@DbColumn(comment = "备注")
private String remark;
}

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.entity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.hutool.core.annotation.Alias;
import lombok.Data;
@@ -10,29 +11,44 @@ import lombok.Data;
*/
@Data
public class AliReconcileBillTotal {
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
@Alias("门店编号")
@DbColumn(comment = "门店编号")
private String storeId;
@Alias("门店名称")
@DbColumn(comment = "门店名称")
private String storeName;
@Alias("交易订单总笔数")
@DbColumn(comment = "交易订单总笔数")
private String totalNum;
@Alias("退款订单总笔数")
@DbColumn(comment = "退款订单总笔数")
private String totalRefundNum;
@Alias("订单金额(元)")
@DbColumn(comment = "订单金额(元)")
private String totalOrderAmount;
@Alias("商家实收(元)")
@DbColumn(comment = "商家实收(元)")
private String totalAmount;
@Alias("支付宝优惠(元)")
@DbColumn(comment = "支付宝优惠(元)")
private String totalDiscountAmount;
@Alias("商家优惠(元)")
@DbColumn(comment = "商家优惠(元)")
private String totalCouponAmount;
// 卡消费金额(元) 服务费(元) 分润(元) 实收净额(元)
@Alias("卡消费金额(元)")
@DbColumn(comment = "卡消费金额(元)")
private String totalConsumeAmount;
@Alias("服务费(元)")
@DbColumn(comment = "服务费(元)")
private String totalServiceAmount;
@Alias("分润(元)")
@DbColumn(comment = "分润(元)")
private String totalShareAmount;
@Alias("实收净额(元)")
@DbColumn(comment = "实收净额(元)")
private String totalNetAmount;
}

View File

@@ -2,9 +2,13 @@ package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayCode;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliReconcileBillDetailManager;
import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliReconcileBillTotalManager;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
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.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.PayReconcileDetail;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvUtil;
@@ -20,6 +24,7 @@ import lombok.val;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
@@ -40,12 +45,19 @@ import java.util.stream.Collectors;
public class AliPayReconcileService {
private final AliPayConfigService configService;
private final AliReconcileBillDetailManager reconcileBillDetailManager;
private final AliReconcileBillTotalManager reconcileBillTotalManager;
/**
* 下载对账单, 并进行解析进行保存
*
* @param date 对账日期 yyyy-MM-dd 格式
* @param recordOrderId 对账订单ID
*/
@SneakyThrows
public void downAndSave(String date){
@Transactional(rollbackFor = Exception.class)
public void downAndSave(String date, Long recordOrderId){
AliPayConfig config = configService.getConfig();
configService.initConfig(config);
@@ -63,29 +75,73 @@ public class AliPayReconcileService {
// 获取对账单下载地址并下载
String url = response.getBillDownloadUrl();
byte[] bytes = HttpUtil.downloadBytes(url);
// 使用 Apache commons-compress 包装流,
// 使用 Apache commons-compress 包装流, 读取返回的对账CSV文件
ZipArchiveInputStream zipArchiveInputStream = new ZipArchiveInputStream(new ByteArrayInputStream(bytes),"GBK");
ZipArchiveEntry entry;
List<AliReconcileBillDetail> billDetails;
List<AliReconcileBillTotal> billTotals;
List<AliReconcileBillDetail> billDetails = new ArrayList<>();
List<AliReconcileBillTotal> billTotals = new ArrayList<>();
while ((entry= zipArchiveInputStream.getNextZipEntry()) != null){
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(zipArchiveInputStream,"GBK"));
List<String> strings = IoUtil.readLines(bufferedReader, new ArrayList<>());
String name = entry.getName();
if (StrUtil.endWith(name,"_业务明细(汇总).csv")){
// 汇总
billTotals = this.parseTotal(strings);
} else {
// 明细
billDetails = this.parseDetail(strings);
}
}
// 保存
System.out.println();
// 保存原始对账记录
this.save(billDetails, billTotals, recordOrderId);
// 将原始交易明细对账记录转换通用结构并保存到上下文中
this.convertAndSave(billDetails);
} catch (AlipayApiException e) {
log.error("下载对账单失败",e);
throw new RuntimeException(e);
}
}
/**
* 保存原始对账记录
*/
private void save(List<AliReconcileBillDetail> billDetails, List<AliReconcileBillTotal> billTotals, Long recordOrderId){
billDetails.forEach(o->o.setRecordOrderId(recordOrderId));
reconcileBillDetailManager.saveAll(billDetails);
billTotals.forEach(o->o.setRecordOrderId(recordOrderId));
reconcileBillTotalManager.saveAll(billTotals);
}
/**
* 转换为通用对账记录对象
*/
private void convertAndSave(List<AliReconcileBillDetail> billDetails){
List<PayReconcileDetail> collect = billDetails.stream()
.map(this::convert)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcile().setReconcileDetails(collect);
}
/**
* 转换为通用对账记录对象
*/
private PayReconcileDetail convert(AliReconcileBillDetail billDetail){
// 默认为支付
PayReconcileDetail payReconcileDetail = new PayReconcileDetail()
.setRecordOrderId(billDetail.getRecordOrderId())
.setOrderId(billDetail.getOutTradeNo())
.setTitle(billDetail.getSubject())
.setGatewayOrderNo(billDetail.getTradeNo());
// 如果是退款
if (Objects.equals(billDetail.getTradeType(), "refund")){
}
return payReconcileDetail;
}
/**
* 解析明细
*/

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileFundFlowDetail;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class WxReconcileFundFlowDetailManager extends BaseManager<WxReconcileFundFlowDetailMapper,WxReconcileFundFlowDetail> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.dao;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileFundFlowDetail;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Mapper
public interface WxReconcileFundFlowDetailMapper extends BaseMapper<WxReconcileFundFlowDetail> {
}

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileFundFlowTotal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class WxReconcileFundFlowTotalManager extends BaseManager<WxReconcileFundFlowTotalMapper, WxReconcileFundFlowTotal> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.dao;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WxReconcileFundFlowTotal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/1/21
*/
@Mapper
public interface WxReconcileFundFlowTotalMapper extends BaseMapper<WxReconcileFundFlowTotal> {
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -15,86 +16,117 @@ import lombok.EqualsAndHashCode;
@Data
@TableName("pay_wechat_reconcile_bill_detail")
public class WxReconcileBillDetail extends MpCreateEntity {
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
@Alias("交易时间")
@DbColumn(comment = "交易时间")
private String transactionTime;
@Alias("公众账号ID")
@DbColumn(comment = "公众账号ID")
private String appId;
@Alias("商户号")
@DbColumn(comment = "商户号")
private String merchantId;
@Alias("特约商户号")
@DbColumn(comment = "特约商户号")
private String subMerchantId;
@Alias("微信订单号")
@DbColumn(comment = "微信订单号")
private String weiXinOrderNo;
@Alias("商户订单号")
@DbColumn(comment = "商户订单号")
private String mchOrderNo;
@Alias("用户标识")
@DbColumn(comment = "用户标识")
private String userId;
@Alias("设备号")
@DbColumn(comment = "设备号")
private String deviceInfo;
@Alias("交易类型")
@DbColumn(comment = "交易类型")
private String type;
@Alias("交易状态")
@DbColumn(comment = "交易状态")
private String status;
@Alias("付款银行")
@DbColumn(comment = "付款银行")
private String bank;
@Alias("货币种类")
@DbColumn(comment = "货币种类")
private String currency;
@Alias("应结订单金额")
@DbColumn(comment = "应结订单金额")
private String amount;
@Alias("代金券金额")
@DbColumn(comment = "代金券金额")
private String envelopeAmount;
@Alias("微信退款单号")
@DbColumn(comment = "微信退款单号")
private String name;
@Alias("商户退款单号")
@DbColumn(comment = "商户退款单号")
private String packet;
@Alias("退款金额")
@DbColumn(comment = "退款金额")
private String poundage;
@Alias("充值券退款金额")
@DbColumn(comment = "充值券退款金额")
private String amount2;
@Alias("退款类型")
@DbColumn(comment = "退款类型")
private String rate;
@Alias("退款状态")
@DbColumn(comment = "退款状态")
private String orderAmount;
@Alias("商品名称")
@DbColumn(comment = "商品名称")
private String packet2;
@Alias("商户数据包")
@DbColumn(comment = "商户数据包")
private String packet3;
@Alias("手续费")
@DbColumn(comment = "手续费")
private String packet4;
@Alias("费率")
@DbColumn(comment = "费率")
private String packet5;
@Alias("订单金额")
@DbColumn(comment = "订单金额")
private String packet6;
@Alias("申请退款金额")
@DbColumn(comment = "申请退款金额")
private String packet7;
@Alias("费率备注")
@DbColumn(comment = "费率备注")
private String packet8;
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -16,25 +17,35 @@ import lombok.EqualsAndHashCode;
@TableName("pay_wechat_reconcile_bill_total")
public class WxReconcileBillTotal extends MpCreateEntity {
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
@Alias("总交易单数")
@DbColumn(comment = "总交易单数")
private String totalNum;
@Alias("应结订单总金额")
@DbColumn(comment = "应结订单总金额")
private String totalAmount;
@Alias("退款总金额")
@DbColumn(comment = "退款总金额")
private String totalRefundAmount;
@Alias("充值券退款总金额")
@DbColumn(comment = "充值券退款总金额")
private String totalRefundCouponAmount;
@Alias("手续费总金额")
@DbColumn(comment = "手续费总金额")
private String totalFee;
@Alias("订单总金额")
@DbColumn(comment = "订单总金额")
private String totalOrderAmount;
@Alias("申请退款总金额")
@DbColumn(comment = "申请退款总金额")
private String applyTotalRefundAmount;
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -15,26 +16,40 @@ import lombok.EqualsAndHashCode;
@Data
@TableName("pay_wechat_reconcile_fund_flow_detail")
public class WxReconcileFundFlowDetail extends MpCreateEntity {
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
@Alias("记账时间")
@DbColumn(comment = "记账时间")
private String time;
@Alias("微信支付业务单号")
@DbColumn(comment = "微信支付业务单号")
private String outTradeNo;
@Alias("资金流水单号")
private String id;
@DbColumn(comment = "资金流水单号")
private String flowId;
@Alias("业务名称")
@DbColumn(comment = "业务名称")
private String packet1;
@Alias("业务类型")
@DbColumn(comment = "业务类型")
private String packet2;
@Alias("收支类型")
@DbColumn(comment = "收支类型")
private String packet3;
@Alias("收支金额(元)")
@DbColumn(comment = "收支金额(元)")
private String amount;
@Alias("账户结余(元)")
@DbColumn(comment = "账户结余(元)")
private String balance;
@Alias("资金变更提交申请人")
@DbColumn(comment = "资金变更提交申请人")
private String packet4;
@Alias("备注")
@DbColumn(comment = "备注")
private String packet5;
@Alias("业务凭证号")
@DbColumn(comment = "业务凭证号")
private String packet6;
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -16,14 +17,21 @@ import lombok.EqualsAndHashCode;
@TableName("pay_wechat_reconcile_fund_flow_total")
public class WxReconcileFundFlowTotal extends MpCreateEntity {
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
@Alias("资金流水总笔数")
@DbColumn(comment = "资金流水总笔数")
private String totalNum;
@Alias("收入笔数")
@DbColumn(comment = "收入笔数")
private String incomeNum;
@Alias("收入金额")
@DbColumn(comment = "收入金额")
private String incomeAmount;
@Alias("支出笔数")
@DbColumn(comment = "支出笔数")
private String expenditureNum;
@Alias("支出金额")
@DbColumn(comment = "支出金额")
private String expenditureAmount;
}

View File

@@ -2,12 +2,14 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
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;
import cn.bootx.platform.daxpay.service.core.channel.wechat.dao.WxReconcileBillTotalManger;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
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.entity.PayReconcileDetail;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvUtil;
@@ -20,11 +22,13 @@ import com.ijpay.wxpay.model.DownloadFundFlowModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.service.code.WeChatPayCode.ACCOUNT_TYPE_BASIC;
@@ -37,7 +41,6 @@ import static cn.bootx.platform.daxpay.service.code.WeChatPayCode.ACCOUNT_TYPE_B
@Service
@RequiredArgsConstructor
public class WechatPayReconcileService{
private final WeChatPayConfigService weChatPayConfigService;
private final WxReconcileBillTotalManger reconcileBillTotalManger;
private final WxReconcileBillDetailManager reconcileBillDetailManager;
@@ -45,17 +48,20 @@ public class WechatPayReconcileService{
* 下载对账单并保存
* @param date 对账日期 yyyyMMdd 格式
*/
public void downAndSave(String date, WeChatPayConfig config) {
config = weChatPayConfigService.getConfig();
this.downBill(date, config);
this.downFundFlow(date, config);
@Transactional(rollbackFor = Exception.class)
public void downAndSave(String date, Long recordOrderId, WeChatPayConfig config) {
this.downBill(date, recordOrderId, config);
// 资金对账单先不启用
// this.downFundFlow(date, recordOrderId, config);
}
/**
* 下载交易账单
* @param date 对账日期 yyyyMMdd 格式
*
* @param date 对账日期 yyyyMMdd 格式
* @param recordOrderId 对账订单ID
*/
public void downBill(String date, WeChatPayConfig config){
public void downBill(String date, Long recordOrderId, WeChatPayConfig config){
// 下载交易账单
Map<String, String> params = DownloadBillModel.builder()
.mch_id(config.getWxMchId())
@@ -74,14 +80,41 @@ public class WechatPayReconcileService{
result = result.replaceAll("`", "").replaceAll("\uFEFF", "");
CsvReader reader = CsvUtil.getReader();
// 获取交易记录
// 获取交易记录并保存
String billDetail = StrUtil.subBefore(result, "总交易单数", false);
List<WxReconcileBillDetail> billDetails = reader.read(billDetail, WxReconcileBillDetail.class);
billDetails.forEach(o->o.setRecordOrderId(recordOrderId));
reconcileBillDetailManager.saveAll(billDetails);
// 获取汇总数据
// 获取汇总数据并保存
String billTotal = result.substring(result.indexOf("总交易单数"));
List<WxReconcileBillTotal> billTotals = reader.read(billTotal, WxReconcileBillTotal.class);
billTotals.forEach(o->o.setRecordOrderId(recordOrderId));
reconcileBillTotalManger.saveAll(billTotals);
// 将原始交易明细对账记录转换通用结构并保存到上下文中
this.convertAndSave(billDetails);
}
/**
* 转换为通用对账记录对象
*/
public void convertAndSave(List<WxReconcileBillDetail> billDetails){
List<PayReconcileDetail> collect = billDetails.stream()
.map(this::convert)
.collect(Collectors.toList());
// 写入到上下文中
PaymentContextLocal.get().getReconcile().setReconcileDetails(collect);
}
/**
* 转换为通用对账记录对象
*/
public PayReconcileDetail convert(WxReconcileBillDetail billDetail){
PayReconcileDetail payReconcileDetail = new PayReconcileDetail()
.setRecordOrderId(billDetail.getRecordOrderId());
return payReconcileDetail;
}
@@ -90,9 +123,10 @@ public class WechatPayReconcileService{
* 如果出现 No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
* 更换一下最新的JDK即可, 例如 corretto Jdk
*
* @param date 对账日期 yyyyMMdd 格式
* @param date 对账日期 yyyyMMdd 格式
* @param recordOrderId 对账订单ID
*/
public void downFundFlow(String date, WeChatPayConfig config){
public void downFundFlow(String date, Long recordOrderId, WeChatPayConfig config){
if (StrUtil.isBlank(config.getP12())){
return;
}

View File

@@ -1,44 +1,52 @@
package cn.bootx.platform.daxpay.service.core.order.reconcile.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付对账记录详情
* 通用支付对账记录
* @author xxm
* @since 2024/1/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("pay_reconcile_detail_record")
@TableName("pay_reconcile_detail")
public class PayReconcileDetail extends MpCreateEntity {
/** 交易状态 */
/** 关联对账订单ID */
@DbColumn(comment = "关联对账订单ID")
private Long recordOrderId;
/**
* 交易状态
* @see cn.bootx.platform.daxpay.code.PayStatusEnum
*/
@DbColumn(comment = "交易状态")
private String status;
/** 交易类型 支付/退款 */
@DbColumn(comment = "交易类型")
private String type;
/** 订单 - 支付ID/退款ID */
private String paymentId;
/** 订单id - 支付ID/退款ID */
@DbColumn(comment = "订单id")
private String orderId;
/** 网关订单号 - 支付宝/微信的订单号 */
@DbColumn(comment = "网关订单号")
private String gatewayOrderNo;
/** 交易金额 */
@DbColumn(comment = "交易金额")
private Integer amount;
/** 手续费 */
private Integer poundage;
/** 费率 */
private Integer rate;
/** 商品名称 */
@DbColumn(comment = "商品名称")
private String title;
}

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.core.order.reconcile.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -10,15 +11,15 @@ import lombok.experimental.Accessors;
import java.time.LocalDate;
/**
* 支付对账单记录
* 支付对账单订单
* @author xxm
* @since 2024/1/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@DbTable(comment = "支付对账单记录")
@TableName("pay_reconcile_record")
@DbTable(comment = "支付对账单订单")
@TableName("pay_reconcile_order")
public class PayReconcileOrder extends MpCreateEntity {
/**
@@ -26,21 +27,27 @@ public class PayReconcileOrder extends MpCreateEntity {
* 规则:通道简称 + yyyyMMdd + 两位流水号
* 例子wx2024012001、ali2024012002
*/
@DbColumn(comment = "批次号")
private String batchNo;
/** 日期 */
@DbColumn(comment = "日期")
private LocalDate date;
/** 通道 */
@DbColumn(comment = "通道")
private String channel;
/** 对账单是否下载成功 */
/** 是否下载成功 */
@DbColumn(comment = "是否下载成功")
private boolean down;
/** 是否比对完成 */
@DbColumn(comment = "是否比对完成")
private boolean compare;
/** 错误信息 */
@DbColumn(comment = "错误信息")
private String errorMsg;
}

View File

@@ -0,0 +1,51 @@
package cn.bootx.platform.daxpay.service.core.order.reconcile.service;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.PayReconcileDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.PayReconcileOrderManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 支付对账订单查询服务类
* @author xxm
* @since 2024/1/21
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayReconcileOrderQueryService {
private final PayReconcileOrderManager orderManager;
private final PayReconcileDetailManager detailManager;
/**
* 分页
*/
public PageResult<?> page(PageParam pageParam){
return null;
}
/**
* 对账订单详情
*/
public void findById(Long id){
}
/**
* 明细分页
*/
public PageResult<?> pageDetail(PageParam pageParam, Long orderId){
return null;
}
/**
* 明细详情
*/
public void findDetailById(Long id){
}
}

View File

@@ -28,7 +28,6 @@ public class PayReconcileOrderService {
private final PayReconcileOrderManager reconcileOrderManager;
private final Sequence sequence;
/**
* 更新, 开启一个新事务进行更新
*/

View File

@@ -1,6 +1,10 @@
package cn.bootx.platform.daxpay.service.core.payment.reconcile.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.daxpay.service.common.context.PaymentContext;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.PayReconcileDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.PayReconcileDetail;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.PayReconcileOrder;
import cn.bootx.platform.daxpay.service.core.order.reconcile.service.PayReconcileOrderService;
import cn.bootx.platform.daxpay.service.core.payment.reconcile.factory.PayReconcileStrategyFactory;
@@ -9,6 +13,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 支付对账单下载服务
* @author xxm
@@ -19,6 +25,7 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class PayReconcileService {
private final PayReconcileOrderService reconcileOrderService;
private final PayReconcileDetailManager reconcileDetailManager;
/**
* 下载对账单并进行保存
@@ -33,6 +40,9 @@ public class PayReconcileService {
* 下载对账单并进行保存
*/
public void downAndSave(PayReconcileOrder recordOrder) {
// 初始化上下文
PaymentContextLocal.setIfAbsent(new PaymentContext());
// 构建对账策略
AbsReconcileStrategy absReconcileStrategy = PayReconcileStrategyFactory.create(recordOrder.getChannel());
absReconcileStrategy.initParam(recordOrder);
absReconcileStrategy.doBeforeHandler();
@@ -46,6 +56,11 @@ public class PayReconcileService {
reconcileOrderService.update(recordOrder);
throw new RuntimeException(e);
}
// 保存转换后的通用结构对账单
List<PayReconcileDetail> reconcileDetails = PaymentContextLocal.get()
.getReconcile()
.getReconcileDetails();
reconcileDetailManager.saveAll(reconcileDetails);
}
}

View File

@@ -52,11 +52,11 @@ public class AlipayReconcileStrategy extends AbsReconcileStrategy {
}
/**
* 下载对账单
* 下载和保存对账单
*/
@Override
public void downAndSave() {
String format = LocalDateTimeUtil.format(this.getRecordOrder().getDate(), DatePattern.NORM_DATE_PATTERN);
reconcileService.downAndSave(format);
String date = LocalDateTimeUtil.format(this.getRecordOrder().getDate(), DatePattern.NORM_DATE_PATTERN);
reconcileService.downAndSave(date,this.getRecordOrder().getId());
}
}

View File

@@ -55,6 +55,6 @@ 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);
reconcileService.downAndSave(format,this.getRecordOrder().getId(), this.config);
}
}