From b13eecbb8d572cdd056eb7d5aed90c05a973e62e Mon Sep 17 00:00:00 2001 From: Jack <46790855@qq.com> Date: Thu, 18 Sep 2025 21:54:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E8=B4=A6=E5=AE=9E=E4=BD=93=E7=B1=BB?= =?UTF-8?q?=20=E6=B5=8B=E8=AF=95=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/lakala/LklOrderSeparate.java | 16 +- .../mall/common/pojo/dto/LklSeparateDTO.java | 1452 ++++++++--------- .../common/pojo/dto/LklSeparateDTOBak.java | 849 ++++++++++ .../service/impl/LakalaApiServiceImpl.java | 334 +--- 4 files changed, 1592 insertions(+), 1059 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTOBak.java diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java index 86928dc4..46536d69 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklOrderSeparate.java @@ -49,9 +49,6 @@ public class LklOrderSeparate { @ApiModelProperty(value = "平台订单Id") private String order_id; - @ApiModelProperty(value = "分账总金额,单位:分") - private String total_amt; - @ApiModelProperty(value = "分账计算类型:0- 按照指定金额,1- 按照指定比例。默认 0") private String cal_type; @@ -79,8 +76,14 @@ public class LklOrderSeparate { @ApiModelProperty(value = "最终分账明细,JSON 格式") private String detail_datas; - @ApiModelProperty(value = "总计分账金额") - private Integer total_separate_value; + @ApiModelProperty(value = "分账发生总金额") + private String total_amt; + + @ApiModelProperty(value = "实际分账金额") + private String actual_separate_amt; + + @ApiModelProperty(value = "分账费用") + private String total_fee_amt; @ApiModelProperty(value = "拉卡拉机构编号") private String lkl_org_no; @@ -91,6 +94,9 @@ public class LklOrderSeparate { @ApiModelProperty(value = "处理状态:ACCEPTED-已受理, PROCESSING-处理中, FAIL-失败, SUCCESS-成功") private String final_status; + @ApiModelProperty(value = "异步通知数据") + private String notify_resp; + @ApiModelProperty(value = "分账(失败后)的标记") private String remark; diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java index abcba871..f9bf2a6f 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java @@ -1,848 +1,792 @@ package com.suisung.mall.common.pojo.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.data.util.Pair; import java.math.BigDecimal; import java.math.RoundingMode; /** - * 拉卡拉订单分账信息对象 - *

- * 分账计算支持两种模式: - * 1. 基于可分账金额模式(默认):先扣除拉卡拉手续费和配送费,再对剩余金额进行分账 - * 2. 基于总金额模式:基于总分账金额进行分账计算 - *

- * 分账优先级顺序:拉卡拉 > 平台 > 一级代理商 > 二级代理商 > 商户 - * 商户作为最后分账方,其金额为剩余金额,不强制遵循分账比例 + * 拉卡拉订单分账信息处理类 + * 实现基于总金额和基于可分账金额的分账算法,支持平台、代理商和商家的多级分账 */ @Data @EqualsAndHashCode(callSuper = false) -@ApiModel(value = "拉卡拉订单分账信息对象", description = "拉卡拉订单分账信息对象") -public class LklSeparateDTO implements java.io.Serializable { - private static final Logger logger = LoggerFactory.getLogger(LklSeparateDTO.class); +public class LklSeparateDTO { + // 基础金额属性 + private Integer totalSeparateAmount; // 分账总金额(分) + private Integer canSeparateAmount; // 可分账金额(分) + private Integer refCanSeparateAmount; // 拉卡拉参考可分账金额(分) + private Integer shippingFee; // 配送费(分) - private static final long serialVersionUID = 1L; + // 分账比例属性 + private BigDecimal lklRatio; // 拉卡拉分账比例(如 0.0025=0.25%) + private BigDecimal mchRatio; // 商户分账比例(如 0.96=96%) + private BigDecimal platRatio; // 平台分账比例(如 0.01=1%) + private BigDecimal agent1stRatio; // 一级代理商分账比例(如 0.01=1%) + private BigDecimal agent2ndRatio; // 二级代理商分账比例(如 0.03=3%) - // ================================ 基础金额属性 ================================ - - @ApiModelProperty(value = "分账总金额(分)") - private Integer totalSeparateAmount; - - @ApiModelProperty(value = "可分账金额(分)") - private Integer canSeparateAmount; - - @ApiModelProperty(value = "拉卡拉参考可分账金额(分)") - private Integer refCanSeparateAmount; - - @ApiModelProperty(value = "配送费(分)") - private Integer shippingFee; - - // ================================ 分账比例属性 ================================ - - @ApiModelProperty(value = "拉卡拉分账比例(如 0.0025=0.25%)") - private BigDecimal lklRatio; - - @ApiModelProperty(value = "商户分账比例(如 0.96=96%)") - private BigDecimal mchRatio; - - @ApiModelProperty(value = "平台分账比例(如 0.01=1%)") - private BigDecimal platRatio; - - @ApiModelProperty(value = "一级代理商分账比例(如 0.01=1%)") - private BigDecimal agent1stRatio; - - @ApiModelProperty(value = "二级代理商分账比例(如 0.03=3%)") - private BigDecimal agent2ndRatio; - - // ================================ 分账金额结果属性 ================================ - - @ApiModelProperty(value = "拉卡拉分账金额(分)") - private Integer lklAmount; - - @ApiModelProperty(value = "商户分账金额(分)") - private Integer mchAmount; - - @ApiModelProperty(value = "平台分账金额(分)") - private Integer platAmount; - - @ApiModelProperty(value = "一级代理商分账金额(分)") - private Integer agent1stAmount; - - @ApiModelProperty(value = "二级代理商分账金额(分)") - private Integer agent2ndAmount; - - // ================================ 内部状态属性 ================================ - - /** - * 分账计算方法类型 - * true表示基于总金额分账(calcOnTotalAmount方法被调用) - * false表示基于可分账金额分账(calcOnCanAmount方法被调用) - */ - private boolean isBasedOnTotalAmount = false; - - // ================================ 构造方法 ================================ - - public LklSeparateDTO() { - // 默认构造函数 - } - - // ================================ 公共方法 ================================ + // 分账金额结果属性 + private Integer lklAmount; // 拉卡拉分账金额(分) + private Integer mchAmount; // 商户分账金额(分) + private Integer platAmount; // 平台分账金额(分) + private Integer agent1stAmount; // 一级代理商分账金额(分) + private Integer agent2ndAmount; // 二级代理商分账金额(分) /** * 测试方法 */ public static void main(String[] args) { - logger.info("开始测试分账计算功能..."); + // 创建分账对象 + LklSeparateDTO dto = new LklSeparateDTO(); - // 临界点测试用例2: 小金额测试 - logger.info("\n临界点测试用例2 - 小金额测试:"); + // 设置测试参数 - 正常情况 + dto.setTotalSeparateAmount(800); // 分账总额 1000分 + dto.setShippingFee(500); // 配送费 100分 + dto.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025 + dto.setMchRatio(new BigDecimal("0.94")); // 商家分账比例 0.94 + dto.setPlatRatio(new BigDecimal("0.06")); // 平台分账比例 0.01 + // dto.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例 0.01 + // dto.setAgent2ndRatio(new BigDecimal("0.01")); // 二级代理商分账比例 0.01 + + // 执行基于总金额的分账算法 + System.out.println("=== 基于总金额的分账算法测试(正常情况) ==="); + SharingResult result1 = dto.sharingOnTotalAmount(); + System.out.println(result1); + if (result1.isSuccess()) { + printSharingDetails(dto, "基于总金额"); + } + + // 测试基于可分账金额的分账算法(正常情况) + System.out.println("\n=== 基于可分账金额的分账算法测试(正常情况) ==="); LklSeparateDTO dto2 = new LklSeparateDTO(); - dto2.setTotalSeparateAmount(1); - dto2.setShippingFee(0); - dto2.setLklRatio(new BigDecimal("0.0025")); - dto2.setMchRatio(new BigDecimal("0.96")); - dto2.setPlatRatio(new BigDecimal("0.01")); + dto2.setTotalSeparateAmount(800); // 分账总额 1000分 + dto2.setShippingFee(500); // 配送费 100分 + dto2.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025 + dto2.setMchRatio(new BigDecimal("0.94")); // 商家分账比例 0.857 (会产生小数) + dto2.setPlatRatio(new BigDecimal("0.06")); // 平台分账比例 0.01 + // 不设置一级和二级代理商分账比例,测试不参与分账的情况 + // dto2.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例 0.023 (会产生小数) + // dto2.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例 0.031 (会产生小数) -// dto2.setAgent1stRatio(new BigDecimal("0.01")); -// dto2.setAgent2ndRatio(new BigDecimal("0.04")); -// dto2.setRefCanSeparateAmount(8079); // 设置参考可分账金额 + SharingResult result2 = dto2.sharingOnCanSeparateAmount(); + System.out.println(result2); + if (result2.isSuccess()) { + printSharingDetails(dto2, "基于可分账金额"); - logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}", - dto2.getTotalSeparateAmount(), dto2.getShippingFee(), dto2.getLklRatio(), - dto2.getMchRatio(), dto2.getPlatRatio()); - - Pair result2 = dto2.calcOnCanSeparateAmount(); - logger.info("分账计算结果: {}", (result2.getFirst() ? "成功" : "失败")); - if (result2.getFirst()) { - logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 参考可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", - dto2.getTotalSeparateAmount(), dto2.getLklAmount(), dto2.getShippingFee(), dto2.getCanSeparateAmount(), dto2.getRefCanSeparateAmount(), - dto2.getMchAmount(), dto2.getPlatAmount(), dto2.getAgent1stAmount(), dto2.getAgent2ndAmount()); - - // 计算并打印各分账方占可分账金额和总金额的百分比 - int canSeparateAmount = dto2.getCanSeparateAmount(); - int totalSeparateAmount = dto2.getTotalSeparateAmount(); - - if (canSeparateAmount > 0) { - double lklRatioOfTotal = (double) dto2.getLklAmount() / totalSeparateAmount * 100; - double mchRatioOfTotal = (double) dto2.getMchAmount() / totalSeparateAmount * 100; - double platRatioOfTotal = (double) dto2.getPlatAmount() / totalSeparateAmount * 100; - double agent1stRatioOfTotal = 0.0; - double agent2ndRatioOfTotal = 0.0; - - // 安全检查,避免空指针异常 - if (dto2.getAgent1stAmount() != null) { - agent1stRatioOfTotal = (double) dto2.getAgent1stAmount() / totalSeparateAmount * 100; - } - if (dto2.getAgent2ndAmount() != null) { - agent2ndRatioOfTotal = (double) dto2.getAgent2ndAmount() / totalSeparateAmount * 100; - } - - double lklRatioOfAvailable = (double) dto2.getLklAmount() / canSeparateAmount * 100; - double mchRatioOfAvailable = (double) dto2.getMchAmount() / canSeparateAmount * 100; - double platRatioOfAvailable = (double) dto2.getPlatAmount() / canSeparateAmount * 100; - double agent1stRatioOfAvailable = 0.0; - double agent2ndRatioOfAvailable = 0.0; - - // 安全检查,避免空指针异常 - if (dto2.getAgent1stAmount() != null) { - agent1stRatioOfAvailable = (double) dto2.getAgent1stAmount() / canSeparateAmount * 100; - } - if (dto2.getAgent2ndAmount() != null) { - agent2ndRatioOfAvailable = (double) dto2.getAgent2ndAmount() / canSeparateAmount * 100; - } - - logger.info("占比详情 (占总金额/占可分账金额): 拉卡拉={}%/{}%, 商户={}%/{}%, 平台={}%/{}%, 一级代理商={}%/{}%, 二级代理商={}%/{}%", - String.format("%.2f", lklRatioOfTotal), String.format("%.2f", lklRatioOfAvailable), - String.format("%.2f", mchRatioOfTotal), String.format("%.2f", mchRatioOfAvailable), - String.format("%.2f", platRatioOfTotal), String.format("%.2f", platRatioOfAvailable), - String.format("%.2f", agent1stRatioOfTotal), String.format("%.2f", agent1stRatioOfAvailable), - String.format("%.2f", agent2ndRatioOfTotal), String.format("%.2f", agent2ndRatioOfAvailable)); - } - - int total = dto2.getLklAmount() + dto2.getMchAmount() + dto2.getPlatAmount() + - (dto2.getAgent1stAmount() != null ? dto2.getAgent1stAmount() : 0) + - (dto2.getAgent2ndAmount() != null ? dto2.getAgent2ndAmount() : 0) + dto2.getShippingFee(); - logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto2.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto2.getTotalSeparateAmount()); } + + // 测试toJSON和toString方法 + System.out.println("\n=== toJSON和toString方法测试 ==="); + System.out.println("基于分账总额算法的JSON输出:"); + System.out.println(dto.toJSON()); + + System.out.println("\n基于分账总额算法的toString输出:"); + System.out.println(dto); + + System.out.println("\n=== toJSON和toString方法测试 ==="); + System.out.println("基于可分账金额算法的JSON输出:"); + System.out.println(dto2.toJSON()); + + System.out.println("\n基于可分账金额算法的toString输出:"); + System.out.println(dto2); } /** - * 基于可分账金额的分账计算方法(默认) - *

- * 计算逻辑: - * 1. 计算拉卡拉分账金额 = 总分账金额 × 拉卡拉分账比例(向上取整) - * 2. 计算可分账金额 = 总分账金额 - 配送费 - 拉卡拉分账金额 - * 3. 如果refCanSeparateAmount有效且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账基数 - * 4. 调整分账比例,确保有效比例之和等于1.0 - * 5. 按优先级计算各参与方分账金额(商户 > 平台 > 一级代理商 > 二级代理商) - * 6. 确保总额平衡并保障各参与方分账比例 + * 打印分账详情 * - * @return Pair 分账结果是否成功和失败的原因 + * @param dto 分账对象 + * @param type 分账类型 */ - public Pair calcOnCanSeparateAmount() { - // 设置分账计算方法类型 - isBasedOnTotalAmount = false; - - // 检查前提条件 - if (!validateInputs()) { - return Pair.of(false, "分账参数不全"); + private static void printSharingDetails(LklSeparateDTO dto, String type) { + System.out.println(type + "分账详情:"); + if (dto.getTotalSeparateAmount() != null) { + System.out.println(" 分账总金额: " + dto.getTotalSeparateAmount() + "分"); + } + // 打印拉卡拉分账金额(即使为0也要显示) + if (dto.getLklAmount() != null) { + System.out.println(" 拉卡拉分账金额: " + dto.getLklAmount() + "分" + + " (占比: " + calculatePercentage(dto.getLklAmount(), dto.getTotalSeparateAmount()) + "%)"); + } + // 打印配送费(即使为0也要显示) + if (dto.getShippingFee() != null) { + System.out.println(" 配送费: " + dto.getShippingFee() + "分" + + " (占比: " + calculatePercentage(dto.getShippingFee(), dto.getTotalSeparateAmount()) + "%)"); + } + if (dto.getCanSeparateAmount() != null) { + System.out.println(" 可分账金额: " + dto.getCanSeparateAmount() + "分"); + } + // 打印平台分账金额(即使为0也要显示) + if (dto.getPlatAmount() != null) { + System.out.println(" 平台分账金额: " + dto.getPlatAmount() + "分" + + (dto.getTotalSeparateAmount() != null + ? " (总金额占比: " + calculatePercentage(dto.getPlatAmount(), dto.getTotalSeparateAmount()) + + "%)" + : "") + + + (dto.getCanSeparateAmount() != null + ? " (可分账占比: " + calculatePercentage(dto.getPlatAmount(), dto.getCanSeparateAmount()) + "%)" + : "")); + } + // 打印一级代理商分账金额(即使为0也要显示) + if (dto.getAgent1stAmount() != null) { + System.out.println(" 一级代理商分账金额: " + dto.getAgent1stAmount() + "分" + + (dto.getTotalSeparateAmount() != null + ? " (总金额占比: " + calculatePercentage(dto.getAgent1stAmount(), dto.getTotalSeparateAmount()) + + "%)" + : "") + + + (dto.getCanSeparateAmount() != null + ? " (可分账占比: " + calculatePercentage(dto.getAgent1stAmount(), dto.getCanSeparateAmount()) + + "%)" + : "")); + } + // 打印二级代理商分账金额(即使为0也要显示) + if (dto.getAgent2ndAmount() != null) { + System.out.println(" 二级代理商分账金额: " + dto.getAgent2ndAmount() + "分" + + (dto.getTotalSeparateAmount() != null + ? " (总金额占比: " + calculatePercentage(dto.getAgent2ndAmount(), dto.getTotalSeparateAmount()) + + "%)" + : "") + + + (dto.getCanSeparateAmount() != null + ? " (可分账占比: " + calculatePercentage(dto.getAgent2ndAmount(), dto.getCanSeparateAmount()) + + "%)" + : "")); + } + // 打印商家分账金额 + if (dto.getMchAmount() != null) { + System.out.println(" 商家分账金额: " + dto.getMchAmount() + "分" + + (dto.getTotalSeparateAmount() != null + ? " (总金额占比: " + calculatePercentage(dto.getMchAmount(), dto.getTotalSeparateAmount()) + "%)" + : "") + + + (dto.getCanSeparateAmount() != null + ? " (可分账占比: " + calculatePercentage(dto.getMchAmount(), dto.getCanSeparateAmount()) + "%)" + : "")); } - logger.info("开始基于可分账金额的分账计算,总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); - - // 计算拉卡拉分账金额 - calculateLklAmount(); - - // 计算可分账金额 - calculateCanSeparateAmount(); - if (canSeparateAmount <= 0) { - String errorMsg = "分账计算失败:可分账金额必须大于0,当前值=" + canSeparateAmount; - logger.info(errorMsg); - return Pair.of(false, errorMsg); + // 验证分账是否平衡 + if (dto.getTotalSeparateAmount() != null) { + int totalDistributed = (dto.getLklAmount() != null ? dto.getLklAmount() : 0) + + (dto.getShippingFee() != null ? dto.getShippingFee() : 0) + + (dto.getPlatAmount() != null ? dto.getPlatAmount() : 0) + + (dto.getAgent1stAmount() != null ? dto.getAgent1stAmount() : 0) + + (dto.getAgent2ndAmount() != null ? dto.getAgent2ndAmount() : 0) + + (dto.getMchAmount() != null ? dto.getMchAmount() : 0); + System.out.println(" 分账平衡验证: " + (totalDistributed == dto.getTotalSeparateAmount() ? "通过" : "失败")); + System.out.println(" 计算的分账总额: " + totalDistributed + "分"); } - - // 检查并应用参考可分账金额 - checkAndApplyRefCanSeparateAmount(); - - // 调整分账比例 - adjustRatios(); - - // 按优先级分账 - performSeparate(); - - // 确保总额平衡 - balanceAmounts(); - - // 保障各参与方分账比例 - guaranteeRatios(); - - String successMsg = String.format("基于可分账金额的分账计算完成:拉卡拉=%d分, 平台=%d分, 一级代理商=%d分, 二级代理商=%d分, 商户=%d分", - lklAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); - logger.info(successMsg); - return Pair.of(true, successMsg); } /** - * 基于总金额的分账计算方法 - *

- * 计算逻辑: - * 1. 计算拉卡拉分账金额 = 总分账金额 × 拉卡拉分账比例(四舍五入保留两位小数) - * 2. 计算可分账金额 = 总分账金额 - 配送费 - 拉卡拉分账金额 - * 3. 如果refCanSeparateAmount有效且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账基数 - * 4. 调整分账比例,确保有效比例之和等于1.0 - * 5. 按优先级计算各参与方分账金额(平台 > 一级代理商 > 二级代理商 > 商户),基于可分账金额进行分账 - * 6. 确保总额平衡并保障各参与方分账比例 + * 计算金额占总额的百分比 * - * @return Pair 分账结果是否成功和失败的原因 + * @param amount 金额 + * @param total 总金额 + * @return 百分比字符串 */ - public Pair calcOnTotalAmount() { - // 设置分账计算方法类型 - isBasedOnTotalAmount = true; - - // 检查前提条件 - if (!validateInputsForTotalAmount()) { - return Pair.of(false, "分账参数不全"); + public static String calculatePercentage(Integer amount, Integer total) { + if (amount == null || total == null || total == 0) { + return "0.00"; } - - logger.info("开始基于总金额的分账计算,总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); - - // 计算拉卡拉分账金额 - calculateLklAmount(); - - // 计算可分账金额 - calculateCanSeparateAmount(); - if (canSeparateAmount <= 0) { - String errorMsg = "基于总金额的分账计算失败:可分账金额必须大于0,当前值=" + canSeparateAmount; - logger.info(errorMsg); - return Pair.of(false, errorMsg); - } - - // 调整分账比例 - adjustRatios(); - - // 检查并应用参考可分账金额 - checkAndApplyRefCanSeparateAmount(); - - // 按优先级分账(基于总金额) - performSeparateBasedOnTotalAmount(); - - // 确保总额平衡 - balanceAmounts(); - - // 保障各参与方分账比例(忽略商户比例保障) - guaranteeRatiosForTotalAmount(); - - String successMsg = String.format("基于总金额的分账计算完成:拉卡拉=%d分, 平台=%d分, 一级代理商=%d分, 二级代理商=%d分, 商户=%d分", - lklAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); - logger.info(successMsg); - return Pair.of(true, successMsg); + BigDecimal percentage = new BigDecimal(amount).multiply(new BigDecimal("100")) + .divide(new BigDecimal(total), 2, RoundingMode.HALF_UP); + return percentage.toString(); } - /** - * 输出具体的分账结果 - * - * @return 分账结果字符串 - */ - @Override - public String toString() { - int canSeparateAmount = getCanSeparateAmount(); - int totalSeparateAmount = getTotalSeparateAmount(); - - // 计算各分账方占总金额和可分账金额的百分比 - double lklRatioOfTotal = 0.0; - double mchRatioOfTotal = 0.0; - double platRatioOfTotal = 0.0; - double agent1stRatioOfTotal = 0.0; - double agent2ndRatioOfTotal = 0.0; - double shippingFeeRatioOfTotal = 0.0; - - double lklRatioOfAvailable = 0.0; - double mchRatioOfAvailable = 0.0; - double platRatioOfAvailable = 0.0; - double agent1stRatioOfAvailable = 0.0; - double agent2ndRatioOfAvailable = 0.0; - double shippingFeeRatioOfAvailable = 0.0; - - if (totalSeparateAmount > 0) { - if (getLklAmount() != null) { - lklRatioOfTotal = (double) getLklAmount() / totalSeparateAmount * 100; - } - if (getMchAmount() != null) { - mchRatioOfTotal = (double) getMchAmount() / totalSeparateAmount * 100; - } - if (getPlatAmount() != null) { - platRatioOfTotal = (double) getPlatAmount() / totalSeparateAmount * 100; - } - if (getAgent1stAmount() != null) { - agent1stRatioOfTotal = (double) getAgent1stAmount() / totalSeparateAmount * 100; - } - if (getAgent2ndAmount() != null) { - agent2ndRatioOfTotal = (double) getAgent2ndAmount() / totalSeparateAmount * 100; - } - if (getShippingFee() != null) { - shippingFeeRatioOfTotal = (double) getShippingFee() / totalSeparateAmount * 100; - } - } - - if (canSeparateAmount > 0) { - if (getLklAmount() != null) { - lklRatioOfAvailable = (double) getLklAmount() / canSeparateAmount * 100; - } - if (getMchAmount() != null) { - mchRatioOfAvailable = (double) getMchAmount() / canSeparateAmount * 100; - } - if (getPlatAmount() != null) { - platRatioOfAvailable = (double) getPlatAmount() / canSeparateAmount * 100; - } - if (getAgent1stAmount() != null) { - agent1stRatioOfAvailable = (double) getAgent1stAmount() / canSeparateAmount * 100; - } - if (getAgent2ndAmount() != null) { - agent2ndRatioOfAvailable = (double) getAgent2ndAmount() / canSeparateAmount * 100; - } - if (getShippingFee() != null) { - shippingFeeRatioOfAvailable = (double) getShippingFee() / canSeparateAmount * 100; - } - } - - String calculationMethod = isBasedOnTotalAmount ? "基于总交易额方式计算" : "基于可分账额方式计算"; - - return String.format("分账结果%s,总交易额=%d分, 可分账额=%d分, 拉卡拉手续费=%d分(%.2f%%/%.2f%%), 配送费=%d分(%.2f%%/%.2f%%), 商户=%d分(%.2f%%/%.2f%%), 平台=%d分(%.2f%%/%.2f%%), 一级代理商=%d分(%.2f%%/%.2f%%), 二级代理商=%d分(%.2f%%/%.2f%%)", - calculationMethod, - totalSeparateAmount, - canSeparateAmount, - getLklAmount() != null ? getLklAmount() : 0, - lklRatioOfTotal, lklRatioOfAvailable, - getShippingFee() != null ? getShippingFee() : 0, - shippingFeeRatioOfTotal, shippingFeeRatioOfAvailable, - getMchAmount() != null ? getMchAmount() : 0, - mchRatioOfTotal, mchRatioOfAvailable, - getPlatAmount() != null ? getPlatAmount() : 0, - platRatioOfTotal, platRatioOfAvailable, - getAgent1stAmount() != null ? getAgent1stAmount() : 0, - agent1stRatioOfTotal, agent1stRatioOfAvailable, - getAgent2ndAmount() != null ? getAgent2ndAmount() : 0, - agent2ndRatioOfTotal, agent2ndRatioOfAvailable); - } /** - * 将对象转换为JSON字符串格式 + * 将分账对象转换为JSON格式字符串 * - * @return 包含所有属性的JSON字符串 + * @return JSON格式字符串 */ public String toJSON() { StringBuilder json = new StringBuilder(); - json.append("{"); + json.append("{\n"); - // 添加基本属性 - appendProperty(json, "totalSeparateAmount", totalSeparateAmount, true); - appendProperty(json, "canSeparateAmount", canSeparateAmount, false); - appendProperty(json, "refCanSeparateAmount", refCanSeparateAmount, false); - appendProperty(json, "shippingFee", shippingFee, false); + // 基础信息 + json.append(" \"分账算法类型\": \"拉卡拉订单分账信息处理\",\n"); + json.append(" \"分账总金额\": ").append(totalSeparateAmount != null ? totalSeparateAmount : 0).append(",\n"); + json.append(" \"配送费\": ").append(shippingFee != null ? shippingFee : 0).append(",\n"); - // 添加比例属性 - appendProperty(json, "lklRatio", lklRatio, false); - appendProperty(json, "mchRatio", mchRatio, false); - appendProperty(json, "platRatio", platRatio, false); - appendProperty(json, "agent1stRatio", agent1stRatio, false); - appendProperty(json, "agent2ndRatio", agent2ndRatio, false); + // 分账比例 + json.append(" \"分账比例\": {\n"); + json.append(" \"拉卡拉分账比例\": \"").append(lklRatio != null ? lklRatio.toString() : "0").append("\",\n"); + json.append(" \"商家分账比例\": \"").append(mchRatio != null ? mchRatio.toString() : "0").append("\",\n"); + json.append(" \"平台分账比例\": \"").append(platRatio != null ? platRatio.toString() : "0").append("\",\n"); + json.append(" \"一级代理商分账比例\": \"").append(agent1stRatio != null ? agent1stRatio.toString() : "0") + .append("\",\n"); + json.append(" \"二级代理商分账比例\": \"").append(agent2ndRatio != null ? agent2ndRatio.toString() : "0") + .append("\"\n"); + json.append(" },\n"); - // 添加金额属性 - appendProperty(json, "lklAmount", lklAmount, false); - appendProperty(json, "mchAmount", mchAmount, false); - appendProperty(json, "platAmount", platAmount, false); - appendProperty(json, "agent1stAmount", agent1stAmount, false); - appendProperty(json, "agent2ndAmount", agent2ndAmount, false); + // 分账金额结果 + json.append(" \"分账金额结果\": {\n"); + json.append(" \"拉卡拉分账金额\": ").append(lklAmount != null ? lklAmount : 0).append(",\n"); + json.append(" \"可分账金额\": ").append(canSeparateAmount != null ? canSeparateAmount : 0).append(",\n"); + json.append(" \"平台分账金额\": ").append(platAmount != null ? platAmount : 0).append(",\n"); + json.append(" \"一级代理商分账金额\": ").append(agent1stAmount != null ? agent1stAmount : 0).append(",\n"); + json.append(" \"二级代理商分账金额\": ").append(agent2ndAmount != null ? agent2ndAmount : 0).append(",\n"); + json.append(" \"商家分账金额\": ").append(mchAmount != null ? mchAmount : 0).append("\n"); + json.append(" },\n"); - // 移除最后的逗号并关闭JSON对象 - if (json.charAt(json.length() - 1) == ',') { - json.setLength(json.length() - 1); + // 分账百分比(基于分账总金额) + json.append(" \"基于分账总金额的百分比\": {\n"); + if (totalSeparateAmount != null && totalSeparateAmount > 0) { + json.append(" \"拉卡拉分账占比\": \"").append(calculatePercentage(lklAmount, totalSeparateAmount)) + .append("%\",\n"); + json.append(" \"配送费占比\": \"").append(calculatePercentage(shippingFee, totalSeparateAmount)) + .append("%\",\n"); + json.append(" \"平台分账占比\": \"").append(calculatePercentage(platAmount, totalSeparateAmount)) + .append("%\",\n"); + json.append(" \"一级代理商分账占比\": \"").append(calculatePercentage(agent1stAmount, totalSeparateAmount)) + .append("%\",\n"); + json.append(" \"二级代理商分账占比\": \"").append(calculatePercentage(agent2ndAmount, totalSeparateAmount)) + .append("%\",\n"); + json.append(" \"商家分账占比\": \"").append(calculatePercentage(mchAmount, totalSeparateAmount)) + .append("%\"\n"); + } else { + json.append(" \"拉卡拉分账占比\": \"0.00%\",\n"); + json.append(" \"配送费占比\": \"0.00%\",\n"); + json.append(" \"平台分账占比\": \"0.00%\",\n"); + json.append(" \"一级代理商分账占比\": \"0.00%\",\n"); + json.append(" \"二级代理商分账占比\": \"0.00%\",\n"); + json.append(" \"商家分账占比\": \"0.00%\"\n"); } + json.append(" },\n"); + + // 分账百分比(基于可分账金额) + json.append(" \"基于可分账金额的百分比\": {\n"); + if (canSeparateAmount != null && canSeparateAmount > 0) { + json.append(" \"平台分账占比\": \"").append(calculatePercentage(platAmount, canSeparateAmount)) + .append("%\",\n"); + json.append(" \"一级代理商分账占比\": \"").append(calculatePercentage(agent1stAmount, canSeparateAmount)) + .append("%\",\n"); + json.append(" \"二级代理商分账占比\": \"").append(calculatePercentage(agent2ndAmount, canSeparateAmount)) + .append("%\",\n"); + json.append(" \"商家分账占比\": \"").append(calculatePercentage(mchAmount, canSeparateAmount)).append("%\"\n"); + } else { + json.append(" \"平台分账占比\": \"0.00%\",\n"); + json.append(" \"一级代理商分账占比\": \"0.00%\",\n"); + json.append(" \"二级代理商分账占比\": \"0.00%\",\n"); + json.append(" \"商家分账占比\": \"0.00%\"\n"); + } + json.append(" }\n"); json.append("}"); return json.toString(); } - // ================================ 私有方法 ================================ + /** + * 重写toString方法,输出分账算法的关键信息 + * + * @return 分账对象的字符串表示 + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("LklSeparateDTO{\n"); + + // 基础信息 + sb.append(" 分账算法类型: 拉卡拉订单分账信息处理\n"); + sb.append(" 分账总金额: ").append(totalSeparateAmount != null ? totalSeparateAmount : 0).append("分\n"); + sb.append(" 配送费: ").append(shippingFee != null ? shippingFee : 0).append("分\n"); + + // 分账比例 + sb.append(" 分账比例: {\n"); + sb.append(" 拉卡拉分账比例: ").append(lklRatio != null ? lklRatio.toString() : "0").append("\n"); + sb.append(" 商家分账比例: ").append(mchRatio != null ? mchRatio.toString() : "0").append("\n"); + sb.append(" 平台分账比例: ").append(platRatio != null ? platRatio.toString() : "0").append("\n"); + sb.append(" 一级代理商分账比例: ").append(agent1stRatio != null ? agent1stRatio.toString() : "0").append("\n"); + sb.append(" 二级代理商分账比例: ").append(agent2ndRatio != null ? agent2ndRatio.toString() : "0").append("\n"); + sb.append(" }\n"); + + // 分账金额结果 + sb.append(" 分账金额结果: {\n"); + sb.append(" 拉卡拉分账金额: ").append(lklAmount != null ? lklAmount : 0).append("分\n"); + sb.append(" 可分账金额: ").append(canSeparateAmount != null ? canSeparateAmount : 0).append("分\n"); + sb.append(" 平台分账金额: ").append(platAmount != null ? platAmount : 0).append("分\n"); + sb.append(" 一级代理商分账金额: ").append(agent1stAmount != null ? agent1stAmount : 0).append("分\n"); + sb.append(" 二级代理商分账金额: ").append(agent2ndAmount != null ? agent2ndAmount : 0).append("分\n"); + sb.append(" 商家分账金额: ").append(mchAmount != null ? mchAmount : 0).append("分\n"); + sb.append(" }\n"); + + // 分账百分比(基于分账总金额) + sb.append(" 基于分账总金额的百分比: {\n"); + if (totalSeparateAmount != null && totalSeparateAmount > 0) { + sb.append(" 拉卡拉分账占比: ").append(calculatePercentage(lklAmount, totalSeparateAmount)).append("%\n"); + sb.append(" 配送费占比: ").append(calculatePercentage(shippingFee, totalSeparateAmount)).append("%\n"); + sb.append(" 平台分账占比: ").append(calculatePercentage(platAmount, totalSeparateAmount)).append("%\n"); + sb.append(" 一级代理商分账占比: ").append(calculatePercentage(agent1stAmount, totalSeparateAmount)).append("%\n"); + sb.append(" 二级代理商分账占比: ").append(calculatePercentage(agent2ndAmount, totalSeparateAmount)).append("%\n"); + sb.append(" 商家分账占比: ").append(calculatePercentage(mchAmount, totalSeparateAmount)).append("%\n"); + } else { + sb.append(" 拉卡拉分账占比: 0.00%\n"); + sb.append(" 配送费占比: 0.00%\n"); + sb.append(" 平台分账占比: 0.00%\n"); + sb.append(" 一级代理商分账占比: 0.00%\n"); + sb.append(" 二级代理商分账占比: 0.00%\n"); + sb.append(" 商家分账占比: 0.00%\n"); + } + sb.append(" }\n"); + + // 分账百分比(基于可分账金额) + sb.append(" 基于可分账金额的百分比: {\n"); + if (canSeparateAmount != null && canSeparateAmount > 0) { + sb.append(" 平台分账占比: ").append(calculatePercentage(platAmount, canSeparateAmount)).append("%\n"); + sb.append(" 一级代理商分账占比: ").append(calculatePercentage(agent1stAmount, canSeparateAmount)).append("%\n"); + sb.append(" 二级代理商分账占比: ").append(calculatePercentage(agent2ndAmount, canSeparateAmount)).append("%\n"); + sb.append(" 商家分账占比: ").append(calculatePercentage(mchAmount, canSeparateAmount)).append("%\n"); + } else { + sb.append(" 平台分账占比: 0.00%\n"); + sb.append(" 一级代理商分账占比: 0.00%\n"); + sb.append(" 二级代理商分账占比: 0.00%\n"); + sb.append(" 商家分账占比: 0.00%\n"); + } + sb.append(" }\n"); + sb.append("}"); + + return sb.toString(); + } /** - * 验证基于可分账金额分账的输入参数 + * 分账算法 - 基于总金额进行分账 + * + * @return SharingResult 分账结果,包含是否成功和失败原因 */ - private boolean validateInputs() { + public SharingResult sharingOnTotalAmount() { + // 执行基于总金额的分账计算 + SharingCalculationResult result = calculateBasedOnTotalAmount(); + + if (result.isSuccess()) { + // 更新实例变量 + this.lklAmount = result.getLklAmount(); + this.canSeparateAmount = result.getCanSeparateAmount(); + this.refCanSeparateAmount = result.getRefCanSeparateAmount(); + this.shippingFee = result.getShippingFee(); + this.platAmount = result.getPlatAmount(); + this.agent1stAmount = result.getAgent1stAmount(); + this.agent2ndAmount = result.getAgent2ndAmount(); + this.mchAmount = result.getMchAmount(); + return new SharingResult(true, null); + } else { + return new SharingResult(false, result.getErrorMessage()); + } + } + + /** + * 基于总金额的分账计算(独立算法) + * + * @return SharingCalculationResult 计算结果 + */ + private SharingCalculationResult calculateBasedOnTotalAmount() { + // 1. 参数校验 if (totalSeparateAmount == null || totalSeparateAmount <= 0) { - logger.info("分账计算失败:分账总金额必须大于0,当前值={}", totalSeparateAmount); - return false; + return new SharingCalculationResult(false, "分账总金额不能为空或小于等于0"); } - if (shippingFee == null || shippingFee < 0) { - logger.info("分账计算失败:配送费不能小于0,当前值={}", shippingFee); - return false; + // 2. 计算拉卡拉分账金额 + int lklAmount = 0; + if (lklRatio != null && lklRatio.compareTo(BigDecimal.ZERO) > 0) { + lklAmount = lklRatio.multiply(new BigDecimal(totalSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); } - if (lklRatio == null || lklRatio.compareTo(BigDecimal.ZERO) <= 0) { - logger.info("分账计算失败:拉卡拉分账比例必须大于0,当前值={}", lklRatio); - return false; + // 3. 处理配送费(可以为空或0,表示免配送费) + int effectiveShippingFee = (shippingFee != null && shippingFee > 0) ? shippingFee : 0; + + // 4. 检查配送费是否合法(不能大于等于可分账金额) + if (effectiveShippingFee >= totalSeparateAmount - lklAmount) { + return new SharingCalculationResult(false, "配送费不能大于等于可分账金额"); } - if (mchRatio == null || mchRatio.compareTo(BigDecimal.ZERO) <= 0) { - logger.info("分账计算失败:商户分账比例必须大于0,当前值={}", mchRatio); - return false; + // 5. 计算可分账金额(总金额 - 拉卡拉分账金额 - 配送费) + int availableAmount = totalSeparateAmount - lklAmount - effectiveShippingFee; + if (availableAmount < 0) { + return new SharingCalculationResult(false, "可分账金额不能为负数"); } - if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) { - logger.info("分账计算失败:平台分账比例必须大于0,当前值={}", platRatio); - return false; - } + int canSeparateAmount = availableAmount; + int refCanSeparateAmount = availableAmount; - return true; - } + // 6. 初始化各分账金额 + int platAmount = 0; + int agent1stAmount = 0; + int agent2ndAmount = 0; + int mchAmount = 0; - /** - * 验证基于总金额分账的输入参数 - */ - private boolean validateInputsForTotalAmount() { - // 基于总金额的分账计算参数验证与基于可分账金额的验证相同 - return validateInputs(); - } + // 7. 根据参与方动态调整分账比例 + BigDecimal effectivePlatRatio = platRatio != null ? platRatio : BigDecimal.ZERO; + BigDecimal effectiveAgent1stRatio = agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO; + BigDecimal effectiveAgent2ndRatio = agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO; - /** - * 计算拉卡拉分账金额 - */ - private void calculateLklAmount() { - BigDecimal lklAmountDecimal = new BigDecimal(totalSeparateAmount).multiply(lklRatio); - lklAmount = lklAmountDecimal.setScale(2, RoundingMode.HALF_UP).intValue(); - logger.debug("拉卡拉分账计算:总金额{} × 拉卡拉比例{} = {},四舍五入保留两位小数后={}", - totalSeparateAmount, lklRatio, lklAmountDecimal, lklAmount); - } + // 判断哪些代理商参与分账 + boolean hasAgent1st = effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0; + boolean hasAgent2nd = effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0; - /** - * 计算可分账金额 - */ - private void calculateCanSeparateAmount() { - canSeparateAmount = totalSeparateAmount - shippingFee - lklAmount; - logger.debug("可分账金额计算:总金额{} - 配送费{} - 拉卡拉分账{} = {}", - totalSeparateAmount, shippingFee, lklAmount, canSeparateAmount); - } + // 根据商家分账比例动态调整其他方比例 + if (mchRatio != null && mchRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal remainingRatio = BigDecimal.ONE.subtract(mchRatio); + BigDecimal onePercent = new BigDecimal("0.01"); - /** - * 检查refCanSeparateAmount是否有效 - * 有效条件:非空且大于0且小于totalSeparateAmount - * - * @return true表示有效,false表示无效 - */ - private boolean isRefCanSeparateAmountValid() { - return refCanSeparateAmount != null && refCanSeparateAmount > 0 && refCanSeparateAmount < totalSeparateAmount; - } - - /** - * 获取分账基数金额 - * 如果refCanSeparateAmount有效,则使用refCanSeparateAmount,否则使用canSeparateAmount - * - * @return 分账基数金额 - */ - private int getSeparateBaseAmount() { - if (isRefCanSeparateAmountValid()) { - return refCanSeparateAmount; - } - return canSeparateAmount; - } - - /** - * 检查并应用参考可分账金额 - * 当refCanSeparateAmount有效且与计算出的canSeparateAmount不一致时, - * 将差额分配给指定优先级的分账对象(平台 > 一级代理商 > 二级代理商 > 商户) - */ - private void checkAndApplyRefCanSeparateAmount() { - // 判断refCanSeparateAmount是否有效(非空且大于0且小于totalSeparateAmount) - if (isRefCanSeparateAmountValid()) { - // 比较refCanSeparateAmount与计算出的canSeparateAmount - if (!refCanSeparateAmount.equals(canSeparateAmount)) { - int difference = refCanSeparateAmount - canSeparateAmount; - logger.info("检测到refCanSeparateAmount与计算出的canSeparateAmount不一致,差额={}分。" + - "refCanSeparateAmount={}分,计算出的canSeparateAmount={}分。", - difference, refCanSeparateAmount, canSeparateAmount); - - // 将差额分配给指定优先级的分账对象(商户 > 二级代理商 > 一级代理商 > 平台) - if (difference != 0) { - if (mchAmount == null) mchAmount = 0; - if (agent2ndAmount == null) agent2ndAmount = 0; - if (agent1stAmount == null) agent1stAmount = 0; - if (platAmount == null) platAmount = 0; - - if (difference > 0) { - // 如果差额为正,优先增加高优先级参与方的金额 - mchAmount += difference; - } else { - // 如果差额为负,优先从低优先级参与方扣除 - int diff = -difference; - if (mchAmount >= diff) { - mchAmount -= diff; - } else if (agent2ndAmount > 0 && agent2ndAmount >= diff) { - agent2ndAmount -= diff; - } else if (agent1stAmount > 0 && agent1stAmount >= diff) { - agent1stAmount -= diff; - } else { - platAmount -= diff; - } - } - logger.debug("已将差额{}分应用于分账金额调整", difference); + if (hasAgent1st && hasAgent2nd) { + // 平台、一级代理商、二级代理商都参与 + // 平台固定1%,一级代理商固定1%,二级代理商为剩余比例 + effectivePlatRatio = onePercent; + effectiveAgent1stRatio = onePercent; + effectiveAgent2ndRatio = remainingRatio.subtract(onePercent).subtract(onePercent); + } else if (hasAgent1st || hasAgent2nd) { + // 只有平台和一个代理商参与 + effectivePlatRatio = onePercent; + // 代理商为剩余比例 + if (hasAgent1st) { + effectiveAgent1stRatio = remainingRatio.subtract(onePercent); + } else { + effectiveAgent2ndRatio = remainingRatio.subtract(onePercent); } } else { - logger.debug("refCanSeparateAmount与计算出的canSeparateAmount一致,值={}分", canSeparateAmount); - } - } else { - logger.debug("refCanSeparateAmount无效或未设置,使用计算出的canSeparateAmount={}分作为分账基数", canSeparateAmount); - } - } - - /** - * 调整分账比例,确保有效值比例相加等于1.0 - */ - private void adjustRatios() { - // 计算有效的分账比例总和 - BigDecimal effectiveRatioSum = BigDecimal.ZERO - .add(mchRatio) - .add(platRatio) - .add(agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO) - .add(agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO); - - logger.debug("调整前各分账比例:商户={},平台={},一级代理商={},二级代理商={},有效比例总和={}", - mchRatio, platRatio, agent1stRatio, agent2ndRatio, effectiveRatioSum); - - // 动态调整最后一个分账方的比例,确保总和为1.0 - BigDecimal adjustmentRatio = BigDecimal.ONE.subtract(effectiveRatioSum); - if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { - // 调整二级代理商比例 - agent2ndRatio = agent2ndRatio.add(adjustmentRatio); - } else if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { - // 调整一级代理商比例 - agent1stRatio = agent1stRatio.add(adjustmentRatio); - } else { - // 调整平台比例 - platRatio = platRatio.add(adjustmentRatio); - } - - logger.debug("调整后各分账比例:商户={},平台={},一级代理商={},二级代理商={}", - mchRatio, platRatio, agent1stRatio, agent2ndRatio); - } - - /** - * 按优先级进行分账(基于可分账金额) - */ - private void performSeparate() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - // 商户分账金额 = 可分账金额 × 商户分账比例(四舍五入) - BigDecimal mchAmountDecimal = new BigDecimal(separateBaseAmount).multiply(mchRatio); - mchAmount = mchAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("商户分账计算:可分账金额{} × 商户比例{} = {},四舍五入后={}", separateBaseAmount, mchRatio, mchAmountDecimal, mchAmount); - - // 平台分账金额 = 可分账金额 × 平台分账比例(四舍五入) - BigDecimal platAmountDecimal = new BigDecimal(separateBaseAmount).multiply(platRatio); - platAmount = platAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("平台分账计算:可分账金额{} × 平台比例{} = {},四舍五入后={}", separateBaseAmount, platRatio, platAmountDecimal, platAmount); - - // 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(四舍五入) - if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { - BigDecimal agent1stAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent1stRatio); - agent1stAmount = agent1stAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("一级代理商分账计算:可分账金额{} × 一级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent1stRatio, agent1stAmountDecimal, agent1stAmount); - } else { - agent1stAmount = 0; - } - - // 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(四舍五入) - if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { - BigDecimal agent2ndAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent2ndRatio); - agent2ndAmount = agent2ndAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("二级代理商分账计算:可分账金额{} × 二级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent2ndRatio, agent2ndAmountDecimal, agent2ndAmount); - } else { - agent2ndAmount = 0; - } - } - - /** - * 按优先级进行分账(基于总金额) - */ - private void performSeparateBasedOnTotalAmount() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - // 平台分账金额 = 可分账金额 × 平台分账比例(四舍五入) - BigDecimal platAmountDecimal = new BigDecimal(separateBaseAmount).multiply(platRatio); - platAmount = platAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("平台分账计算:可分账金额{} × 平台比例{} = {},四舍五入后={}", separateBaseAmount, platRatio, platAmountDecimal, platAmount); - - // 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(四舍五入) - if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { - BigDecimal agent1stAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent1stRatio); - agent1stAmount = agent1stAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("一级代理商分账计算:可分账金额{} × 一级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent1stRatio, agent1stAmountDecimal, agent1stAmount); - } else { - agent1stAmount = 0; - } - - // 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(四舍五入) - if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { - BigDecimal agent2ndAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent2ndRatio); - agent2ndAmount = agent2ndAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); - logger.debug("二级代理商分账计算:可分账金额{} × 二级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent2ndRatio, agent2ndAmountDecimal, agent2ndAmount); - } else { - agent2ndAmount = 0; - } - - // 商户分账金额 = 可分账金额 - 平台分账金额 - 一级代理商分账金额 - 二级代理商分账金额 - mchAmount = separateBaseAmount - platAmount - agent1stAmount - agent2ndAmount; - logger.debug("商户分账计算:可分账金额{} - 平台分账{} - 一级代理商分账{} - 二级代理商分账{} = {}", - separateBaseAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); - } - - /** - * 确保总额平衡 - */ - private void balanceAmounts() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - int totalAmount = mchAmount + platAmount + agent1stAmount + agent2ndAmount; - if (totalAmount != separateBaseAmount) { - int diff = separateBaseAmount - totalAmount; - logger.debug("分账总额与可分账金额不符,差额={},开始调整", diff); - - // 将差额分配给优先级最低的参与方,确保总额平衡 - if (mchAmount + diff >= 0) { - mchAmount += diff; - logger.debug("调整商户分账金额:{} + {} = {}", mchAmount - diff, diff, mchAmount); - } else if (agent2ndAmount > 0 && agent2ndAmount + diff >= 0) { - agent2ndAmount += diff; - logger.debug("调整二级代理商分账金额:{} + {} = {}", agent2ndAmount - diff, diff, agent2ndAmount); - } else if (agent1stAmount > 0 && agent1stAmount + diff >= 0) { - agent1stAmount += diff; - logger.debug("调整一级代理商分账金额:{} + {} = {}", agent1stAmount - diff, diff, agent1stAmount); - } else if (platAmount + diff >= 0) { - platAmount += diff; - logger.debug("调整平台分账金额:{} + {} = {}", platAmount - diff, diff, platAmount); + // 只有平台参与 + effectivePlatRatio = remainingRatio; } } - } - /** - * 保障各参与方分账比例不低于指定值(基于总金额的分账计算专用) - * 忽略商户比例保障,只保障平台、一级代理商、二级代理商的比例 - */ - private void guaranteeRatiosForTotalAmount() { - // 平台优先级最高 - guaranteePlatformRatio(); - - // 一级代理商优先级次之 - guaranteeAgent1stRatio(); - - // 不保障商户比例,商户只分到剩余余额 - } - - /** - * 保障各参与方分账比例不低于指定值 - */ - private void guaranteeRatios() { - // 商户优先级最高 - guaranteeMerchantRatio(); - - // 平台优先级次之 - guaranteePlatformRatio(); - - // 一级代理商优先级再次之 - guaranteeAgent1stRatio(); - - // 最终验证和调整,确保所有参与方分账比例不低于指定值 - finalGuaranteeMerchantRatio(); - } - - /** - * 保障商户分账比例 - */ - private void guaranteeMerchantRatio() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - BigDecimal actualMchRatio = new BigDecimal(mchAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); - if (actualMchRatio.compareTo(mchRatio) < 0) { - int requiredMchAmount = mchRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.HALF_UP).intValue(); - int diff = requiredMchAmount - mchAmount; - if (diff > 0) { - logger.debug("商户分账比例不足,需要调整金额={},商户当前金额={},应得金额={}", diff, mchAmount, requiredMchAmount); - - if (agent2ndAmount > 0 && agent2ndAmount >= diff) { - agent2ndAmount -= diff; - mchAmount += diff; - logger.debug("从二级代理商调整{}给商户,调整后:商户={},二级代理商={}", diff, mchAmount, agent2ndAmount); - } else if (agent1stAmount > 0 && agent1stAmount >= diff) { - agent1stAmount -= diff; - mchAmount += diff; - logger.debug("从一级代理商调整{}给商户,调整后:商户={},一级代理商={}", diff, mchAmount, agent1stAmount); - } else if (platAmount >= diff) { - platAmount -= diff; - mchAmount += diff; - logger.debug("从平台调整{}给商户,调整后:商户={},平台={}", diff, mchAmount, platAmount); - } + // 8. 按优先级顺序计算各分账方金额 + // 平台分账(优先级1)- 基于总金额计算 + if (effectivePlatRatio.compareTo(BigDecimal.ZERO) > 0) { + platAmount = effectivePlatRatio.multiply(new BigDecimal(totalSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + // 确保平台分账金额不超过可分账金额 + if (platAmount > availableAmount) { + platAmount = availableAmount; } + availableAmount -= platAmount; } - } - /** - * 保障平台分账比例 - */ - private void guaranteePlatformRatio() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - BigDecimal actualPlatRatio = new BigDecimal(platAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); - if (actualPlatRatio.compareTo(platRatio) < 0) { - int requiredPlatAmount = platRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.HALF_UP).intValue(); - int diff = requiredPlatAmount - platAmount; - if (diff > 0) { - logger.debug("平台分账比例不足,需要调整金额={},平台当前金额={},应得金额={}", diff, platAmount, requiredPlatAmount); - - if (agent2ndAmount > 0 && agent2ndAmount >= diff) { - agent2ndAmount -= diff; - platAmount += diff; - logger.debug("从二级代理商调整{}给平台,调整后:平台={},二级代理商={}", diff, platAmount, agent2ndAmount); - } else if (agent1stAmount > 0 && agent1stAmount >= diff) { - agent1stAmount -= diff; - platAmount += diff; - logger.debug("从一级代理商调整{}给平台,调整后:平台={},一级代理商={}", diff, platAmount, agent1stAmount); - } + // 一级代理商分账(优先级2)- 基于总金额计算 + if (hasAgent1st && effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + agent1stAmount = effectiveAgent1stRatio.multiply(new BigDecimal(totalSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + // 确保不超过剩余金额 + if (agent1stAmount > availableAmount) { + agent1stAmount = availableAmount; } + availableAmount -= agent1stAmount; } - } - /** - * 保障一级代理商分账比例 - */ - private void guaranteeAgent1stRatio() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { - BigDecimal actualAgent1stRatio = new BigDecimal(agent1stAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); - if (actualAgent1stRatio.compareTo(agent1stRatio) < 0) { - int requiredAgent1stAmount = agent1stRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.HALF_UP).intValue(); - int diff = requiredAgent1stAmount - agent1stAmount; - if (diff > 0) { - logger.debug("一级代理商分账比例不足,需要调整金额={},一级代理商当前金额={},应得金额={}", diff, agent1stAmount, requiredAgent1stAmount); - - if (agent2ndAmount > 0 && agent2ndAmount >= diff) { - agent2ndAmount -= diff; - agent1stAmount += diff; - logger.debug("从二级代理商调整{}给一级代理商,调整后:一级代理商={},二级代理商={}", diff, agent1stAmount, agent2ndAmount); - } - } + // 二级代理商分账(优先级3)- 基于总金额计算 + if (hasAgent2nd && effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { + agent2ndAmount = effectiveAgent2ndRatio.multiply(new BigDecimal(totalSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + // 确保不超过剩余金额 + if (agent2ndAmount > availableAmount) { + agent2ndAmount = availableAmount; } + availableAmount -= agent2ndAmount; } + + // 9. 商家分账(优先级4,获得剩余所有金额) + mchAmount = availableAmount; + + // 10. 检查商家分账金额是否合法 + if (mchAmount < 0) { + return new SharingCalculationResult(false, "商家分账金额不能为负数"); + } + + // 创建并返回计算结果 + SharingCalculationResult calculationResult = new SharingCalculationResult(true, null); + calculationResult.setLklAmount(lklAmount); + calculationResult.setCanSeparateAmount(canSeparateAmount); + calculationResult.setRefCanSeparateAmount(refCanSeparateAmount); + calculationResult.setShippingFee(effectiveShippingFee); + calculationResult.setPlatAmount(platAmount); + calculationResult.setAgent1stAmount(agent1stAmount); + calculationResult.setAgent2ndAmount(agent2ndAmount); + calculationResult.setMchAmount(mchAmount); + + return calculationResult; } /** - * 最终保障商户分账比例 - */ - private void finalGuaranteeMerchantRatio() { - // 确定分账基数 - int separateBaseAmount = getSeparateBaseAmount(); - - BigDecimal finalMchRatio = new BigDecimal(mchAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); - if (finalMchRatio.compareTo(mchRatio) < 0) { - int requiredMchAmount = mchRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.UP).intValue(); - int diff = requiredMchAmount - mchAmount; - if (diff > 0) { - logger.debug("商户最终分账比例仍不足,需要调整金额={},商户当前金额={},应得金额={}", diff, mchAmount, requiredMchAmount); - - if (agent2ndAmount > 0 && agent2ndAmount >= diff) { - agent2ndAmount -= diff; - mchAmount += diff; - logger.debug("最终调整:从二级代理商调整{}给商户,调整后:商户={},二级代理商={}", diff, mchAmount, agent2ndAmount); - } else { - int remainingDiff = diff - agent2ndAmount; - if (agent1stAmount > 0 && agent1stAmount >= remainingDiff) { - agent1stAmount -= remainingDiff; - mchAmount += diff; - logger.debug("最终调整:从一级代理商调整{}给商户,调整后:商户={},一级代理商={}", remainingDiff, mchAmount, agent1stAmount); - } else { - int remainingDiff2 = remainingDiff - agent1stAmount; - if (platAmount >= remainingDiff2) { - platAmount -= remainingDiff2; - mchAmount += diff; - logger.debug("最终调整:从平台调整{}给商户,调整后:商户={},平台={}", remainingDiff2, mchAmount, platAmount); - } - } - } - } - } - } - - /** - * 向JSON字符串中添加属性 + * 分账算法 - 基于可分账金额进行分账 * - * @param json StringBuilder对象 - * @param key 属性名 - * @param value 属性值 - * @param first 是否为第一个属性 + * @return SharingResult 分账结果,包含是否成功和失败原因 */ - private void appendProperty(StringBuilder json, String key, Object value, boolean first) { - if (!first) { - json.append(","); - } - json.append("\"").append(key).append("\":"); - if (value == null) { - json.append("null"); - } else if (value instanceof String) { - json.append("\"").append(value).append("\""); + public SharingResult sharingOnCanSeparateAmount() { + // 执行基于可分账金额的分账计算 + SharingCalculationResult result = calculateBasedOnCanSeparateAmount(); + + if (result.isSuccess()) { + // 更新实例变量 + this.lklAmount = result.getLklAmount(); + this.canSeparateAmount = result.getCanSeparateAmount(); + this.refCanSeparateAmount = result.getRefCanSeparateAmount(); + this.shippingFee = result.getShippingFee(); + this.platAmount = result.getPlatAmount(); + this.agent1stAmount = result.getAgent1stAmount(); + this.agent2ndAmount = result.getAgent2ndAmount(); + this.mchAmount = result.getMchAmount(); + return new SharingResult(true, null); } else { - json.append(value); + return new SharingResult(false, result.getErrorMessage()); } } -} \ No newline at end of file + + /** + * 基于可分账金额的分账计算(独立算法) + * + * @return SharingCalculationResult 计算结果 + */ + private SharingCalculationResult calculateBasedOnCanSeparateAmount() { + // 1. 参数校验 + if (totalSeparateAmount == null || totalSeparateAmount <= 0) { + return new SharingCalculationResult(false, "分账总金额不能为空或小于等于0"); + } + + // 2. 先扣除配送费(可以为空或0,表示免配送费) + int effectiveShippingFee = (shippingFee != null && shippingFee > 0) ? shippingFee : 0; + if (effectiveShippingFee >= totalSeparateAmount) { + return new SharingCalculationResult(false, "配送费不能大于等于分账总金额"); + } + + // 3. 计算扣除配送费后的金额 + int amountAfterShipping = totalSeparateAmount - effectiveShippingFee; + + // 4. 计算拉卡拉分账金额(手续费)= 分账总金额 * 拉卡拉分账比例 + int lklAmount = 0; + if (lklRatio != null && lklRatio.compareTo(BigDecimal.ZERO) > 0) { + lklAmount = lklRatio.multiply(new BigDecimal(totalSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + } + + // 5. 检查拉卡拉分账金额是否合法 + if (lklAmount >= amountAfterShipping) { + return new SharingCalculationResult(false, "拉卡拉分账金额不能大于等于扣除配送费后的金额"); + } + + // 6. 计算可分账金额 = 分账总金额 - 配送费 - 拉卡拉分账金额 + int availableAmount = amountAfterShipping - lklAmount; + if (availableAmount < 0) { + return new SharingCalculationResult(false, "可分账金额不能为负数"); + } + + int canSeparateAmount = availableAmount; + int refCanSeparateAmount = availableAmount; + + // 7. 初始化各分账金额 + int platAmount = 0; + int agent1stAmount = 0; + int agent2ndAmount = 0; + int mchAmount = 0; + + // 8. 根据参与方动态调整分账比例 + BigDecimal effectivePlatRatio = platRatio != null ? platRatio : BigDecimal.ZERO; + BigDecimal effectiveAgent1stRatio = agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO; + BigDecimal effectiveAgent2ndRatio = agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO; + + // 判断哪些代理商参与分账 + boolean hasAgent1st = effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0; + boolean hasAgent2nd = effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0; + + // 根据商家分账比例动态调整其他方比例 + if (mchRatio != null && mchRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal remainingRatio = BigDecimal.ONE.subtract(mchRatio); + BigDecimal onePercent = new BigDecimal("0.01"); + + if (hasAgent1st && hasAgent2nd) { + // 平台、一级代理商、二级代理商都参与 + // 平台固定1%,一级代理商固定1%,二级代理商为剩余比例 + effectivePlatRatio = onePercent; + effectiveAgent1stRatio = onePercent; + effectiveAgent2ndRatio = remainingRatio.subtract(onePercent).subtract(onePercent); + } else if (hasAgent1st || hasAgent2nd) { + // 只有平台和一个代理商参与 + effectivePlatRatio = onePercent; + // 代理商为剩余比例 + if (hasAgent1st) { + effectiveAgent1stRatio = remainingRatio.subtract(onePercent); + } else { + effectiveAgent2ndRatio = remainingRatio.subtract(onePercent); + } + } else { + // 只有平台参与 + effectivePlatRatio = remainingRatio; + } + } + + // 9. 按新的优先级顺序计算各分账方金额(基于可分账金额计算) + // 商家分账(优先级1)- 基于可分账金额计算,采用向上进位方式 + if (mchRatio != null && mchRatio.compareTo(BigDecimal.ZERO) > 0) { + mchAmount = mchRatio.multiply(new BigDecimal(canSeparateAmount)) + .setScale(0, RoundingMode.UP).intValue(); + // 确保商家分账金额不超过可分账金额 + if (mchAmount > availableAmount) { + mchAmount = availableAmount; + } + availableAmount -= mchAmount; + } + + // 平台分账(优先级2)- 基于可分账金额计算,采用四舍五入方式 + if (effectivePlatRatio.compareTo(BigDecimal.ZERO) > 0) { + platAmount = effectivePlatRatio.multiply(new BigDecimal(canSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + // 确保平台分账金额不超过剩余金额 + if (platAmount > availableAmount) { + platAmount = availableAmount; + } + availableAmount -= platAmount; + } + + // 一级代理商分账(优先级3)- 基于可分账金额计算,采用四舍五入方式 + if (hasAgent1st && effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + agent1stAmount = effectiveAgent1stRatio.multiply(new BigDecimal(canSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + // 确保不超过剩余金额 + if (agent1stAmount > availableAmount) { + agent1stAmount = availableAmount; + } + availableAmount -= agent1stAmount; + } + + // 二级代理商分账(优先级4)- 基于可分账金额计算,采用四舍五入方式 + if (hasAgent2nd && effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { + agent2ndAmount = effectiveAgent2ndRatio.multiply(new BigDecimal(canSeparateAmount)) + .setScale(0, RoundingMode.HALF_UP).intValue(); + // 确保不超过剩余金额 + if (agent2ndAmount > availableAmount) { + agent2ndAmount = availableAmount; + } + availableAmount -= agent2ndAmount; + } + + // 10. 如果还有剩余金额,将其分配给商家 + if (availableAmount > 0) { + mchAmount += availableAmount; + } + + // 11. 检查商家分账金额是否合法 + if (mchAmount < 0) { + return new SharingCalculationResult(false, "商家分账金额不能为负数"); + } + + // 创建并返回计算结果 + SharingCalculationResult calculationResult = new SharingCalculationResult(true, null); + calculationResult.setLklAmount(lklAmount); + calculationResult.setCanSeparateAmount(canSeparateAmount); + calculationResult.setRefCanSeparateAmount(refCanSeparateAmount); + calculationResult.setShippingFee(effectiveShippingFee); + calculationResult.setPlatAmount(platAmount); + calculationResult.setAgent1stAmount(agent1stAmount); + calculationResult.setAgent2ndAmount(agent2ndAmount); + calculationResult.setMchAmount(mchAmount); + + return calculationResult; + } + + /** + * 分账结果类,包含分账是否成功以及失败原因 + */ + @Data + @EqualsAndHashCode(callSuper = false) + public static class SharingResult { + private final boolean success; + private final String errorMessage; + + public SharingResult(boolean success, String errorMessage) { + this.success = success; + this.errorMessage = errorMessage; + } + + public boolean isSuccess() { + return success; + } + + @Override + public String toString() { + return success ? "分账成功" : "分账失败: " + errorMessage; + } + } + + /** + * 分账计算结果类,包含分账计算的详细结果 + */ + public static class SharingCalculationResult extends SharingResult { + // 分账金额结果属性 + private Integer lklAmount; // 拉卡拉分账金额(分) + private Integer canSeparateAmount; // 可分账金额(分) + private Integer refCanSeparateAmount; // 拉卡拉参考可分账金额(分) + private Integer shippingFee; // 配送费(分) + private Integer platAmount; // 平台分账金额(分) + private Integer agent1stAmount; // 一级代理商分账金额(分) + private Integer agent2ndAmount; // 二级代理商分账金额(分) + private Integer mchAmount; // 商户分账金额(分) + + public SharingCalculationResult(boolean success, String errorMessage) { + super(success, errorMessage); + } + + public Integer getMchAmount() { + return mchAmount; + } + + public void setMchAmount(Integer mchAmount) { + this.mchAmount = mchAmount; + } + + public Integer getAgent2ndAmount() { + return agent2ndAmount; + } + + public void setAgent2ndAmount(Integer agent2ndAmount) { + this.agent2ndAmount = agent2ndAmount; + } + + public Integer getAgent1stAmount() { + return agent1stAmount; + } + + public void setAgent1stAmount(Integer agent1stAmount) { + this.agent1stAmount = agent1stAmount; + } + + public Integer getPlatAmount() { + return platAmount; + } + + public void setPlatAmount(Integer platAmount) { + this.platAmount = platAmount; + } + + public Integer getShippingFee() { + return shippingFee; + } + + public void setShippingFee(Integer shippingFee) { + this.shippingFee = shippingFee; + } + + public Integer getRefCanSeparateAmount() { + return refCanSeparateAmount; + } + + public void setRefCanSeparateAmount(Integer refCanSeparateAmount) { + this.refCanSeparateAmount = refCanSeparateAmount; + } + + public Integer getCanSeparateAmount() { + return canSeparateAmount; + } + + public void setCanSeparateAmount(Integer canSeparateAmount) { + this.canSeparateAmount = canSeparateAmount; + } + + public Integer getLklAmount() { + return lklAmount; + } + + public void setLklAmount(Integer lklAmount) { + this.lklAmount = lklAmount; + } + } +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTOBak.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTOBak.java new file mode 100644 index 00000000..1038f51b --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTOBak.java @@ -0,0 +1,849 @@ +package com.suisung.mall.common.pojo.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.util.Pair; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 拉卡拉订单分账信息对象 + *

+ * 分账计算支持两种模式: + * 1. 基于可分账金额模式(默认):先扣除拉卡拉手续费和配送费,再对剩余金额进行分账 + * 2. 基于总金额模式:基于总分账金额进行分账计算 + *

+ * 分账优先级顺序:拉卡拉 > 平台 > 一级代理商 > 二级代理商 > 商户 + * 商户作为最后分账方,其金额为剩余金额,不强制遵循分账比例 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@ApiModel(value = "拉卡拉订单分账信息对象", description = "拉卡拉订单分账信息对象") +public class LklSeparateDTOBak implements java.io.Serializable { + private static final Logger logger = LoggerFactory.getLogger(LklSeparateDTOBak.class); + + private static final long serialVersionUID = 1L; + + // ================================ 基础金额属性 ================================ + + @ApiModelProperty(value = "分账总金额(分)") + private Integer totalSeparateAmount; + + @ApiModelProperty(value = "可分账金额(分)") + private Integer canSeparateAmount; + + @ApiModelProperty(value = "拉卡拉参考可分账金额(分)") + private Integer refCanSeparateAmount; + + @ApiModelProperty(value = "配送费(分)") + private Integer shippingFee; + + // ================================ 分账比例属性 ================================ + + @ApiModelProperty(value = "拉卡拉分账比例(如 0.0025=0.25%)") + private BigDecimal lklRatio; + + @ApiModelProperty(value = "商户分账比例(如 0.96=96%)") + private BigDecimal mchRatio; + + @ApiModelProperty(value = "平台分账比例(如 0.01=1%)") + private BigDecimal platRatio; + + @ApiModelProperty(value = "一级代理商分账比例(如 0.01=1%)") + private BigDecimal agent1stRatio; + + @ApiModelProperty(value = "二级代理商分账比例(如 0.03=3%)") + private BigDecimal agent2ndRatio; + + // ================================ 分账金额结果属性 ================================ + + @ApiModelProperty(value = "拉卡拉分账金额(分)") + private Integer lklAmount; + + @ApiModelProperty(value = "商户分账金额(分)") + private Integer mchAmount; + + @ApiModelProperty(value = "平台分账金额(分)") + private Integer platAmount; + + @ApiModelProperty(value = "一级代理商分账金额(分)") + private Integer agent1stAmount; + + @ApiModelProperty(value = "二级代理商分账金额(分)") + private Integer agent2ndAmount; + + // ================================ 内部状态属性 ================================ + + /** + * 分账计算方法类型 + * true表示基于总金额分账(calcOnTotalAmount方法被调用) + * false表示基于可分账金额分账(calcOnCanAmount方法被调用) + */ + private boolean isBasedOnTotalAmount = false; + + // ================================ 构造方法 ================================ + + public LklSeparateDTOBak() { + // 默认构造函数 + } + + // ================================ 公共方法 ================================ + + /** + * 测试方法 + */ + public static void main(String[] args) { + logger.info("开始测试分账计算功能..."); + + // 临界点测试用例2: 小金额测试 + logger.info("\n临界点测试用例2 - 小金额测试:"); + LklSeparateDTOBak dto2 = new LklSeparateDTOBak(); + dto2.setTotalSeparateAmount(800); + dto2.setShippingFee(600); + dto2.setLklRatio(new BigDecimal("0.0025")); + dto2.setMchRatio(new BigDecimal("0.96")); + dto2.setPlatRatio(new BigDecimal("0.04")); + dto2.setBasedOnTotalAmount(true); + +// dto2.setAgent1stRatio(new BigDecimal("0.01")); +// dto2.setAgent2ndRatio(new BigDecimal("0.04")); +// dto2.setRefCanSeparateAmount(8079); // 设置参考可分账金额 + + logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}", + dto2.getTotalSeparateAmount(), dto2.getShippingFee(), dto2.getLklRatio(), + dto2.getMchRatio(), dto2.getPlatRatio()); + + Pair result2 = dto2.calcOnCanSeparateAmount(); + logger.info("分账计算结果: {}", (result2.getFirst() ? "成功" : "失败")); + if (result2.getFirst()) { + logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 参考可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", + dto2.getTotalSeparateAmount(), dto2.getLklAmount(), dto2.getShippingFee(), dto2.getCanSeparateAmount(), dto2.getRefCanSeparateAmount(), + dto2.getMchAmount(), dto2.getPlatAmount(), dto2.getAgent1stAmount(), dto2.getAgent2ndAmount()); + + // 计算并打印各分账方占可分账金额和总金额的百分比 + int canSeparateAmount = dto2.getCanSeparateAmount(); + int totalSeparateAmount = dto2.getTotalSeparateAmount(); + + if (canSeparateAmount > 0) { + double lklRatioOfTotal = (double) dto2.getLklAmount() / totalSeparateAmount * 100; + double mchRatioOfTotal = (double) dto2.getMchAmount() / totalSeparateAmount * 100; + double platRatioOfTotal = (double) dto2.getPlatAmount() / totalSeparateAmount * 100; + double agent1stRatioOfTotal = 0.0; + double agent2ndRatioOfTotal = 0.0; + + // 安全检查,避免空指针异常 + if (dto2.getAgent1stAmount() != null) { + agent1stRatioOfTotal = (double) dto2.getAgent1stAmount() / totalSeparateAmount * 100; + } + if (dto2.getAgent2ndAmount() != null) { + agent2ndRatioOfTotal = (double) dto2.getAgent2ndAmount() / totalSeparateAmount * 100; + } + + double lklRatioOfAvailable = (double) dto2.getLklAmount() / canSeparateAmount * 100; + double mchRatioOfAvailable = (double) dto2.getMchAmount() / canSeparateAmount * 100; + double platRatioOfAvailable = (double) dto2.getPlatAmount() / canSeparateAmount * 100; + double agent1stRatioOfAvailable = 0.0; + double agent2ndRatioOfAvailable = 0.0; + + // 安全检查,避免空指针异常 + if (dto2.getAgent1stAmount() != null) { + agent1stRatioOfAvailable = (double) dto2.getAgent1stAmount() / canSeparateAmount * 100; + } + if (dto2.getAgent2ndAmount() != null) { + agent2ndRatioOfAvailable = (double) dto2.getAgent2ndAmount() / canSeparateAmount * 100; + } + + logger.info("占比详情 (占总金额/占可分账金额): 拉卡拉={}%/{}%, 商户={}%/{}%, 平台={}%/{}%, 一级代理商={}%/{}%, 二级代理商={}%/{}%", + String.format("%.2f", lklRatioOfTotal), String.format("%.2f", lklRatioOfAvailable), + String.format("%.2f", mchRatioOfTotal), String.format("%.2f", mchRatioOfAvailable), + String.format("%.2f", platRatioOfTotal), String.format("%.2f", platRatioOfAvailable), + String.format("%.2f", agent1stRatioOfTotal), String.format("%.2f", agent1stRatioOfAvailable), + String.format("%.2f", agent2ndRatioOfTotal), String.format("%.2f", agent2ndRatioOfAvailable)); + } + + int total = dto2.getLklAmount() + dto2.getMchAmount() + dto2.getPlatAmount() + + (dto2.getAgent1stAmount() != null ? dto2.getAgent1stAmount() : 0) + + (dto2.getAgent2ndAmount() != null ? dto2.getAgent2ndAmount() : 0) + dto2.getShippingFee(); + logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto2.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto2.getTotalSeparateAmount()); + } + } + + /** + * 基于可分账金额的分账计算方法(默认) + *

+ * 计算逻辑: + * 1. 计算拉卡拉分账金额 = 总分账金额 × 拉卡拉分账比例(向上取整) + * 2. 计算可分账金额 = 总分账金额 - 配送费 - 拉卡拉分账金额 + * 3. 如果refCanSeparateAmount有效且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账基数 + * 4. 调整分账比例,确保有效比例之和等于1.0 + * 5. 按优先级计算各参与方分账金额(商户 > 平台 > 一级代理商 > 二级代理商) + * 6. 确保总额平衡并保障各参与方分账比例 + * + * @return Pair 分账结果是否成功和失败的原因 + */ + public Pair calcOnCanSeparateAmount() { + // 设置分账计算方法类型 + isBasedOnTotalAmount = false; + + // 检查前提条件 + if (!validateInputs()) { + return Pair.of(false, "分账参数不全"); + } + + logger.info("开始基于可分账金额的分账计算,总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); + + // 计算拉卡拉分账金额 + calculateLklAmount(); + + // 计算可分账金额 + calculateCanSeparateAmount(); + if (canSeparateAmount <= 0) { + String errorMsg = "分账计算失败:可分账金额必须大于0,当前值=" + canSeparateAmount; + logger.info(errorMsg); + return Pair.of(false, errorMsg); + } + + // 检查并应用参考可分账金额 + checkAndApplyRefCanSeparateAmount(); + + // 调整分账比例 + adjustRatios(); + + // 按优先级分账 + performSeparate(); + + // 确保总额平衡 + balanceAmounts(); + + // 保障各参与方分账比例 + guaranteeRatios(); + + String successMsg = String.format("基于可分账金额的分账计算完成:拉卡拉=%d分, 平台=%d分, 一级代理商=%d分, 二级代理商=%d分, 商户=%d分", + lklAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); + logger.info(successMsg); + return Pair.of(true, successMsg); + } + + /** + * 基于总金额的分账计算方法 + *

+ * 计算逻辑: + * 1. 计算拉卡拉分账金额 = 总分账金额 × 拉卡拉分账比例(四舍五入保留两位小数) + * 2. 计算可分账金额 = 总分账金额 - 配送费 - 拉卡拉分账金额 + * 3. 如果refCanSeparateAmount有效且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账基数 + * 4. 调整分账比例,确保有效比例之和等于1.0 + * 5. 按优先级计算各参与方分账金额(平台 > 一级代理商 > 二级代理商 > 商户),基于可分账金额进行分账 + * 6. 确保总额平衡并保障各参与方分账比例 + * + * @return Pair 分账结果是否成功和失败的原因 + */ + public Pair calcOnTotalAmount() { + // 设置分账计算方法类型 + isBasedOnTotalAmount = true; + + // 检查前提条件 + if (!validateInputsForTotalAmount()) { + return Pair.of(false, "分账参数不全"); + } + + logger.info("开始基于总金额的分账计算,总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); + + // 计算拉卡拉分账金额 + calculateLklAmount(); + + // 计算可分账金额 + calculateCanSeparateAmount(); + if (canSeparateAmount <= 0) { + String errorMsg = "基于总金额的分账计算失败:可分账金额必须大于0,当前值=" + canSeparateAmount; + logger.info(errorMsg); + return Pair.of(false, errorMsg); + } + + // 调整分账比例 + adjustRatios(); + + // 检查并应用参考可分账金额 + checkAndApplyRefCanSeparateAmount(); + + // 按优先级分账(基于总金额) + performSeparateBasedOnTotalAmount(); + + // 确保总额平衡 + balanceAmounts(); + + // 保障各参与方分账比例(忽略商户比例保障) + guaranteeRatiosForTotalAmount(); + + String successMsg = String.format("基于总金额的分账计算完成:拉卡拉=%d分, 平台=%d分, 一级代理商=%d分, 二级代理商=%d分, 商户=%d分", + lklAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); + logger.info(successMsg); + return Pair.of(true, successMsg); + } + + /** + * 输出具体的分账结果 + * + * @return 分账结果字符串 + */ + @Override + public String toString() { + int canSeparateAmount = getCanSeparateAmount(); + int totalSeparateAmount = getTotalSeparateAmount(); + + // 计算各分账方占总金额和可分账金额的百分比 + double lklRatioOfTotal = 0.0; + double mchRatioOfTotal = 0.0; + double platRatioOfTotal = 0.0; + double agent1stRatioOfTotal = 0.0; + double agent2ndRatioOfTotal = 0.0; + double shippingFeeRatioOfTotal = 0.0; + + double lklRatioOfAvailable = 0.0; + double mchRatioOfAvailable = 0.0; + double platRatioOfAvailable = 0.0; + double agent1stRatioOfAvailable = 0.0; + double agent2ndRatioOfAvailable = 0.0; + double shippingFeeRatioOfAvailable = 0.0; + + if (totalSeparateAmount > 0) { + if (getLklAmount() != null) { + lklRatioOfTotal = (double) getLklAmount() / totalSeparateAmount * 100; + } + if (getMchAmount() != null) { + mchRatioOfTotal = (double) getMchAmount() / totalSeparateAmount * 100; + } + if (getPlatAmount() != null) { + platRatioOfTotal = (double) getPlatAmount() / totalSeparateAmount * 100; + } + if (getAgent1stAmount() != null) { + agent1stRatioOfTotal = (double) getAgent1stAmount() / totalSeparateAmount * 100; + } + if (getAgent2ndAmount() != null) { + agent2ndRatioOfTotal = (double) getAgent2ndAmount() / totalSeparateAmount * 100; + } + if (getShippingFee() != null) { + shippingFeeRatioOfTotal = (double) getShippingFee() / totalSeparateAmount * 100; + } + } + + if (canSeparateAmount > 0) { + if (getLklAmount() != null) { + lklRatioOfAvailable = (double) getLklAmount() / canSeparateAmount * 100; + } + if (getMchAmount() != null) { + mchRatioOfAvailable = (double) getMchAmount() / canSeparateAmount * 100; + } + if (getPlatAmount() != null) { + platRatioOfAvailable = (double) getPlatAmount() / canSeparateAmount * 100; + } + if (getAgent1stAmount() != null) { + agent1stRatioOfAvailable = (double) getAgent1stAmount() / canSeparateAmount * 100; + } + if (getAgent2ndAmount() != null) { + agent2ndRatioOfAvailable = (double) getAgent2ndAmount() / canSeparateAmount * 100; + } + if (getShippingFee() != null) { + shippingFeeRatioOfAvailable = (double) getShippingFee() / canSeparateAmount * 100; + } + } + + String calculationMethod = isBasedOnTotalAmount ? "基于总交易额方式计算" : "基于可分账额方式计算"; + + return String.format("分账结果%s,总交易额=%d分, 可分账额=%d分, 拉卡拉手续费=%d分(%.2f%%/%.2f%%), 配送费=%d分(%.2f%%/%.2f%%), 商户=%d分(%.2f%%/%.2f%%), 平台=%d分(%.2f%%/%.2f%%), 一级代理商=%d分(%.2f%%/%.2f%%), 二级代理商=%d分(%.2f%%/%.2f%%)", + calculationMethod, + totalSeparateAmount, + canSeparateAmount, + getLklAmount() != null ? getLklAmount() : 0, + lklRatioOfTotal, lklRatioOfAvailable, + getShippingFee() != null ? getShippingFee() : 0, + shippingFeeRatioOfTotal, shippingFeeRatioOfAvailable, + getMchAmount() != null ? getMchAmount() : 0, + mchRatioOfTotal, mchRatioOfAvailable, + getPlatAmount() != null ? getPlatAmount() : 0, + platRatioOfTotal, platRatioOfAvailable, + getAgent1stAmount() != null ? getAgent1stAmount() : 0, + agent1stRatioOfTotal, agent1stRatioOfAvailable, + getAgent2ndAmount() != null ? getAgent2ndAmount() : 0, + agent2ndRatioOfTotal, agent2ndRatioOfAvailable); + } + + /** + * 将对象转换为JSON字符串格式 + * + * @return 包含所有属性的JSON字符串 + */ + public String toJSON() { + StringBuilder json = new StringBuilder(); + json.append("{"); + + // 添加基本属性 + appendProperty(json, "totalSeparateAmount", totalSeparateAmount, true); + appendProperty(json, "canSeparateAmount", canSeparateAmount, false); + appendProperty(json, "refCanSeparateAmount", refCanSeparateAmount, false); + appendProperty(json, "shippingFee", shippingFee, false); + + // 添加比例属性 + appendProperty(json, "lklRatio", lklRatio, false); + appendProperty(json, "mchRatio", mchRatio, false); + appendProperty(json, "platRatio", platRatio, false); + appendProperty(json, "agent1stRatio", agent1stRatio, false); + appendProperty(json, "agent2ndRatio", agent2ndRatio, false); + + // 添加金额属性 + appendProperty(json, "lklAmount", lklAmount, false); + appendProperty(json, "mchAmount", mchAmount, false); + appendProperty(json, "platAmount", platAmount, false); + appendProperty(json, "agent1stAmount", agent1stAmount, false); + appendProperty(json, "agent2ndAmount", agent2ndAmount, false); + + // 移除最后的逗号并关闭JSON对象 + if (json.charAt(json.length() - 1) == ',') { + json.setLength(json.length() - 1); + } + json.append("}"); + + return json.toString(); + } + + // ================================ 私有方法 ================================ + + /** + * 验证基于可分账金额分账的输入参数 + */ + private boolean validateInputs() { + if (totalSeparateAmount == null || totalSeparateAmount <= 0) { + logger.info("分账计算失败:分账总金额必须大于0,当前值={}", totalSeparateAmount); + return false; + } + + if (shippingFee == null || shippingFee < 0) { + logger.info("分账计算失败:配送费不能小于0,当前值={}", shippingFee); + return false; + } + + if (lklRatio == null || lklRatio.compareTo(BigDecimal.ZERO) <= 0) { + logger.info("分账计算失败:拉卡拉分账比例必须大于0,当前值={}", lklRatio); + return false; + } + + if (mchRatio == null || mchRatio.compareTo(BigDecimal.ZERO) <= 0) { + logger.info("分账计算失败:商户分账比例必须大于0,当前值={}", mchRatio); + return false; + } + + if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) { + logger.info("分账计算失败:平台分账比例必须大于0,当前值={}", platRatio); + return false; + } + + return true; + } + + /** + * 验证基于总金额分账的输入参数 + */ + private boolean validateInputsForTotalAmount() { + // 基于总金额的分账计算参数验证与基于可分账金额的验证相同 + return validateInputs(); + } + + /** + * 计算拉卡拉分账金额 + */ + private void calculateLklAmount() { + BigDecimal lklAmountDecimal = new BigDecimal(totalSeparateAmount).multiply(lklRatio); + lklAmount = lklAmountDecimal.setScale(2, RoundingMode.HALF_UP).intValue(); + logger.debug("拉卡拉分账计算:总金额{} × 拉卡拉比例{} = {},四舍五入保留两位小数后={}", + totalSeparateAmount, lklRatio, lklAmountDecimal, lklAmount); + } + + /** + * 计算可分账金额 + */ + private void calculateCanSeparateAmount() { + canSeparateAmount = totalSeparateAmount - shippingFee - lklAmount; + logger.debug("可分账金额计算:总金额{} - 配送费{} - 拉卡拉分账{} = {}", + totalSeparateAmount, shippingFee, lklAmount, canSeparateAmount); + } + + /** + * 检查refCanSeparateAmount是否有效 + * 有效条件:非空且大于0且小于totalSeparateAmount + * + * @return true表示有效,false表示无效 + */ + private boolean isRefCanSeparateAmountValid() { + return refCanSeparateAmount != null && refCanSeparateAmount > 0 && refCanSeparateAmount < totalSeparateAmount; + } + + /** + * 获取分账基数金额 + * 如果refCanSeparateAmount有效,则使用refCanSeparateAmount,否则使用canSeparateAmount + * + * @return 分账基数金额 + */ + private int getSeparateBaseAmount() { + if (isRefCanSeparateAmountValid()) { + return refCanSeparateAmount; + } + return canSeparateAmount; + } + + /** + * 检查并应用参考可分账金额 + * 当refCanSeparateAmount有效且与计算出的canSeparateAmount不一致时, + * 将差额分配给指定优先级的分账对象(平台 > 一级代理商 > 二级代理商 > 商户) + */ + private void checkAndApplyRefCanSeparateAmount() { + // 判断refCanSeparateAmount是否有效(非空且大于0且小于totalSeparateAmount) + if (isRefCanSeparateAmountValid()) { + // 比较refCanSeparateAmount与计算出的canSeparateAmount + if (!refCanSeparateAmount.equals(canSeparateAmount)) { + int difference = refCanSeparateAmount - canSeparateAmount; + logger.info("检测到refCanSeparateAmount与计算出的canSeparateAmount不一致,差额={}分。" + + "refCanSeparateAmount={}分,计算出的canSeparateAmount={}分。", + difference, refCanSeparateAmount, canSeparateAmount); + + // 将差额分配给指定优先级的分账对象(商户 > 二级代理商 > 一级代理商 > 平台) + if (difference != 0) { + if (mchAmount == null) mchAmount = 0; + if (agent2ndAmount == null) agent2ndAmount = 0; + if (agent1stAmount == null) agent1stAmount = 0; + if (platAmount == null) platAmount = 0; + + if (difference > 0) { + // 如果差额为正,优先增加高优先级参与方的金额 + mchAmount += difference; + } else { + // 如果差额为负,优先从低优先级参与方扣除 + int diff = -difference; + if (mchAmount >= diff) { + mchAmount -= diff; + } else if (agent2ndAmount > 0 && agent2ndAmount >= diff) { + agent2ndAmount -= diff; + } else if (agent1stAmount > 0 && agent1stAmount >= diff) { + agent1stAmount -= diff; + } else { + platAmount -= diff; + } + } + logger.debug("已将差额{}分应用于分账金额调整", difference); + } + } else { + logger.debug("refCanSeparateAmount与计算出的canSeparateAmount一致,值={}分", canSeparateAmount); + } + } else { + logger.debug("refCanSeparateAmount无效或未设置,使用计算出的canSeparateAmount={}分作为分账基数", canSeparateAmount); + } + } + + /** + * 调整分账比例,确保有效值比例相加等于1.0 + */ + private void adjustRatios() { + // 计算有效的分账比例总和 + BigDecimal effectiveRatioSum = BigDecimal.ZERO + .add(mchRatio) + .add(platRatio) + .add(agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO) + .add(agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO); + + logger.debug("调整前各分账比例:商户={},平台={},一级代理商={},二级代理商={},有效比例总和={}", + mchRatio, platRatio, agent1stRatio, agent2ndRatio, effectiveRatioSum); + + // 动态调整最后一个分账方的比例,确保总和为1.0 + BigDecimal adjustmentRatio = BigDecimal.ONE.subtract(effectiveRatioSum); + if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { + // 调整二级代理商比例 + agent2ndRatio = agent2ndRatio.add(adjustmentRatio); + } else if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + // 调整一级代理商比例 + agent1stRatio = agent1stRatio.add(adjustmentRatio); + } else { + // 调整平台比例 + platRatio = platRatio.add(adjustmentRatio); + } + + logger.debug("调整后各分账比例:商户={},平台={},一级代理商={},二级代理商={}", + mchRatio, platRatio, agent1stRatio, agent2ndRatio); + } + + /** + * 按优先级进行分账(基于可分账金额) + */ + private void performSeparate() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + // 商户分账金额 = 可分账金额 × 商户分账比例(四舍五入) + BigDecimal mchAmountDecimal = new BigDecimal(separateBaseAmount).multiply(mchRatio); + mchAmount = mchAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("商户分账计算:可分账金额{} × 商户比例{} = {},四舍五入后={}", separateBaseAmount, mchRatio, mchAmountDecimal, mchAmount); + + // 平台分账金额 = 可分账金额 × 平台分账比例(四舍五入) + BigDecimal platAmountDecimal = new BigDecimal(separateBaseAmount).multiply(platRatio); + platAmount = platAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("平台分账计算:可分账金额{} × 平台比例{} = {},四舍五入后={}", separateBaseAmount, platRatio, platAmountDecimal, platAmount); + + // 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(四舍五入) + if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal agent1stAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent1stRatio); + agent1stAmount = agent1stAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("一级代理商分账计算:可分账金额{} × 一级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent1stRatio, agent1stAmountDecimal, agent1stAmount); + } else { + agent1stAmount = 0; + } + + // 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(四舍五入) + if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal agent2ndAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent2ndRatio); + agent2ndAmount = agent2ndAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("二级代理商分账计算:可分账金额{} × 二级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent2ndRatio, agent2ndAmountDecimal, agent2ndAmount); + } else { + agent2ndAmount = 0; + } + } + + /** + * 按优先级进行分账(基于总金额) + */ + private void performSeparateBasedOnTotalAmount() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + // 平台分账金额 = 可分账金额 × 平台分账比例(四舍五入) + BigDecimal platAmountDecimal = new BigDecimal(separateBaseAmount).multiply(platRatio); + platAmount = platAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("平台分账计算:可分账金额{} × 平台比例{} = {},四舍五入后={}", separateBaseAmount, platRatio, platAmountDecimal, platAmount); + + // 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(四舍五入) + if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal agent1stAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent1stRatio); + agent1stAmount = agent1stAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("一级代理商分账计算:可分账金额{} × 一级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent1stRatio, agent1stAmountDecimal, agent1stAmount); + } else { + agent1stAmount = 0; + } + + // 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(四舍五入) + if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal agent2ndAmountDecimal = new BigDecimal(separateBaseAmount).multiply(agent2ndRatio); + agent2ndAmount = agent2ndAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("二级代理商分账计算:可分账金额{} × 二级代理商比例{} = {},四舍五入后={}", separateBaseAmount, agent2ndRatio, agent2ndAmountDecimal, agent2ndAmount); + } else { + agent2ndAmount = 0; + } + + // 商户分账金额 = 可分账金额 - 平台分账金额 - 一级代理商分账金额 - 二级代理商分账金额 + mchAmount = separateBaseAmount - platAmount - agent1stAmount - agent2ndAmount; + logger.debug("商户分账计算:可分账金额{} - 平台分账{} - 一级代理商分账{} - 二级代理商分账{} = {}", + separateBaseAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); + } + + /** + * 确保总额平衡 + */ + private void balanceAmounts() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + int totalAmount = mchAmount + platAmount + agent1stAmount + agent2ndAmount; + if (totalAmount != separateBaseAmount) { + int diff = separateBaseAmount - totalAmount; + logger.debug("分账总额与可分账金额不符,差额={},开始调整", diff); + + // 将差额分配给优先级最低的参与方,确保总额平衡 + if (mchAmount + diff >= 0) { + mchAmount += diff; + logger.debug("调整商户分账金额:{} + {} = {}", mchAmount - diff, diff, mchAmount); + } else if (agent2ndAmount > 0 && agent2ndAmount + diff >= 0) { + agent2ndAmount += diff; + logger.debug("调整二级代理商分账金额:{} + {} = {}", agent2ndAmount - diff, diff, agent2ndAmount); + } else if (agent1stAmount > 0 && agent1stAmount + diff >= 0) { + agent1stAmount += diff; + logger.debug("调整一级代理商分账金额:{} + {} = {}", agent1stAmount - diff, diff, agent1stAmount); + } else if (platAmount + diff >= 0) { + platAmount += diff; + logger.debug("调整平台分账金额:{} + {} = {}", platAmount - diff, diff, platAmount); + } + } + } + + /** + * 保障各参与方分账比例不低于指定值(基于总金额的分账计算专用) + * 忽略商户比例保障,只保障平台、一级代理商、二级代理商的比例 + */ + private void guaranteeRatiosForTotalAmount() { + // 平台优先级最高 + guaranteePlatformRatio(); + + // 一级代理商优先级次之 + guaranteeAgent1stRatio(); + + // 不保障商户比例,商户只分到剩余余额 + } + + /** + * 保障各参与方分账比例不低于指定值 + */ + private void guaranteeRatios() { + // 商户优先级最高 + guaranteeMerchantRatio(); + + // 平台优先级次之 + guaranteePlatformRatio(); + + // 一级代理商优先级再次之 + guaranteeAgent1stRatio(); + + // 最终验证和调整,确保所有参与方分账比例不低于指定值 + finalGuaranteeMerchantRatio(); + } + + /** + * 保障商户分账比例 + */ + private void guaranteeMerchantRatio() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + BigDecimal actualMchRatio = new BigDecimal(mchAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); + if (actualMchRatio.compareTo(mchRatio) < 0) { + int requiredMchAmount = mchRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.HALF_UP).intValue(); + int diff = requiredMchAmount - mchAmount; + if (diff > 0) { + logger.debug("商户分账比例不足,需要调整金额={},商户当前金额={},应得金额={}", diff, mchAmount, requiredMchAmount); + + if (agent2ndAmount > 0 && agent2ndAmount >= diff) { + agent2ndAmount -= diff; + mchAmount += diff; + logger.debug("从二级代理商调整{}给商户,调整后:商户={},二级代理商={}", diff, mchAmount, agent2ndAmount); + } else if (agent1stAmount > 0 && agent1stAmount >= diff) { + agent1stAmount -= diff; + mchAmount += diff; + logger.debug("从一级代理商调整{}给商户,调整后:商户={},一级代理商={}", diff, mchAmount, agent1stAmount); + } else if (platAmount >= diff) { + platAmount -= diff; + mchAmount += diff; + logger.debug("从平台调整{}给商户,调整后:商户={},平台={}", diff, mchAmount, platAmount); + } + } + } + } + + /** + * 保障平台分账比例 + */ + private void guaranteePlatformRatio() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + BigDecimal actualPlatRatio = new BigDecimal(platAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); + if (actualPlatRatio.compareTo(platRatio) < 0) { + int requiredPlatAmount = platRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.HALF_UP).intValue(); + int diff = requiredPlatAmount - platAmount; + if (diff > 0) { + logger.debug("平台分账比例不足,需要调整金额={},平台当前金额={},应得金额={}", diff, platAmount, requiredPlatAmount); + + if (agent2ndAmount > 0 && agent2ndAmount >= diff) { + agent2ndAmount -= diff; + platAmount += diff; + logger.debug("从二级代理商调整{}给平台,调整后:平台={},二级代理商={}", diff, platAmount, agent2ndAmount); + } else if (agent1stAmount > 0 && agent1stAmount >= diff) { + agent1stAmount -= diff; + platAmount += diff; + logger.debug("从一级代理商调整{}给平台,调整后:平台={},一级代理商={}", diff, platAmount, agent1stAmount); + } + } + } + } + + /** + * 保障一级代理商分账比例 + */ + private void guaranteeAgent1stRatio() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal actualAgent1stRatio = new BigDecimal(agent1stAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); + if (actualAgent1stRatio.compareTo(agent1stRatio) < 0) { + int requiredAgent1stAmount = agent1stRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.HALF_UP).intValue(); + int diff = requiredAgent1stAmount - agent1stAmount; + if (diff > 0) { + logger.debug("一级代理商分账比例不足,需要调整金额={},一级代理商当前金额={},应得金额={}", diff, agent1stAmount, requiredAgent1stAmount); + + if (agent2ndAmount > 0 && agent2ndAmount >= diff) { + agent2ndAmount -= diff; + agent1stAmount += diff; + logger.debug("从二级代理商调整{}给一级代理商,调整后:一级代理商={},二级代理商={}", diff, agent1stAmount, agent2ndAmount); + } + } + } + } + } + + /** + * 最终保障商户分账比例 + */ + private void finalGuaranteeMerchantRatio() { + // 确定分账基数 + int separateBaseAmount = getSeparateBaseAmount(); + + BigDecimal finalMchRatio = new BigDecimal(mchAmount).divide(new BigDecimal(separateBaseAmount), 6, RoundingMode.HALF_UP); + if (finalMchRatio.compareTo(mchRatio) < 0) { + int requiredMchAmount = mchRatio.multiply(new BigDecimal(separateBaseAmount)).setScale(0, RoundingMode.UP).intValue(); + int diff = requiredMchAmount - mchAmount; + if (diff > 0) { + logger.debug("商户最终分账比例仍不足,需要调整金额={},商户当前金额={},应得金额={}", diff, mchAmount, requiredMchAmount); + + if (agent2ndAmount > 0 && agent2ndAmount >= diff) { + agent2ndAmount -= diff; + mchAmount += diff; + logger.debug("最终调整:从二级代理商调整{}给商户,调整后:商户={},二级代理商={}", diff, mchAmount, agent2ndAmount); + } else { + int remainingDiff = diff - agent2ndAmount; + if (agent1stAmount > 0 && agent1stAmount >= remainingDiff) { + agent1stAmount -= remainingDiff; + mchAmount += diff; + logger.debug("最终调整:从一级代理商调整{}给商户,调整后:商户={},一级代理商={}", remainingDiff, mchAmount, agent1stAmount); + } else { + int remainingDiff2 = remainingDiff - agent1stAmount; + if (platAmount >= remainingDiff2) { + platAmount -= remainingDiff2; + mchAmount += diff; + logger.debug("最终调整:从平台调整{}给商户,调整后:商户={},平台={}", remainingDiff2, mchAmount, platAmount); + } + } + } + } + } + } + + /** + * 向JSON字符串中添加属性 + * + * @param json StringBuilder对象 + * @param key 属性名 + * @param value 属性值 + * @param first 是否为第一个属性 + */ + private void appendProperty(StringBuilder json, String key, Object value, boolean first) { + if (!first) { + json.append(","); + } + json.append("\"").append(key).append("\":"); + if (value == null) { + json.append("null"); + } else if (value instanceof String) { + json.append("\"").append(value).append("\""); + } else { + json.append(value); + } + } +} \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 9929309a..a5c2db54 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -32,7 +32,7 @@ import com.suisung.mall.common.modules.lakala.*; import com.suisung.mall.common.modules.order.ShopOrderLkl; import com.suisung.mall.common.modules.store.ShopMchEntry; import com.suisung.mall.common.modules.store.ShopStoreBase; -import com.suisung.mall.common.pojo.dto.LklSeparateDTO; +import com.suisung.mall.common.pojo.dto.LklSeparateDTOBak; import com.suisung.mall.common.service.impl.CommonService; import com.suisung.mall.common.utils.*; import com.suisung.mall.shop.lakala.service.*; @@ -2064,12 +2064,12 @@ public class LakalaApiServiceImpl implements LakalaApiService { separateRecord.setLog_no(separateRequest.getLogNo()); // 发货完成交易流水号后14位 。分账商户用该流水号发起分账 separateRecord.setLog_date(separateRequest.getLogDate()); separateRecord.setOrder_id(shopOrderLkl.getOrder_id()); - separateRecord.setTotal_amt(separateRequest.getTotalAmt()); separateRecord.setNotify_url(separateRequest.getNotifyUrl()); separateRecord.setLkl_org_no(separateRequest.getLklOrgNo()); separateRecord.setRecv_datas(JSONUtil.toJsonStr(separateRequest.getRecvDatas())); separateRecord.setStatus(respData.getStr("status")); - separateRecord.setTotal_separate_value(platformAmount + agentAmount); + separateRecord.setTotal_amt(separateRequest.getTotalAmt()); + separateRecord.setActual_separate_amt(Convert.toStr(shopOrderLkl.getSplit_amt())); try { if (lklOrderSeparateService.addOrUpdateByReceiverNo(separateRecord)) { @@ -2160,19 +2160,11 @@ public class LakalaApiServiceImpl implements LakalaApiService { LklOrderSeparate existingSeparateRecord = lklOrderSeparateService.getByLogNoAndOutTradeNo( shopOrderLkl.getLkl_receive_log_no(), shopOrderLkl.getOut_separate_no()); - if (existingSeparateRecord != null) { - String status = existingSeparateRecord.getStatus(); - switch (status) { - case "SUCCESS": - log.info("[分账操作] 订单[{}]交易对账流水号[{}]已完成分账,跳过处理", orderId, shopOrderLkl.getLkl_receive_log_no()); - return Pair.of(true, "订单已处理"); - case "PROCESSING": - case "ACCEPTED": - log.info("[分账操作] 订单[{}]交易对账流水号[{}]分账处理中或已受理,跳过处理", orderId, shopOrderLkl.getLkl_receive_log_no()); - return Pair.of(true, "订单已处理中或已受理"); - } - } else if (CommonConstant.Enable.equals(shopOrderLkl.getSeparate_status())) { - log.warn("[分账操作] 订单[{}]交易流水号[{}]订单已分账,跳过处理", orderId, receiveTradeNo); + if (existingSeparateRecord != null + && "SUCCESS".equals(existingSeparateRecord.getStatus()) + && "SUCCESS".equals(existingSeparateRecord.getFinal_status()) + && CommonConstant.Enable.equals(shopOrderLkl.getSeparate_status())) { + log.info("[分账操作] 订单[{}]交易对账流水号[{}]已完成分账,跳过处理", orderId, shopOrderLkl.getLkl_receive_log_no()); return Pair.of(true, "订单已分账,请勿重复操作"); } @@ -2229,31 +2221,31 @@ public class LakalaApiServiceImpl implements LakalaApiService { BigDecimal wxFeeRatio = StrUtil.isEmpty(wxFee) ? BigDecimal.valueOf(0.0025) : new BigDecimal(wxFee).divide(BigDecimal.valueOf(100)); // 构建分账参数对象 - LklSeparateDTO lklSeparateDTO = new LklSeparateDTO(); - lklSeparateDTO.setTotalSeparateAmount(shopOrderLkl.getTotal_amt()); - lklSeparateDTO.setShippingFee(shoppingFeeInner); - lklSeparateDTO.setLklRatio(wxFeeRatio); // 拉卡拉给的微信分账比例 0.0025 千分之2.5 - lklSeparateDTO.setMchRatio(mchSplitRatio); - lklSeparateDTO.setPlatRatio(platformSplitRatio); + LklSeparateDTOBak lklSeparateDTOBak = new LklSeparateDTOBak(); + lklSeparateDTOBak.setTotalSeparateAmount(shopOrderLkl.getTotal_amt()); + lklSeparateDTOBak.setShippingFee(shoppingFeeInner); + lklSeparateDTOBak.setLklRatio(wxFeeRatio); // 拉卡拉给的微信分账比例 0.0025 千分之2.5 + lklSeparateDTOBak.setMchRatio(mchSplitRatio); + lklSeparateDTOBak.setPlatRatio(platformSplitRatio); if (distributorSplitRatio.compareTo(BigDecimal.ZERO) > 0) { // 二级代理商参与分账 - lklSeparateDTO.setAgent2ndRatio(distributorSplitRatio); + lklSeparateDTOBak.setAgent2ndRatio(distributorSplitRatio); } - lklSeparateDTO.setRefCanSeparateAmount(canSeparateAmt); // 拉卡拉实时返回的可分账金额 + lklSeparateDTOBak.setRefCanSeparateAmount(canSeparateAmt); // 拉卡拉实时返回的可分账金额 // 分账方式:根据可分账金额分账 - Pair canSeparateAmtResult = lklSeparateDTO.calcOnCanSeparateAmount(); + Pair canSeparateAmtResult = lklSeparateDTOBak.calcOnCanSeparateAmount(); if (!canSeparateAmtResult.getFirst()) { - log.error("[分账操作] 分账参数评估,结果无法分账 {}", lklSeparateDTO); + log.error("[分账操作] 分账参数评估,结果无法分账 {}", lklSeparateDTOBak); return Pair.of(false, "分账参数评估,结果无法分账"); } - log.debug("[分账操作] 分账参数计算结果:{}", lklSeparateDTO); + log.debug("[分账操作] 分账参数计算结果:{}", lklSeparateDTOBak); // 更新分账计算结果 - shopOrderLkl.setSeparate_remark(lklSeparateDTO.toString()); + shopOrderLkl.setSeparate_remark(lklSeparateDTOBak.toString()); if (CheckUtil.isEmpty(canSeparateAmt)) { - shopOrderLkl.setSplit_amt(lklSeparateDTO.getCanSeparateAmount()); - canSeparateAmt = lklSeparateDTO.getCanSeparateAmount(); + shopOrderLkl.setSplit_amt(lklSeparateDTOBak.getCanSeparateAmount()); + canSeparateAmt = lklSeparateDTOBak.getCanSeparateAmount(); } else { shopOrderLkl.setSplit_amt_ref(canSeparateAmt); } @@ -2273,9 +2265,9 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 构建分账接收方列表 List recvDatas = new ArrayList<>(); - Integer merchantAmount = lklSeparateDTO.getMchAmount(); - Integer platformAmount = lklSeparateDTO.getPlatAmount(); - Integer agentAmount = lklSeparateDTO.getAgent2ndAmount(); + Integer merchantAmount = lklSeparateDTOBak.getMchAmount(); + Integer platformAmount = lklSeparateDTOBak.getPlatAmount(); + Integer agentAmount = lklSeparateDTOBak.getAgent2ndAmount(); log.info("[分账操作] 金额计算结果:订单={}, 商户={}, 总金额={}分, 可分金额={}分, 商家比例={}, 商家分得={}分, 平台比例={}, 平台分得={}分, 代理商比例={}, 代理商分得={}分", orderId, merchantNo, shopOrderLkl.getTotal_amt(), canSeparateAmt, mchSplitRatio, merchantAmount, @@ -2363,12 +2355,14 @@ public class LakalaApiServiceImpl implements LakalaApiService { separateRecord.setLog_no(separateRequest.getLogNo()); separateRecord.setLog_date(separateRequest.getLogDate()); separateRecord.setOrder_id(shopOrderLkl.getOrder_id()); - separateRecord.setTotal_amt(separateRequest.getTotalAmt()); + separateRecord.setNotify_url(separateRequest.getNotifyUrl()); separateRecord.setLkl_org_no(separateRequest.getLklOrgNo()); separateRecord.setRecv_datas(JSONUtil.toJsonStr(separateRequest.getRecvDatas())); separateRecord.setStatus(respData.getStr("status")); - separateRecord.setTotal_separate_value(canSeparateAmt); + separateRecord.setTotal_amt(separateRequest.getTotalAmt()); + separateRecord.setActual_separate_amt(Convert.toStr(canSeparateAmt)); + separateRecord.setTotal_fee_amt(Convert.toStr(lklSeparateDTOBak.getLklAmount())); if (lklOrderSeparateService.addOrUpdateByReceiverNo(separateRecord)) { log.info("[分账操作] 记录保存成功:订单={}, 分账单号={}, 状态={}, 分账流水号={}", @@ -2390,260 +2384,6 @@ public class LakalaApiServiceImpl implements LakalaApiService { } } - - public Pair innerDoOrderSeparateByMerchantAndLogNoBak(String lklMerchantNo, String receiveTradeNo, String receiveLogNo) { - // 1. 输入参数校验 - if (StrUtil.isBlank(lklMerchantNo) || StrUtil.isBlank(receiveTradeNo) || StrUtil.isBlank(receiveLogNo)) { - log.warn("[分账操作] 参数校验失败:缺少必要参数, lklMerchantNo={}, receiveTradeNo={}, receiveLogNo={}", - lklMerchantNo, receiveTradeNo, receiveLogNo); - return Pair.of(false, "缺少必要参数"); - } - - try { - log.info("[分账操作] 开始处理分账请求, lklMerchantNo={}, receiveTradeNo={}, receiveLogNo={}", - lklMerchantNo, receiveTradeNo, receiveLogNo); - - // 2. 查询订单信息 - ShopOrderLkl shopOrderLkl = shopOrderLklService.getByLklMchNoAndReceiveTradeNoAndReceiveLogNo(lklMerchantNo, receiveTradeNo, receiveLogNo); - if (shopOrderLkl == null) { - log.warn("[分账操作] 失败:对账流水号[{}]不存在", receiveLogNo); - return Pair.of(false, "订单不存在"); - } - - // TODO 检查可分账余额是否足够? - Pair balanceCheckResult = queryMchCanSplitAmt(lklMerchantNo, receiveLogNo, shopOrderLkl.getLkl_log_date()); - if (balanceCheckResult == null) { - log.error("[分账操作] 查询可分账余额失败:lklMerchantNo={} logDate={} receiveLogNo={}", lklMerchantNo, shopOrderLkl.getLkl_log_date(), receiveLogNo); - } - - log.warn("[分账操作] 可分账余额检查结果:分账总余额={}分 可分账金额={}分", balanceCheckResult.getFirst(), balanceCheckResult.getSecond()); - Integer canSeparateAmt = Convert.toInt(balanceCheckResult.getSecond()); - if (canSeparateAmt == null || canSeparateAmt <= 0) { - log.warn("[分账操作] lklMerchantNo={} receiveTradeNo={} receiveLogNo={} 可分账金额={}分 可分账余额不足,跳过处理", lklMerchantNo, receiveTradeNo, receiveLogNo, balanceCheckResult.getSecond()); - return Pair.of(false, "可分账余额不足"); - } - - String orderId = shopOrderLkl.getOrder_id(); - log.info("[分账操作] 开始处理订单[{}]的分账", orderId); - - // 3. 检查是否已确认收货 - if (!CommonConstant.Enable.equals(shopOrderLkl.getReceive_status())) { - log.warn("[分账操作] 订单[{}]交易流水号[{}]未被确认收货,跳过处理", orderId, receiveTradeNo); - return Pair.of(false, "订单未确认收货,暂无法分账"); - } - - if (CommonConstant.Enable.equals(shopOrderLkl.getSeparate_status())) { - log.warn("[分账操作] 订单[{}]交易流水号[{}]未被确认收货,跳过处理", orderId, receiveTradeNo); - return Pair.of(true, "订单已分账,请勿重复操作"); - } - - // 4. 检查分账状态,避免重复处理 - LklOrderSeparate existingSeparateRecord = lklOrderSeparateService.getByLogNoAndOutTradeNo(shopOrderLkl.getLkl_receive_log_no(), shopOrderLkl.getOut_separate_no()); - if (existingSeparateRecord != null) { - String status = existingSeparateRecord.getStatus(); - if ("SUCCESS".equals(status)) { - log.info("[分账操作] 订单[{}]交易对账流水号[{}]已完成分账,跳过处理", orderId, shopOrderLkl.getLkl_sub_log_no()); - return Pair.of(true, "订单已处理"); - } - if ("PROCESSING".equals(status) || "ACCEPTED".equals(status)) { - log.info("[分账操作] 订单[{}]交易对账流水号[{}]分账处理中或已受理,跳过处理", orderId, shopOrderLkl.getLkl_sub_log_no()); - return Pair.of(true, "订单已处理中或已受理"); - } - } - - // 5. 获取订单分账相关参数 - String merchantNo = shopOrderLkl.getLkl_merchant_no(); - - // 分账金额 = 应付总金额-运费(支付时已计算好) - Integer splitAmount = shopOrderLkl.getSplit_amt(); - splitAmount = CheckUtil.isEmpty(splitAmount) ? 0 : splitAmount; - - // 6. 分账金额校验 - if (splitAmount < 1) { - String errorMsg = String.format("[分账操作] 店铺[%s]订单[%s]分账金额[%d]低于1分钱,跳过分账", - shopOrderLkl.getStore_id(), orderId, splitAmount); - log.error(errorMsg); - if (existingSeparateRecord != null) { - lklOrderSeparateService.updateRemark(existingSeparateRecord.getId(), errorMsg); - } - return Pair.of(false, "订单分账金额低于1分钱"); - } - - // 7. 获取分账平台接收方信息 - LklLedgerMerReceiverBind platformReceiver = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo); - if (platformReceiver == null) { - String errorMsg = String.format("[分账操作] 店铺[%s]未绑定平台方接收账户,跳过分账", shopOrderLkl.getStore_id()); - log.error(errorMsg); - return Pair.of(false, "平台方未绑定账户"); - } - - // 8. 构建分账接收方列表 - List recvDatas = new ArrayList<>(); - - // 9. 获取商家分账比例并校验 - BigDecimal merchantSplitRatioRaw = shopOrderLkl.getSplit_ratio(); - // 判断商家分账比例是否有效(必须在(0, 100]范围内) - boolean canSplitForMerchant = merchantSplitRatioRaw != null - && merchantSplitRatioRaw.compareTo(BigDecimal.ZERO) > 0 - && merchantSplitRatioRaw.compareTo(new BigDecimal(100)) <= 0; - if (!canSplitForMerchant) { - String errorMsg = String.format("[分账操作] 店铺[%s]商家分账比例[%s]不在(0-100]范围内,无法分账", - shopOrderLkl.getStore_id(), merchantSplitRatioRaw); - log.error(errorMsg); - return Pair.of(false, "商家分账比例无效"); - } - - // 10. 计算商家分账比例(转换为小数) - BigDecimal merchantSplitRatio = merchantSplitRatioRaw.divide(new BigDecimal(100)); - - // 11. 获取代理商分账信息 - BigDecimal distributorSplitRatio = BigDecimal.ZERO; - BigDecimal platformSplitRatio = BigDecimal.ONE; - List distributorReceivers = lklLedgerMerReceiverBindService.selectDistributorByMerCupNo(merchantNo); - if (distributorReceivers != null && !distributorReceivers.isEmpty()) { - distributorSplitRatio = new BigDecimal("0.8"); - platformSplitRatio = new BigDecimal("0.2"); - log.debug("[分账操作] 检测到代理商存在,调整分账比例: 代理商比例={}, 平台比例={}", distributorSplitRatio, platformSplitRatio); - } - - // 12. 记录关键分账参数,便于问题排查 - log.info("[分账操作] 参数信息:订单={}, 商户={}, 总金额={}分, 商家比例={}, 平台比例={}, 代理商比例={}, 是否有代理商={}", - orderId, merchantNo, splitAmount, merchantSplitRatio, platformSplitRatio, distributorSplitRatio, - (distributorReceivers != null && !distributorReceivers.isEmpty())); - - // 13. 计算各参与方分账金额 - Map splitAmountMap = CommonUtil.calculateProfitSharing(splitAmount, merchantSplitRatio, platformSplitRatio, distributorSplitRatio); - Integer merchantAmount = splitAmountMap.get("merchantAmount"); - Integer platformAmount = splitAmountMap.get("platformAmount"); - Integer agentAmount = splitAmountMap.get("agentAmount"); - - // 14. 记录分账结果,便于问题排查 - log.info("[分账操作] 金额计算结果:订单={}, 商户={}, 总金额={}分, 商家分得={}分, 平台分得={}分, 代理商分得={}分", - orderId, merchantNo, splitAmount, merchantAmount, platformAmount, agentAmount); - - // 15. 构建分账接收方列表 - if (merchantAmount > 0) { - V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); - receiver.setRecvMerchantNo(merchantNo); - receiver.setSeparateValue(merchantAmount.toString()); - recvDatas.add(receiver); - log.debug("[分账操作] 添加商家接收方: merchantNo={}, amount={}", merchantNo, merchantAmount); - } - - if (platformAmount > 0) { - V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); - receiver.setRecvNo(platformReceiver.getReceiver_no()); - receiver.setSeparateValue(platformAmount.toString()); - recvDatas.add(receiver); - log.debug("[分账操作] 添加平台接收方: receiverNo={}, amount={}", platformReceiver.getReceiver_no(), platformAmount); - } - - if (agentAmount > 0 && distributorReceivers != null && !distributorReceivers.isEmpty()) { - V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas(); - receiver.setRecvNo(distributorReceivers.get(0).getReceiver_no()); - receiver.setSeparateValue(agentAmount.toString()); - recvDatas.add(receiver); - log.debug("[分账操作] 添加代理商接收方: receiverNo={}, amount={}", distributorReceivers.get(0).getReceiver_no(), agentAmount); - } - - // 16. 初始化拉卡拉SDK - log.debug("[分账操作] 初始化拉卡拉SDK"); - initLKLSDK(); - - // 17. 构建分账请求对象 - V3SacsSeparateRequest separateRequest = new V3SacsSeparateRequest(); - separateRequest.setMerchantNo(merchantNo); - separateRequest.setOutSeparateNo(shopOrderLkl.getOut_separate_no()); - separateRequest.setLogNo(shopOrderLkl.getLkl_receive_log_no()); // 使用确认收货流水号作为分账流水号 - separateRequest.setLogDate(shopOrderLkl.getLkl_log_date()); - separateRequest.setTotalAmt(splitAmount.toString()); - separateRequest.setLklOrgNo(orgCode); - separateRequest.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0 - separateRequest.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify"); - - // 18. 设置分账接收方列表 - separateRequest.setRecvDatas(recvDatas); - log.info("[分账操作] 请求参数: 订单={}, 商户={}, 金额={}分, 分账接收方数量={}", - orderId, merchantNo, splitAmount, recvDatas.size()); - - log.debug("[分账操作] 请求详细参数: {}", JSONUtil.toJsonStr(separateRequest)); - - // 19. 发送分账请求 - log.info("[分账操作] 向拉卡拉发送分账请求:订单={}, 商户={}, 分账流水号={}", - orderId, merchantNo, shopOrderLkl.getLkl_receive_log_no()); - String response = LKLSDK.httpPost(separateRequest); - if (StrUtil.isBlank(response)) { - String errorMsg = String.format("[分账操作] 拉卡拉无响应,订单=%s,商户=%s,分账流水号=%s", - orderId, merchantNo, shopOrderLkl.getLkl_receive_log_no()); - // 更改分账状态:分账失败 - shopOrderLklService.updateSeparateStatusByReceiveLogNo(shopOrderLkl.getLkl_receive_log_no(), CommonConstant.Sta_Separate_Fail, errorMsg); - log.error(errorMsg); - return Pair.of(false, "拉卡拉无响应"); - } - - log.debug("[分账操作] 响应结果: {}", response); - - // 20. 解析响应结果 - JSONObject respJson = JSONUtil.parseObj(response); - if (respJson == null || !lklSacsSuccessCode.equals(respJson.getStr("code")) || respJson.getJSONObject("resp_data") == null) { - String errorMsg = String.format("[分账操作] 拉卡拉返回格式异常,订单=%s,商户=%s,分账流水号=%s,响应=%s,respJson=%s", - orderId, merchantNo, shopOrderLkl.getLkl_receive_log_no(), response, respJson); - // 更改分账状态:分账失败 - shopOrderLklService.updateSeparateStatusByReceiveLogNo(shopOrderLkl.getLkl_receive_log_no(), CommonConstant.Sta_Separate_Fail, errorMsg); - log.error(errorMsg); - return Pair.of(false, "拉卡拉分账异常:[" + respJson.getStr("code") + "]" + respJson.getStr("msg")); - } - - // 21. 保存分账记录 - JSONObject respData = respJson.getJSONObject("resp_data"); - LklOrderSeparate separateRecord = new LklOrderSeparate(); - separateRecord.setSeparate_no(respData.getStr("separate_no")); - separateRecord.setOut_separate_no(separateRequest.getOutSeparateNo()); - separateRecord.setMerchant_no(merchantNo); - separateRecord.setLog_no(separateRequest.getLogNo()); - separateRecord.setLog_date(separateRequest.getLogDate()); - separateRecord.setOrder_id(shopOrderLkl.getOrder_id()); - separateRecord.setTotal_amt(separateRequest.getTotalAmt()); - separateRecord.setNotify_url(separateRequest.getNotifyUrl()); - separateRecord.setLkl_org_no(separateRequest.getLklOrgNo()); - separateRecord.setRecv_datas(JSONUtil.toJsonStr(separateRequest.getRecvDatas())); - separateRecord.setStatus(respData.getStr("status")); - separateRecord.setTotal_separate_value(platformAmount + agentAmount); - - try { - if (lklOrderSeparateService.addOrUpdateByReceiverNo(separateRecord)) { - log.info("[分账操作] 记录保存成功:订单={}, 分账单号={}, 状态={}, 分账流水号={}", - orderId, separateRecord.getSeparate_no(), separateRecord.getStatus(), separateRecord.getLog_no()); - } else { - String errorMsg = String.format("[分账操作] 保存分账记录失败,订单=%s,分账单号=%s,分账流水号=%s", - orderId, separateRecord.getSeparate_no(), separateRecord.getLog_no()); - log.error(errorMsg); - lklOrderSeparateService.updateRemark(separateRecord.getLog_no(), separateRecord.getSeparate_no(), errorMsg); - return Pair.of(false, "保存分账记录失败"); - } - - // 保存 shop_order_lkl 关键字段 - - String result = "订单分账已提交处理"; - log.info("[分账操作] 结果:订单[{}] {}", orderId, result); - return Pair.of(true, result); - } catch (Exception e) { - String errorMsg = String.format("[分账操作] 保存分账记录异常,订单=%s,分账单号=%s,流水号=%s,错误=%s", - orderId, - separateRecord.getSeparate_no(), - separateRecord.getLog_no(), - e.getMessage()); - log.error(errorMsg, e); - return Pair.of(false, "保存分账记录异常"); - } - - } catch (Exception e) { - String errorMsg = String.format("[分账操作] 系统异常,分账对账流水号=%s,错误=%s", receiveLogNo, e.getMessage()); - log.error(errorMsg, e); - return Pair.of(false, "系统异常,请稍后重试"); - } - } - /** * 拉卡拉分账结果通知处理 *

@@ -2729,15 +2469,6 @@ public class LakalaApiServiceImpl implements LakalaApiService { .put("message", "分账已处理成功,请不要重复通知"); } - if (!"FAIL".equals(existingFinalStatus) && StrUtil.isNotBlank(existingFinalStatus)) { - String warnMsg = "已受理或处理中,成功后再通知"; - log.warn("[拉卡拉分账通知] {},订单号={},外部分账单号={},参数详情: {}", - warnMsg, logNo, outSeparateNo, paramsJson); - return JSONUtil.createObj() - .put("code", "FAIL") - .put("message", "已受理或处理中,成功后再通知"); - } - // 6. 记录关键参数信息,便于问题排查 log.info("[拉卡拉分账通知] 接收到分账通知,分账单号={},外部分账单号={},状态={},最终状态={}", separateNo, outSeparateNo, status, finalStatus); @@ -2753,9 +2484,12 @@ public class LakalaApiServiceImpl implements LakalaApiService { lklOrderSeparate.setSeparate_type(paramsJson.getStr("separate_type")); lklOrderSeparate.setSeparate_date(paramsJson.getStr("separate_date")); // 总计分账金额 - lklOrderSeparate.setTotal_separate_value(paramsJson.getInt("total_separate_value", 0)); + lklOrderSeparate.setTotal_amt(paramsJson.getStr("total_amt", "0")); + lklOrderSeparate.setActual_separate_amt(paramsJson.getStr("actual_separate_amt", "0")); + lklOrderSeparate.setTotal_fee_amt(paramsJson.getStr("total_fee_amt", "0")); lklOrderSeparate.setRemark("分账已完成"); lklOrderSeparate.setFinish_date(paramsJson.getStr("finish_date")); + lklOrderSeparate.setNotify_resp(signCheckResult.getSecond()); // 处理detail_datas(避免空指针) JSONArray detailDatas = paramsJson.getJSONArray("detail_datas"); @@ -2774,7 +2508,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { } // 更改分账状态:分账成功 - shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Success, "分账成功"); + shopOrderLklService.updateSeparateStatusByReceiveLogNo(logNo, CommonConstant.Sta_Separate_Success, ""); // 9. 记录处理成功日志 log.info("[拉卡拉分账通知] 分账通知处理成功, separateNo={}, status={}", separateNo, status);