diff --git a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java index ebd13572..dc6e70d4 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java +++ b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java @@ -98,4 +98,10 @@ public class CommonConstant { public static final String Sep_DeliveryFee_Prefix = "DF_"; public static final String Sep_GoodsFee_Prefix = "ORD_"; + // 分账状态:1-已分账;2-未分账;3-分账已失败; + public static final Integer Sta_Separate_Success = 1; + public static final Integer Sta_Separate_Undone = 2; + public static final Integer Sta_Separate_Fail = 3; + + } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java index 6cd02679..0eacc338 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/ShopOrderLkl.java @@ -90,6 +90,8 @@ public class ShopOrderLkl implements Serializable { private Integer receive_status; + private Integer separate_status; + private Integer status; private Date created_at; 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 new file mode 100644 index 00000000..9cf79d93 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateDTO.java @@ -0,0 +1,579 @@ +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 java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 拉卡拉订单分账信息对象 + *
+ * 分账计算逻辑说明: + * 前提条件:分账总金额必须大于0,配送费不能小于0;拉卡拉、商户、平台分账比例必须大于0, + * 一级、二级代理商分账比例可以为null或小于等于0 + *
+ * 分账计算步骤:
+ * 1. 计算拉卡拉分账金额 = 分账总金额 × 拉卡拉分账比例(结果向上取整)
+ * 2. 计算可分账金额 = 分账总金额 - 配送费 - 拉卡拉分账金额
+ * 3. 调整分账比例,确保商户、平台、一级代理商、二级代理商的有效比例之和等于1.0
+ * - 如果有二级代理商参与分账,则调整二级代理商比例
+ * - 否则如果有一级代理商参与分账,则调整一级代理商比例
+ * - 否则调整平台比例
+ * 4. 按优先级计算各参与方分账金额(结果四舍五入):
+ * - 商户分账金额 = 可分账金额 × 商户分账比例
+ * - 平台分账金额 = 可分账金额 × 平台分账比例
+ * - 一级代理商分账金额 = 可分账金额 × 一级代理商分账比例(如果参与分账)
+ * - 二级代理商分账金额 = 可分账金额 × 二级代理商分账比例(如果参与分账)
+ * 5. 如分配总额与可分账金额不符,则调整最后一个分账方(按优先级顺序)以确保总额平衡:
+ * - 优先调整二级代理商(如果参与分账)
+ * - 否则调整一级代理商(如果参与分账)
+ * - 否则调整平台
+ * - 最后调整商户
+ * 6. 保障各参与方分账比例不能低于指定属性的比例(按优先级处理):
+ * - 商户优先级最高
+ * - 平台优先级次之
+ * - 一级代理商优先级再次之
+ * - 二级代理商优先级最低
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@ApiModel(value = "拉卡拉订单分账信息对象", description = "拉卡拉订单分账信息对象")
+public class LklSeparateDTO implements java.io.Serializable {
+ private static final Logger logger = LoggerFactory.getLogger(LklSeparateDTO.class);
+
+ private static final long serialVersionUID = 1L;
+
+ @ApiModelProperty(value = "分账总金额(分)")
+ private Integer totalSeparateAmount;
+
+ @ApiModelProperty(value = "可分账金额(分)")
+ private Integer canSeparateAmount;
+
+ @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;
+
+ /**
+ * 测试方法
+ */
+ 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();
+ dto2.setTotalSeparateAmount(800);
+ dto2.setShippingFee(500);
+ dto2.setLklRatio(new BigDecimal("0.0025"));
+ dto2.setMchRatio(new BigDecimal("0.94"));
+ dto2.setPlatRatio(new BigDecimal("0.06"));
+
+ logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}",
+ dto2.getTotalSeparateAmount(), dto2.getShippingFee(), dto2.getLklRatio(),
+ dto2.getMchRatio(), dto2.getPlatRatio());
+
+ boolean result2 = dto2.calculateProfitSharing();
+ logger.info("分账计算结果: {}", (result2 ? "成功" : "失败"));
+ if (result2) {
+ logger.info("分账结果: 总金额={}分, 拉卡拉={}分, 配送费={}分, 可分账={}分, 商户={}分, 平台={}分, 一级代理商={}分, 二级代理商={}分",
+ dto2.getTotalSeparateAmount(), dto2.getLklAmount(), dto2.getShippingFee(), dto2.getCanSeparateAmount(),
+ 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 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;
+
+ 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 = dto2.getLklAmount() + dto2.getMchAmount() + dto2.getPlatAmount() + dto2.getAgent1stAmount() + dto2.getAgent2ndAmount() + 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"));
+
+ logger.info("测试参数: 总分账金额={}分, 配送费={}分, 拉卡拉比例={}, 商户比例={}, 平台比例={}",
+ dto3.getTotalSeparateAmount(), dto3.getShippingFee(), dto3.getLklRatio(),
+ dto3.getMchRatio(), dto3.getPlatRatio());
+
+ 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());
+
+ 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 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;
+
+ 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 = dto3.getLklAmount() + dto3.getMchAmount() + dto3.getPlatAmount() + dto3.getAgent1stAmount() + dto3.getAgent2ndAmount() + dto3.getShippingFee();
+ logger.info("金额校验: {} (计算总金额={}分, 原始总金额={}分)", (total == dto3.getTotalSeparateAmount() ? "通过" : "不通过"), total, dto3.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());
+ }
+ }
+
+ /**
+ * 根据实体类的属性,扣除拉卡拉手续费,配送费之后,从可用余额中进行分账计算
+ *
+ * @return 分账结果是否成功
+ */
+ public boolean calculateProfitSharing() {
+ // 检查前提条件
+ if (!validateInputs()) {
+ return false;
+ }
+
+ logger.info("开始分账计算,分账总金额={}分,配送费={}分", totalSeparateAmount, shippingFee);
+
+ // 1. 计算拉卡拉分账金额 = 分账总金额 × 拉卡拉分账比例(向上取整)
+ calculateLklAmount();
+
+ // 2. 计算可分账金额 = 分账总金额 - 配送费 - 拉卡拉分账金额
+ calculateCanSeparateAmount();
+ if (canSeparateAmount <= 0) {
+ logger.info("分账计算失败:可分账金额必须大于0,当前值={}", canSeparateAmount);
+ return false;
+ }
+
+ // 3. 调整分账比例,确保有效值比例相加等于1.0
+ adjustRatios();
+
+ // 4. 按优先级分账:商户 > 平台 > 一级代理商 > 二级代理商
+ performSeparate();
+
+ // 5. 确保总额平衡
+ balanceAmounts();
+
+ // 6. 保障各参与方分账比例不低于指定值
+ guaranteeRatios();
+
+ logger.info("分账计算完成:商户={},平台={},一级代理商={},二级代理商={}",
+ mchAmount, platAmount, agent1stAmount, agent2ndAmount);
+ return true;
+ }
+
+ /**
+ * 验证输入参数
+ */
+ 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 void calculateLklAmount() {
+ BigDecimal lklAmountDecimal = new BigDecimal(totalSeparateAmount).multiply(lklRatio);
+ lklAmount = lklAmountDecimal.setScale(0, RoundingMode.UP).intValue();
+ logger.debug("拉卡拉分账计算:分账总金额{} × 拉卡拉比例{} = {},向上取整后={}", totalSeparateAmount, lklRatio, lklAmountDecimal, lklAmount);
+ }
+
+ /**
+ * 计算可分账金额
+ */
+ private void calculateCanSeparateAmount() {
+ canSeparateAmount = totalSeparateAmount - shippingFee - lklAmount;
+ logger.debug("可分账金额计算:分账总金额{} - 配送费{} - 拉卡拉分账{} = {}", totalSeparateAmount, shippingFee, lklAmount, canSeparateAmount);
+ }
+
+ /**
+ * 调整分账比例,确保有效值比例相加等于1.0
+ */
+ private void adjustRatios() {
+ // 计算有效的分账比例总和
+ BigDecimal effectiveRatioSum = BigDecimal.ZERO
+ .add(mchRatio)
+ .add(platRatio)
+ .add(getOrDefault(agent1stRatio, BigDecimal.ZERO))
+ .add(getOrDefault(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() {
+ // 商户分账金额 = 可分账金额 × 商户分账比例(四舍五入)
+ BigDecimal mchAmountDecimal = new BigDecimal(canSeparateAmount).multiply(mchRatio);
+ mchAmount = mchAmountDecimal.setScale(0, RoundingMode.HALF_UP).intValue();
+ logger.debug("商户分账计算:可分账金额{} × 商户比例{} = {},四舍五入后={}", canSeparateAmount, mchRatio, mchAmountDecimal, mchAmount);
+
+ // 平台分账金额 = 可分账金额 × 平台分账比例(四舍五入)
+ 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;
+ }
+ }
+
+ /**
+ * 确保总额平衡
+ */
+ private void balanceAmounts() {
+ int totalAmount = mchAmount + platAmount + agent1stAmount + agent2ndAmount;
+ if (totalAmount != canSeparateAmount) {
+ int diff = canSeparateAmount - totalAmount;
+ logger.debug("分账总额与可分账金额不符,差额={},开始调整", diff);
+
+ // 将差额分配给优先级最低的参与方,确保总额平衡
+ 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);
+ } else {
+ mchAmount += diff;
+ logger.debug("调整商户分账金额:{} + {} = {}", mchAmount - diff, diff, mchAmount);
+ }
+ }
+ }
+
+ /**
+ * 保障各参与方分账比例不低于指定值
+ */
+ private void guaranteeRatios() {
+ // 商户优先级最高
+ guaranteeMerchantRatio();
+
+ // 平台优先级次之
+ guaranteePlatformRatio();
+
+ // 一级代理商优先级再次之
+ guaranteeAgent1stRatio();
+
+ // 最终验证和调整,确保所有参与方分账比例不低于指定值
+ finalGuaranteeMerchantRatio();
+ }
+
+ /**
+ * 保障商户分账比例
+ */
+ private void guaranteeMerchantRatio() {
+ BigDecimal actualMchRatio = new BigDecimal(mchAmount).divide(new BigDecimal(canSeparateAmount), 6, RoundingMode.HALF_UP);
+ if (actualMchRatio.compareTo(mchRatio) < 0) {
+ int requiredMchAmount = mchRatio.multiply(new BigDecimal(canSeparateAmount)).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() {
+ BigDecimal actualPlatRatio = new BigDecimal(platAmount).divide(new BigDecimal(canSeparateAmount), 6, RoundingMode.HALF_UP);
+ if (actualPlatRatio.compareTo(platRatio) < 0) {
+ int requiredPlatAmount = platRatio.multiply(new BigDecimal(canSeparateAmount)).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() {
+ if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) {
+ BigDecimal actualAgent1stRatio = new BigDecimal(agent1stAmount).divide(new BigDecimal(canSeparateAmount), 6, RoundingMode.HALF_UP);
+ if (actualAgent1stRatio.compareTo(agent1stRatio) < 0) {
+ int requiredAgent1stAmount = agent1stRatio.multiply(new BigDecimal(canSeparateAmount)).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() {
+ BigDecimal finalMchRatio = new BigDecimal(mchAmount).divide(new BigDecimal(canSeparateAmount), 6, RoundingMode.HALF_UP);
+ if (finalMchRatio.compareTo(mchRatio) < 0) {
+ int requiredMchAmount = mchRatio.multiply(new BigDecimal(canSeparateAmount)).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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 获取BigDecimal值,如果为null则返回默认值
+ */
+ private BigDecimal getOrDefault(BigDecimal value, BigDecimal defaultValue) {
+ return value != null ? value : defaultValue;
+ }
+}
\ 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 22ab0e8e..5b7a279d 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
@@ -10,6 +10,7 @@ package com.suisung.mall.shop.lakala.service.impl;
import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@@ -555,8 +556,13 @@ public class LakalaApiServiceImpl implements LakalaApiService {
shopMchEntryService.updateMerchEntryEcResultUrlByMchId(mchId, ecResultUrl);
// 发短信给商家,及时签署合同 SMS_488465246
- // 【小发同城商家】恭喜您的开店入驻申请已审核通过!请尽快登录小发同城商家版APP平台签署电子合同,签署链接24小时内有效(逾期需重新提交申请)。如有疑问请联系客服,感谢您的支持!
- shopMessageTemplateService.aliyunSmsSend(contractMobile, "SMS_493160417", null);//SMS_479760276
+ //
+ //恭喜您的开店入驻申请已审核通过!请尽快登录小发商家版APP平台签署电子合同,签署链接24小时内有效(逾期需重新提交申请)。如有疑问请联系客服,感谢您的支持!
+ // shopMessageTemplateService.aliyunSmsSend(contractMobile, "SMS_493160417", null);//SMS_479760276
+
+ // 恭喜!您的开店入驻申请已审核通过,点击链接 https://mall.gpxscs.cn/api/mobile/shop/lakala/sign/ec/${code}
+ // 或前往小发商家版APP完成电子合同签署,链接24小时内有效(逾期需重新提交申请)。如有疑问可联系客服,感谢您的支持!
+ shopMessageTemplateService.aliyunSmsSend(contractMobile, "SMS_494860064", null);
JSONObject payload = new JSONObject();
payload.put("category", CommonConstant.PUSH_MSG_CATE_EC);
@@ -1737,42 +1743,56 @@ public class LakalaApiServiceImpl implements LakalaApiService {
@Override
public Pair