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 9cf79d93..5e67a982 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 @@ -13,32 +13,12 @@ import java.math.RoundingMode; /** * 拉卡拉订单分账信息对象 *
- * 分账计算逻辑说明: - * 前提条件:分账总金额必须大于0,配送费不能小于0;拉卡拉、商户、平台分账比例必须大于0, - * 一级、二级代理商分账比例可以为null或小于等于0 + * 分账计算支持两种模式: + * 1. 基于可分账金额模式(默认):先扣除拉卡拉手续费和配送费,再对剩余金额进行分账 + * 2. 基于总金额模式:基于总分账金额进行分账计算 *
- * 分账计算步骤: - * 1. 计算拉卡拉分账金额 = 分账总金额 × 拉卡拉分账比例(结果向上取整) - * 2. 计算可分账金额 = 分账总金额 - 配送费 - 拉卡拉分账金额 - * 3. 调整分账比例,确保商户、平台、一级代理商、二级代理商的有效比例之和等于1.0 - * - 如果有二级代理商参与分账,则调整二级代理商比例 - * - 否则如果有一级代理商参与分账,则调整一级代理商比例 - * - 否则调整平台比例 - * 4. 按优先级计算各参与方分账金额(结果四舍五入): - * - 商户分账金额 = 可分账金额 × 商户分账比例 - * - 平台分账金额 = 可分账金额 × 平台分账比例 - * - 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(如果参与分账) - * - 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(如果参与分账) - * 5. 如分配总额与可分账金额不符,则调整最后一个分账方(按优先级顺序)以确保总额平衡: - * - 优先调整二级代理商(如果参与分账) - * - 否则调整一级代理商(如果参与分账) - * - 否则调整平台 - * - 最后调整商户 - * 6. 保障各参与方分账比例不能低于指定属性的比例(按优先级处理): - * - 商户优先级最高 - * - 平台优先级次之 - * - 一级代理商优先级再次之 - * - 二级代理商优先级最低 + * 分账优先级顺序:拉卡拉 > 平台 > 一级代理商 > 二级代理商 > 商户 + * 商户作为最后分账方,其金额为剩余金额,不强制遵循分账比例 */ @Data @EqualsAndHashCode(callSuper = false) @@ -54,9 +34,13 @@ public class LklSeparateDTO implements java.io.Serializable { @ApiModelProperty(value = "可分账金额(分)") private Integer canSeparateAmount; + @ApiModelProperty(value = "拉卡拉参考可分账金额(分)") + private Integer refCanSeparateAmount; + @ApiModelProperty(value = "配送费(分)") private Integer shippingFee; + @ApiModelProperty(value = "拉卡拉分账比例(如 0.0025=0.25%)") private BigDecimal lklRatio; @@ -72,6 +56,7 @@ public class LklSeparateDTO implements java.io.Serializable { @ApiModelProperty(value = "二级代理商分账比例(如 0.03=3%)") private BigDecimal agent2ndRatio; + @ApiModelProperty(value = "拉卡拉分账金额(分)") private Integer lklAmount; @@ -83,7 +68,7 @@ public class LklSeparateDTO implements java.io.Serializable { @ApiModelProperty(value = "一级代理商分账金额(分)") private Integer agent1stAmount; - + @ApiModelProperty(value = "二级代理商分账金额(分)") private Integer agent2ndAmount; @@ -93,49 +78,6 @@ public class LklSeparateDTO implements java.io.Serializable { public static void main(String[] args) { logger.info("开始测试分账计算功能..."); - // 临界点测试用例1: 极小金额测试 - logger.info("\n\n临界点测试用例1 - 极小金额测试:"); - LklSeparateDTO dto1 = new LklSeparateDTO(); - dto1.setTotalSeparateAmount(3); - dto1.setShippingFee(0); - dto1.setLklRatio(new BigDecimal("0.0025")); - dto1.setMchRatio(new BigDecimal("0.94")); - dto1.setPlatRatio(new BigDecimal("0.06")); - - logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}", - dto1.getTotalSeparateAmount(), dto1.getShippingFee(), dto1.getLklRatio(), - dto1.getMchRatio(), dto1.getPlatRatio()); - - boolean result1 = dto1.calculateProfitSharing(); - logger.info("分账计算结果: {}", (result1 ? "成功" : "失败")); - if (result1) { - logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", - dto1.getTotalSeparateAmount(), dto1.getLklAmount(), dto1.getShippingFee(), dto1.getCanSeparateAmount(), - dto1.getMchAmount(), dto1.getPlatAmount(), dto1.getAgent1stAmount(), dto1.getAgent2ndAmount()); - - double lklRatioOfTotal = (double) dto1.getLklAmount() / dto1.getTotalSeparateAmount() * 100; - double mchRatioOfTotal = (double) dto1.getMchAmount() / dto1.getTotalSeparateAmount() * 100; - double platRatioOfTotal = (double) dto1.getPlatAmount() / dto1.getTotalSeparateAmount() * 100; - double agent1stRatioOfTotal = (double) dto1.getAgent1stAmount() / dto1.getTotalSeparateAmount() * 100; - double agent2ndRatioOfTotal = (double) dto1.getAgent2ndAmount() / dto1.getTotalSeparateAmount() * 100; - - double lklRatioOfAvailable = (double) dto1.getLklAmount() / dto1.getCanSeparateAmount() * 100; - double mchRatioOfAvailable = (double) dto1.getMchAmount() / dto1.getCanSeparateAmount() * 100; - double platRatioOfAvailable = (double) dto1.getPlatAmount() / dto1.getCanSeparateAmount() * 100; - double agent1stRatioOfAvailable = (double) dto1.getAgent1stAmount() / dto1.getCanSeparateAmount() * 100; - double agent2ndRatioOfAvailable = (double) dto1.getAgent2ndAmount() / dto1.getCanSeparateAmount() * 100; - - logger.info("占比详情 (总金额占比 / 可分账金额占比): 拉卡拉={}% / {}%, 商户={}% / {}%, 平台={}% / {}%, 一级代理商={}% / {}%, 二级代理商={}% / {}%", - String.format("%.4f", lklRatioOfTotal), String.format("%.4f", lklRatioOfAvailable), - String.format("%.4f", mchRatioOfTotal), String.format("%.4f", mchRatioOfAvailable), - String.format("%.4f", platRatioOfTotal), String.format("%.4f", platRatioOfAvailable), - String.format("%.4f", agent1stRatioOfTotal), String.format("%.4f", agent1stRatioOfAvailable), - String.format("%.4f", agent2ndRatioOfTotal), String.format("%.4f", agent2ndRatioOfAvailable)); - - int total = dto1.getLklAmount() + dto1.getMchAmount() + dto1.getPlatAmount() + dto1.getAgent1stAmount() + dto1.getAgent2ndAmount() + dto1.getShippingFee(); - logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto1.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto1.getTotalSeparateAmount()); - } - // 临界点测试用例2: 小金额测试 logger.info("\n\n临界点测试用例2 - 小金额测试:"); LklSeparateDTO dto2 = new LklSeparateDTO(); @@ -149,24 +91,40 @@ public class LklSeparateDTO implements java.io.Serializable { dto2.getTotalSeparateAmount(), dto2.getShippingFee(), dto2.getLklRatio(), dto2.getMchRatio(), dto2.getPlatRatio()); - boolean result2 = dto2.calculateProfitSharing(); + boolean result2 = dto2.calcOnCanAmount(); logger.info("分账计算结果: {}", (result2 ? "成功" : "失败")); if (result2) { - logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", - dto2.getTotalSeparateAmount(), dto2.getLklAmount(), dto2.getShippingFee(), dto2.getCanSeparateAmount(), + logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 参考可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", + dto2.getTotalSeparateAmount(), dto2.getLklAmount(), dto2.getShippingFee(), dto2.getCanSeparateAmount(), dto2.getRefCanSeparateAmount(), dto2.getMchAmount(), dto2.getPlatAmount(), dto2.getAgent1stAmount(), dto2.getAgent2ndAmount()); double lklRatioOfTotal = (double) dto2.getLklAmount() / dto2.getTotalSeparateAmount() * 100; double mchRatioOfTotal = (double) dto2.getMchAmount() / dto2.getTotalSeparateAmount() * 100; double platRatioOfTotal = (double) dto2.getPlatAmount() / dto2.getTotalSeparateAmount() * 100; - double agent1stRatioOfTotal = (double) dto2.getAgent1stAmount() / dto2.getTotalSeparateAmount() * 100; - double agent2ndRatioOfTotal = (double) dto2.getAgent2ndAmount() / dto2.getTotalSeparateAmount() * 100; + double agent1stRatioOfTotal = 0.0; + double agent2ndRatioOfTotal = 0.0; + + // 安全检查,避免空指针异常 + if (dto2.getAgent1stAmount() != null) { + agent1stRatioOfTotal = (double) dto2.getAgent1stAmount() / dto2.getTotalSeparateAmount() * 100; + } + if (dto2.getAgent2ndAmount() != null) { + agent2ndRatioOfTotal = (double) dto2.getAgent2ndAmount() / dto2.getTotalSeparateAmount() * 100; + } double lklRatioOfAvailable = (double) dto2.getLklAmount() / dto2.getCanSeparateAmount() * 100; double mchRatioOfAvailable = (double) dto2.getMchAmount() / dto2.getCanSeparateAmount() * 100; double platRatioOfAvailable = (double) dto2.getPlatAmount() / dto2.getCanSeparateAmount() * 100; - double agent1stRatioOfAvailable = (double) dto2.getAgent1stAmount() / dto2.getCanSeparateAmount() * 100; - double agent2ndRatioOfAvailable = (double) dto2.getAgent2ndAmount() / dto2.getCanSeparateAmount() * 100; + double agent1stRatioOfAvailable = 0.0; + double agent2ndRatioOfAvailable = 0.0; + + // 安全检查,避免空指针异常 + if (dto2.getAgent1stAmount() != null) { + agent1stRatioOfAvailable = (double) dto2.getAgent1stAmount() / dto2.getCanSeparateAmount() * 100; + } + if (dto2.getAgent2ndAmount() != null) { + agent2ndRatioOfAvailable = (double) dto2.getAgent2ndAmount() / dto2.getCanSeparateAmount() * 100; + } logger.info("占比详情 (总金额占比 / 可分账金额占比): 拉卡拉={}% / {}%, 商户={}% / {}%, 平台={}% / {}%, 一级代理商={}% / {}%, 二级代理商={}% / {}%", String.format("%.4f", lklRatioOfTotal), String.format("%.4f", lklRatioOfAvailable), @@ -175,41 +133,62 @@ public class LklSeparateDTO implements java.io.Serializable { String.format("%.4f", agent1stRatioOfTotal), String.format("%.4f", agent1stRatioOfAvailable), String.format("%.4f", agent2ndRatioOfTotal), String.format("%.4f", agent2ndRatioOfAvailable)); - int total = dto2.getLklAmount() + dto2.getMchAmount() + dto2.getPlatAmount() + dto2.getAgent1stAmount() + dto2.getAgent2ndAmount() + dto2.getShippingFee(); + 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()); } - // 临界点测试用例3: 中等金额测试 - logger.info("\n\n临界点测试用例3 - 中等金额测试:"); - LklSeparateDTO dto3 = new LklSeparateDTO(); - dto3.setTotalSeparateAmount(100); - dto3.setShippingFee(30); - dto3.setLklRatio(new BigDecimal("0.0025")); - dto3.setMchRatio(new BigDecimal("0.94")); - dto3.setPlatRatio(new BigDecimal("0.06")); + // 基于总金额的分账计算测试用例(带refCanSeparateAmount) + logger.info("\n\n基于总金额的分账计算测试用例 - 带参考可分账金额:"); + LklSeparateDTO dto7 = new LklSeparateDTO(); + dto7.setTotalSeparateAmount(502); + dto7.setShippingFee(500); + dto7.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉比例0.25% + dto7.setMchRatio(new BigDecimal("0.94")); // 商户比例90% + dto7.setPlatRatio(new BigDecimal("0.01")); // 平台比例5% +// dto7.setAgent1stRatio(new BigDecimal("0.01")); +// dto7.setAgent2ndRatio(new BigDecimal("0.04")); +// dto7.setRefCanSeparateAmount(8079); // 设置参考可分账金额 - logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}", - dto3.getTotalSeparateAmount(), dto3.getShippingFee(), dto3.getLklRatio(), - dto3.getMchRatio(), dto3.getPlatRatio()); + logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}, 一级代理商比例={}, 二级代理商比例={}, 参考可分账金额={}分", + dto7.getTotalSeparateAmount(), dto7.getShippingFee(), dto7.getLklRatio(), dto7.getMchRatio(), + dto7.getPlatRatio(), dto7.getAgent1stRatio(), dto7.getAgent2ndRatio(), dto7.getRefCanSeparateAmount()); - boolean result3 = dto3.calculateProfitSharing(); - logger.info("分账计算结果: {}", (result3 ? "成功" : "失败")); - if (result3) { - logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", - dto3.getTotalSeparateAmount(), dto3.getLklAmount(), dto3.getShippingFee(), dto3.getCanSeparateAmount(), - dto3.getMchAmount(), dto3.getPlatAmount(), dto3.getAgent1stAmount(), dto3.getAgent2ndAmount()); + boolean result7 = dto7.calcOnTotalAmount(); + logger.info("基于总金额的分账计算结果(带参考可分账金额): {}", (result7 ? "成功" : "失败")); + if (result7) { + logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 参考可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", + dto7.getTotalSeparateAmount(), dto7.getLklAmount(), dto7.getShippingFee(), dto7.getCanSeparateAmount(), dto7.getRefCanSeparateAmount(), + dto7.getMchAmount(), dto7.getPlatAmount(), dto7.getAgent1stAmount(), dto7.getAgent2ndAmount()); - double lklRatioOfTotal = (double) dto3.getLklAmount() / dto3.getTotalSeparateAmount() * 100; - double mchRatioOfTotal = (double) dto3.getMchAmount() / dto3.getTotalSeparateAmount() * 100; - double platRatioOfTotal = (double) dto3.getPlatAmount() / dto3.getTotalSeparateAmount() * 100; - double agent1stRatioOfTotal = (double) dto3.getAgent1stAmount() / dto3.getTotalSeparateAmount() * 100; - double agent2ndRatioOfTotal = (double) dto3.getAgent2ndAmount() / dto3.getTotalSeparateAmount() * 100; + double lklRatioOfTotal = (double) dto7.getLklAmount() / dto7.getTotalSeparateAmount() * 100; + double mchRatioOfTotal = (double) dto7.getMchAmount() / dto7.getTotalSeparateAmount() * 100; + double platRatioOfTotal = (double) dto7.getPlatAmount() / dto7.getTotalSeparateAmount() * 100; + double agent1stRatioOfTotal = 0.0; + double agent2ndRatioOfTotal = 0.0; - double lklRatioOfAvailable = (double) dto3.getLklAmount() / dto3.getCanSeparateAmount() * 100; - double mchRatioOfAvailable = (double) dto3.getMchAmount() / dto3.getCanSeparateAmount() * 100; - double platRatioOfAvailable = (double) dto3.getPlatAmount() / dto3.getCanSeparateAmount() * 100; - double agent1stRatioOfAvailable = (double) dto3.getAgent1stAmount() / dto3.getCanSeparateAmount() * 100; - double agent2ndRatioOfAvailable = (double) dto3.getAgent2ndAmount() / dto3.getCanSeparateAmount() * 100; + // 安全检查,避免空指针异常 + if (dto7.getAgent1stAmount() != null) { + agent1stRatioOfTotal = (double) dto7.getAgent1stAmount() / dto7.getTotalSeparateAmount() * 100; + } + if (dto7.getAgent2ndAmount() != null) { + agent2ndRatioOfTotal = (double) dto7.getAgent2ndAmount() / dto7.getTotalSeparateAmount() * 100; + } + + double lklRatioOfAvailable = (double) dto7.getLklAmount() / dto7.getCanSeparateAmount() * 100; + double mchRatioOfAvailable = (double) dto7.getMchAmount() / dto7.getCanSeparateAmount() * 100; + double platRatioOfAvailable = (double) dto7.getPlatAmount() / dto7.getCanSeparateAmount() * 100; + double agent1stRatioOfAvailable = 0.0; + double agent2ndRatioOfAvailable = 0.0; + + // 安全检查,避免空指针异常 + if (dto7.getAgent1stAmount() != null) { + agent1stRatioOfAvailable = (double) dto7.getAgent1stAmount() / dto7.getCanSeparateAmount() * 100; + } + if (dto7.getAgent2ndAmount() != null) { + agent2ndRatioOfAvailable = (double) dto7.getAgent2ndAmount() / dto7.getCanSeparateAmount() * 100; + } logger.info("占比详情 (总金额占比 / 可分账金额占比): 拉卡拉={}% / {}%, 商户={}% / {}%, 平台={}% / {}%, 一级代理商={}% / {}%, 二级代理商={}% / {}%", String.format("%.4f", lklRatioOfTotal), String.format("%.4f", lklRatioOfAvailable), @@ -218,96 +197,124 @@ public class LklSeparateDTO implements java.io.Serializable { String.format("%.4f", agent1stRatioOfTotal), String.format("%.4f", agent1stRatioOfAvailable), String.format("%.4f", agent2ndRatioOfTotal), String.format("%.4f", agent2ndRatioOfAvailable)); - int total = dto3.getLklAmount() + dto3.getMchAmount() + dto3.getPlatAmount() + dto3.getAgent1stAmount() + dto3.getAgent2ndAmount() + dto3.getShippingFee(); - logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto3.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto3.getTotalSeparateAmount()); - } + int total = dto7.getLklAmount() + dto7.getMchAmount() + dto7.getPlatAmount() + + (dto7.getAgent1stAmount() != null ? dto7.getAgent1stAmount() : 0) + + (dto7.getAgent2ndAmount() != null ? dto7.getAgent2ndAmount() : 0) + dto7.getShippingFee(); + logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto7.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto7.getTotalSeparateAmount()); - // 临界点测试用例4: 大金额测试 - logger.info("\n\n临界点测试用例4 - 大金额测试:"); - LklSeparateDTO dto4 = new LklSeparateDTO(); - dto4.setTotalSeparateAmount(10000); - dto4.setShippingFee(500); - dto4.setLklRatio(new BigDecimal("0.0025")); - dto4.setMchRatio(new BigDecimal("0.94")); - dto4.setPlatRatio(new BigDecimal("0.06")); - - logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}", - dto4.getTotalSeparateAmount(), dto4.getShippingFee(), dto4.getLklRatio(), - dto4.getMchRatio(), dto4.getPlatRatio()); - - boolean result4 = dto4.calculateProfitSharing(); - logger.info("分账计算结果: {}", (result4 ? "成功" : "失败")); - if (result4) { - logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分", - dto4.getTotalSeparateAmount(), dto4.getLklAmount(), dto4.getShippingFee(), dto4.getCanSeparateAmount(), - dto4.getMchAmount(), dto4.getPlatAmount(), dto4.getAgent1stAmount(), dto4.getAgent2ndAmount()); - - double lklRatioOfTotal = (double) dto4.getLklAmount() / dto4.getTotalSeparateAmount() * 100; - double mchRatioOfTotal = (double) dto4.getMchAmount() / dto4.getTotalSeparateAmount() * 100; - double platRatioOfTotal = (double) dto4.getPlatAmount() / dto4.getTotalSeparateAmount() * 100; - double agent1stRatioOfTotal = (double) dto4.getAgent1stAmount() / dto4.getTotalSeparateAmount() * 100; - double agent2ndRatioOfTotal = (double) dto4.getAgent2ndAmount() / dto4.getTotalSeparateAmount() * 100; - - double lklRatioOfAvailable = (double) dto4.getLklAmount() / dto4.getCanSeparateAmount() * 100; - double mchRatioOfAvailable = (double) dto4.getMchAmount() / dto4.getCanSeparateAmount() * 100; - double platRatioOfAvailable = (double) dto4.getPlatAmount() / dto4.getCanSeparateAmount() * 100; - double agent1stRatioOfAvailable = (double) dto4.getAgent1stAmount() / dto4.getCanSeparateAmount() * 100; - double agent2ndRatioOfAvailable = (double) dto4.getAgent2ndAmount() / dto4.getCanSeparateAmount() * 100; - - logger.info("占比详情 (总金额占比 / 可分账金额占比): 拉卡拉={}% / {}%, 商户={}% / {}%, 平台={}% / {}%, 一级代理商={}% / {}%, 二级代理商={}% / {}%", - String.format("%.4f", lklRatioOfTotal), String.format("%.4f", lklRatioOfAvailable), - String.format("%.4f", mchRatioOfTotal), String.format("%.4f", mchRatioOfAvailable), - String.format("%.4f", platRatioOfTotal), String.format("%.4f", platRatioOfAvailable), - String.format("%.4f", agent1stRatioOfTotal), String.format("%.4f", agent1stRatioOfAvailable), - String.format("%.4f", agent2ndRatioOfTotal), String.format("%.4f", agent2ndRatioOfAvailable)); - - int total = dto4.getLklAmount() + dto4.getMchAmount() + dto4.getPlatAmount() + dto4.getAgent1stAmount() + dto4.getAgent2ndAmount() + dto4.getShippingFee(); - logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto4.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto4.getTotalSeparateAmount()); + // 验证refCanSeparateAmount是否生效 + if (dto7.getCanSeparateAmount().equals(dto7.getRefCanSeparateAmount())) { + logger.info("参考可分账金额生效: 计算出的可分账金额={}分, 参考可分账金额={}分", dto7.getCanSeparateAmount(), dto7.getRefCanSeparateAmount()); + } else { + logger.info("参考可分账金额未生效: 计算出的可分账金额={}分, 参考可分账金额={}分", dto7.getCanSeparateAmount(), dto7.getRefCanSeparateAmount()); + } } } /** - * 根据实体类的属性,扣除拉卡拉手续费,配送费之后,从可用余额中进行分账计算 + * 基于可分账金额的分账计算方法(默认) + *
+ * 计算逻辑: + * 1. 计算拉卡拉分账金额 = 总分账金额 × 拉卡拉分账比例(向上取整) + * 2. 计算可分账金额 = 总分账金额 - 配送费 - 拉卡拉分账金额 + * 3. 如果refCanSeparateAmount有效且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账基数 + * 4. 调整分账比例,确保有效比例之和等于1.0 + * 5. 按优先级计算各参与方分账金额(商户 > 平台 > 一级代理商 > 二级代理商) + * 6. 确保总额平衡并保障各参与方分账比例 * * @return 分账结果是否成功 */ - public boolean calculateProfitSharing() { + public boolean calcOnCanAmount() { // 检查前提条件 if (!validateInputs()) { return false; } - logger.info("开始分账计算,分账总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); + logger.info("开始基于可分账金额的分账计算,总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); - // 1. 计算拉卡拉分账金额 = 分账总金额 × 拉卡拉分账比例(向上取整) + // 计算拉卡拉分账金额 calculateLklAmount(); - // 2. 计算可分账金额 = 分账总金额 - 配送费 - 拉卡拉分账金额 + // 计算可分账金额 calculateCanSeparateAmount(); if (canSeparateAmount <= 0) { logger.info("分账计算失败:可分账金额必须大于0,当前值={}", canSeparateAmount); return false; } - // 3. 调整分账比例,确保有效值比例相加等于1.0 + // 检查并应用参考可分账金额 + checkAndApplyRefCanSeparateAmount(); + + // 调整分账比例 adjustRatios(); - // 4. 按优先级分账:商户 > 平台 > 一级代理商 > 二级代理商 + // 按优先级分账 performSeparate(); - // 5. 确保总额平衡 + // 确保总额平衡 balanceAmounts(); - // 6. 保障各参与方分账比例不低于指定值 + // 保障各参与方分账比例 guaranteeRatios(); - logger.info("分账计算完成:商户={},平台={},一级代理商={},二级代理商={}", - mchAmount, platAmount, agent1stAmount, agent2ndAmount); + logger.info("基于可分账金额的分账计算完成:拉卡拉={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分, 商户={}分", + lklAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); return true; } /** - * 验证输入参数 + * 基于总金额的分账计算方法 + *
+ * 计算逻辑: + * 1. 计算拉卡拉分账金额 = 总分账金额 × 拉卡拉分账比例(四舍五入保留两位小数) + * 2. 计算可分账金额 = 总分账金额 - 配送费 - 拉卡拉分账金额 + * 3. 如果refCanSeparateAmount有效且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账基数 + * 4. 调整分账比例,确保有效比例之和等于1.0 + * 5. 按优先级计算各参与方分账金额(平台 > 一级代理商 > 二级代理商 > 商户),基于可分账金额进行分账 + * 6. 确保总额平衡并保障各参与方分账比例 + * + * @return 分账结果是否成功 + */ + public boolean calcOnTotalAmount() { + // 检查前提条件 + if (!validateInputsForTotalAmount()) { + return false; + } + + logger.info("开始基于总金额的分账计算,总金额={}分,配送费={}分", totalSeparateAmount, shippingFee); + + // 计算拉卡拉分账金额 + calculateLklAmount(); + + // 计算可分账金额 + calculateCanSeparateAmount(); + if (canSeparateAmount <= 0) { + logger.info("基于总金额的分账计算失败:可分账金额必须大于0,当前值={}", canSeparateAmount); + return false; + } + + // 调整分账比例 + adjustRatios(); + + // 检查并应用参考可分账金额 + checkAndApplyRefCanSeparateAmount(); + + // 按优先级分账(基于总金额) + performSeparateBasedOnTotalAmount(); + + // 确保总额平衡 + balanceAmounts(); + + // 保障各参与方分账比例(忽略商户比例保障) + guaranteeRatiosForTotalAmount(); + + logger.info("基于总金额的分账计算完成:拉卡拉={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分, 商户={}分", + lklAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); + return true; + } + + /** + * 验证基于可分账金额分账的输入参数 */ private boolean validateInputs() { if (totalSeparateAmount == null || totalSeparateAmount <= 0) { @@ -338,13 +345,22 @@ public class LklSeparateDTO implements java.io.Serializable { return true; } + /** + * 验证基于总金额分账的输入参数 + */ + private boolean validateInputsForTotalAmount() { + // 基于总金额的分账计算参数验证与基于可分账金额的验证相同 + return validateInputs(); + } + /** * 计算拉卡拉分账金额 */ private void calculateLklAmount() { BigDecimal lklAmountDecimal = new BigDecimal(totalSeparateAmount).multiply(lklRatio); - lklAmount = lklAmountDecimal.setScale(0, RoundingMode.UP).intValue(); - logger.debug("拉卡拉分账计算:分账总金额{} × 拉卡拉比例{} = {},向上取整后={}", totalSeparateAmount, lklRatio, lklAmountDecimal, lklAmount); + lklAmount = lklAmountDecimal.setScale(2, RoundingMode.HALF_UP).intValue(); + logger.debug("拉卡拉分账计算:总金额{} × 拉卡拉比例{} = {},四舍五入保留两位小数后={}", + totalSeparateAmount, lklRatio, lklAmountDecimal, lklAmount); } /** @@ -352,7 +368,59 @@ public class LklSeparateDTO implements java.io.Serializable { */ private void calculateCanSeparateAmount() { canSeparateAmount = totalSeparateAmount - shippingFee - lklAmount; - logger.debug("可分账金额计算:分账总金额{} - 配送费{} - 拉卡拉分账{} = {}", totalSeparateAmount, shippingFee, lklAmount, canSeparateAmount); + logger.debug("可分账金额计算:总金额{} - 配送费{} - 拉卡拉分账{} = {}", + totalSeparateAmount, shippingFee, lklAmount, canSeparateAmount); + } + + /** + * 检查并应用参考可分账金额 + * 当refCanSeparateAmount有效且与计算出的canSeparateAmount不一致时, + * 将差额分配给指定优先级的分账对象(平台 > 一级代理商 > 二级代理商 > 商户) + */ + private void checkAndApplyRefCanSeparateAmount() { + // 判断refCanSeparateAmount是否有效(大于0且小于等于totalSeparateAmount) + if (refCanSeparateAmount != null && refCanSeparateAmount > 0 && refCanSeparateAmount <= totalSeparateAmount) { + // 比较refCanSeparateAmount与计算出的canSeparateAmount + if (!refCanSeparateAmount.equals(canSeparateAmount)) { + int difference = refCanSeparateAmount - canSeparateAmount; + logger.info("检测到refCanSeparateAmount与计算出的canSeparateAmount不一致,差额={}分。" + + "refCanSeparateAmount={}分,计算出的canSeparateAmount={}分。", + difference, refCanSeparateAmount, canSeparateAmount); + + // 使用refCanSeparateAmount作为分账基数 + canSeparateAmount = refCanSeparateAmount; + + // 将差额分配给指定优先级的分账对象(商户 > 二级代理商 > 一级代理商 > 平台) + 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); + } } /** @@ -363,8 +431,8 @@ public class LklSeparateDTO implements java.io.Serializable { BigDecimal effectiveRatioSum = BigDecimal.ZERO .add(mchRatio) .add(platRatio) - .add(getOrDefault(agent1stRatio, BigDecimal.ZERO)) - .add(getOrDefault(agent2ndRatio, BigDecimal.ZERO)); + .add(agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO) + .add(agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO); logger.debug("调整前各分账比例:商户={},平台={},一级代理商={},二级代理商={},有效比例总和={}", mchRatio, platRatio, agent1stRatio, agent2ndRatio, effectiveRatioSum); @@ -387,7 +455,7 @@ public class LklSeparateDTO implements java.io.Serializable { } /** - * 按优先级进行分账 + * 按优先级进行分账(基于可分账金额) */ private void performSeparate() { // 商户分账金额 = 可分账金额 × 商户分账比例(四舍五入) @@ -419,6 +487,39 @@ public class LklSeparateDTO implements java.io.Serializable { } } + /** + * 按优先级进行分账(基于总金额) + */ + private void performSeparateBasedOnTotalAmount() { + // 平台分账金额 = 可分账金额 × 平台分账比例(四舍五入) + BigDecimal platAmountDecimal = new BigDecimal(canSeparateAmount).multiply(platRatio); + platAmount = platAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("平台分账计算:可分账金额{} × 平台比例{} = {},四舍五入后={}", canSeparateAmount, platRatio, platAmountDecimal, platAmount); + + // 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(四舍五入) + if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal agent1stAmountDecimal = new BigDecimal(canSeparateAmount).multiply(agent1stRatio); + agent1stAmount = agent1stAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("一级代理商分账计算:可分账金额{} × 一级代理商比例{} = {},四舍五入后={}", canSeparateAmount, agent1stRatio, agent1stAmountDecimal, agent1stAmount); + } else { + agent1stAmount = 0; + } + + // 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(四舍五入) + if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { + BigDecimal agent2ndAmountDecimal = new BigDecimal(canSeparateAmount).multiply(agent2ndRatio); + agent2ndAmount = agent2ndAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue(); + logger.debug("二级代理商分账计算:可分账金额{} × 二级代理商比例{} = {},四舍五入后={}", canSeparateAmount, agent2ndRatio, agent2ndAmountDecimal, agent2ndAmount); + } else { + agent2ndAmount = 0; + } + + // 商户分账金额 = 可分账金额 - 平台分账金额 - 一级代理商分账金额 - 二级代理商分账金额 + mchAmount = canSeparateAmount - platAmount - agent1stAmount - agent2ndAmount; + logger.debug("商户分账计算:可分账金额{} - 平台分账{} - 一级代理商分账{} - 二级代理商分账{} = {}", + canSeparateAmount, platAmount, agent1stAmount, agent2ndAmount, mchAmount); + } + /** * 确保总额平衡 */ @@ -429,7 +530,10 @@ public class LklSeparateDTO implements java.io.Serializable { logger.debug("分账总额与可分账金额不符,差额={},开始调整", diff); // 将差额分配给优先级最低的参与方,确保总额平衡 - if (agent2ndAmount > 0 && agent2ndAmount + diff >= 0) { + 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) { @@ -438,13 +542,24 @@ public class LklSeparateDTO implements java.io.Serializable { } else if (platAmount + diff >= 0) { platAmount += diff; logger.debug("调整平台分账金额:{} + {} = {}", platAmount - diff, diff, platAmount); - } else { - mchAmount += diff; - logger.debug("调整商户分账金额:{} + {} = {}", mchAmount - diff, diff, mchAmount); } } } + /** + * 保障各参与方分账比例不低于指定值(基于总金额的分账计算专用) + * 忽略商户比例保障,只保障平台、一级代理商、二级代理商的比例 + */ + private void guaranteeRatiosForTotalAmount() { + // 平台优先级最高 + guaranteePlatformRatio(); + + // 一级代理商优先级次之 + guaranteeAgent1stRatio(); + + // 不保障商户比例,商户只分到剩余余额 + } + /** * 保障各参与方分账比例不低于指定值 */