feat(settlement): 更新分销结算逻辑支持固定金额和百分比两种佣金类型

- 替换 CommissionRateConfig 为 CommissionConfig,支持 commissionType 字段区分固定金额和百分比模式
- 新增 calcMoneyByCommissionType 方法,根据 commissionType 计算佣金金额
- 修改 findOrderSingleGoods 为 findOrderSingleGoodsInfo,返回商品信息和数量
- 更新日志输出格式,显示商品数量和佣金类型信息
- 调整信用分销商佣金方法参数,传递商品数量和佣金配置对象
- 新增 OrderGoodsInfo 和 CommissionConfig 内部类定义
- 实现固定金额模式按件计算佣金的逻辑
- 添加安全数值处理方法 safeValue 和 safePositive
- 更新佣金注释构建方法,显示佣金类型和具体数值信息
This commit is contained in:
2026-01-28 15:11:22 +08:00
parent fa53fd399f
commit 9e5c5ceab3

View File

@@ -151,32 +151,37 @@ public class DealerOrderSettlement10584Task {
throw new IllegalStateException("订单金额为空或<=0无法结算 - orderId=" + order.getOrderId() + ", orderNo=" + order.getOrderNo());
}
ShopGoods goods = findOrderSingleGoods(order);
OrderGoodsInfo goodsInfo = findOrderSingleGoodsInfo(order);
ShopGoods goods = goodsInfo.goods;
int goodsQty = goodsInfo.quantity;
if (goods != null && goods.getIsOpenCommission() != null && goods.getIsOpenCommission() != 1) {
log.info("商品未开启分销,跳过订单结算 - orderId={}, orderNo={}, buyerUserId={}, goodsId={}, isOpenCommission={}",
order.getOrderId(), order.getOrderNo(), order.getUserId(), goods.getGoodsId(), goods.getIsOpenCommission());
return;
}
CommissionRateConfig rateConfig = resolveCommissionRateConfig(goods);
CommissionConfig commissionConfig = resolveCommissionConfig(goods);
log.info("开始结算订单 - orderId={}, orderNo={}, buyerUserId={}, payPrice={}, goodsId={}, rates[dealer1={}, dealer2={}, dealer3={}, div1={}, div2={}]",
log.info("开始结算订单 - orderId={}, orderNo={}, buyerUserId={}, payPrice={}, goodsId={}, goodsQty={}, commissionType={}, cfg[dealer1={}, dealer2={}, dealer3={}, div1={}, div2={}]",
order.getOrderId(),
order.getOrderNo(),
order.getUserId(),
baseAmount,
goods != null ? goods.getGoodsId() : null,
rateConfig.dealerDirectRate,
rateConfig.dealerSimpleRate,
rateConfig.dealerThirdRate,
rateConfig.storeDirectRate,
rateConfig.storeSimpleRate);
goodsQty,
commissionConfig.commissionType,
commissionConfig.dealerDirectValue,
commissionConfig.dealerSimpleValue,
commissionConfig.dealerThirdValue,
commissionConfig.storeDirectValue,
commissionConfig.storeSimpleValue);
// 1) 直推/间推shop_dealer_referee
DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount, rateConfig);
DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount, goodsQty, commissionConfig);
// 2) 门店分红上级:从下单用户开始逐级向上找,命中 ShopDealerUser.type=1 的最近两级(直推门店/间推门店)。
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount, rateConfig, level1ParentCache, shopRoleCache);
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount, goodsQty, commissionConfig, level1ParentCache, shopRoleCache);
// 3) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
createDealerOrderRecord(order, baseAmount, dealerRefereeCommission, shopRoleCommission);
@@ -184,7 +189,7 @@ public class DealerOrderSettlement10584Task {
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
}
private DealerRefereeCommission settleDealerRefereeCommission(ShopOrder order, BigDecimal baseAmount, CommissionRateConfig rateConfig) {
private DealerRefereeCommission settleDealerRefereeCommission(ShopOrder order, BigDecimal baseAmount, int goodsQty, CommissionConfig commissionConfig) {
// 兼容两种数据形态:
// 1) 同一 userId 下有 level=1/2 的多级关系(直接按 level 取);
// 2) 仅维护 level=1用“查两次”回退获取上级
@@ -198,18 +203,39 @@ public class DealerOrderSettlement10584Task {
thirdDealerId = getDealerRefereeId(simpleDealerId, 1);
}
BigDecimal directMoney = directDealerId != null ? calcMoney(baseAmount, rateConfig.dealerDirectRate) : BigDecimal.ZERO;
BigDecimal directMoney =
directDealerId != null ? calcMoneyByCommissionType(baseAmount, commissionConfig.dealerDirectValue, goodsQty, 2, commissionConfig.commissionType) : BigDecimal.ZERO;
// 允许同一条线内同一个人同时拿到“直推 + 间推”(即使 directDealerId == simpleDealerId 也照常发放两笔)
BigDecimal simpleMoney = simpleDealerId != null ? calcMoney(baseAmount, rateConfig.dealerSimpleRate) : BigDecimal.ZERO;
BigDecimal thirdMoney = thirdDealerId != null ? calcMoney(baseAmount, rateConfig.dealerThirdRate) : BigDecimal.ZERO;
BigDecimal simpleMoney =
simpleDealerId != null ? calcMoneyByCommissionType(baseAmount, commissionConfig.dealerSimpleValue, goodsQty, 2, commissionConfig.commissionType) : BigDecimal.ZERO;
BigDecimal thirdMoney =
thirdDealerId != null ? calcMoneyByCommissionType(baseAmount, commissionConfig.dealerThirdValue, goodsQty, 2, commissionConfig.commissionType) : BigDecimal.ZERO;
log.info("分销直推/间推/第3级查询结果 - orderNo={}, buyerUserId={}, directDealerId={}, directMoney={}, simpleDealerId={}, simpleMoney={}, thirdDealerId={}, thirdMoney={}",
order.getOrderNo(), order.getUserId(), directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);
// 直推:对方=买家;推荐奖(5%):对方=直推分销商(便于在资金明细中看出“来自哪个下级分销商/团队订单”)
creditDealerCommission(directDealerId, directMoney, order, order.getUserId(), "直推佣金(rate=" + rateConfig.dealerDirectRate + ")");
creditDealerCommission(simpleDealerId, simpleMoney, order, directDealerId, "推荐奖(rate=" + rateConfig.dealerSimpleRate + ")");
creditDealerCommission(thirdDealerId, thirdMoney, order, simpleDealerId, "第3级佣金(rate=" + rateConfig.dealerThirdRate + ")");
creditDealerCommission(
directDealerId,
directMoney,
order,
order.getUserId(),
buildCommissionComment("直推佣金", commissionConfig.commissionType, commissionConfig.dealerDirectValue, goodsQty)
);
creditDealerCommission(
simpleDealerId,
simpleMoney,
order,
directDealerId,
buildCommissionComment("推荐奖", commissionConfig.commissionType, commissionConfig.dealerSimpleValue, goodsQty)
);
creditDealerCommission(
thirdDealerId,
thirdMoney,
order,
simpleDealerId,
buildCommissionComment("第3级佣金", commissionConfig.commissionType, commissionConfig.dealerThirdValue, goodsQty)
);
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);
}
@@ -238,7 +264,8 @@ public class DealerOrderSettlement10584Task {
private ShopRoleCommission settleShopRoleRefereeCommission(
ShopOrder order,
BigDecimal baseAmount,
CommissionRateConfig rateConfig,
int goodsQty,
CommissionConfig commissionConfig,
Map<Integer, Integer> level1ParentCache,
Map<Integer, Boolean> shopRoleCache
) {
@@ -251,21 +278,48 @@ public class DealerOrderSettlement10584Task {
if (shopRoleReferees.size() == 1) {
// 仅找到一个门店:按(直推+间推)汇总发放
BigDecimal singleStoreRate = safeRate(rateConfig.storeDirectRate).add(safeRate(rateConfig.storeSimpleRate));
BigDecimal money = calcDividendMoney(baseAmount, singleStoreRate);
log.info("分红发放(仅1门店) - orderNo={}, firstDividendUserId={}, rate={}, money={}",
order.getOrderNo(), shopRoleReferees.get(0), singleStoreRate, money);
creditDealerCommission(shopRoleReferees.get(0), money, order, order.getUserId(), "门店直推佣金(仅1门店,rate=" + singleStoreRate + ")");
BigDecimal singleStoreValue = safeValue(commissionConfig.storeDirectValue).add(safeValue(commissionConfig.storeSimpleValue));
BigDecimal money = calcMoneyByCommissionType(baseAmount, singleStoreValue, goodsQty, DIVIDEND_SCALE, commissionConfig.commissionType);
log.info("分红发放(仅1门店) - orderNo={}, firstDividendUserId={}, commissionType={}, value={}, money={}",
order.getOrderNo(), shopRoleReferees.get(0), commissionConfig.commissionType, singleStoreValue, money);
creditDealerCommission(
shopRoleReferees.get(0),
money,
order,
order.getUserId(),
buildCommissionComment("门店直推佣金(仅1门店)", commissionConfig.commissionType, singleStoreValue, goodsQty)
);
return new ShopRoleCommission(shopRoleReferees.get(0), money, null, BigDecimal.ZERO);
}
// 两个或以上:按配置分别发放
BigDecimal storeDirectMoney = calcDividendMoney(baseAmount, rateConfig.storeDirectRate);
BigDecimal storeSimpleMoney = calcDividendMoney(baseAmount, rateConfig.storeSimpleRate);
log.info("分红发放(2人) - orderNo={}, firstDividendUserId={}, firstDividendRate={}, firstDividendMoney={}, secondDividendUserId={}, secondDividendRate={}, secondDividendMoney={}",
order.getOrderNo(), shopRoleReferees.get(0), rateConfig.storeDirectRate, storeDirectMoney, shopRoleReferees.get(1), rateConfig.storeSimpleRate, storeSimpleMoney);
creditDealerCommission(shopRoleReferees.get(0), storeDirectMoney, order, order.getUserId(), "门店直推佣金(rate=" + rateConfig.storeDirectRate + ")");
creditDealerCommission(shopRoleReferees.get(1), storeSimpleMoney, order, order.getUserId(), "门店间推佣金(rate=" + rateConfig.storeSimpleRate + ")");
BigDecimal storeDirectMoney =
calcMoneyByCommissionType(baseAmount, commissionConfig.storeDirectValue, goodsQty, DIVIDEND_SCALE, commissionConfig.commissionType);
BigDecimal storeSimpleMoney =
calcMoneyByCommissionType(baseAmount, commissionConfig.storeSimpleValue, goodsQty, DIVIDEND_SCALE, commissionConfig.commissionType);
log.info("分红发放(2人) - orderNo={}, firstDividendUserId={}, commissionType={}, firstValue={}, firstMoney={}, secondDividendUserId={}, secondValue={}, secondMoney={}",
order.getOrderNo(),
shopRoleReferees.get(0),
commissionConfig.commissionType,
commissionConfig.storeDirectValue,
storeDirectMoney,
shopRoleReferees.get(1),
commissionConfig.storeSimpleValue,
storeSimpleMoney);
creditDealerCommission(
shopRoleReferees.get(0),
storeDirectMoney,
order,
order.getUserId(),
buildCommissionComment("门店直推佣金", commissionConfig.commissionType, commissionConfig.storeDirectValue, goodsQty)
);
creditDealerCommission(
shopRoleReferees.get(1),
storeSimpleMoney,
order,
order.getUserId(),
buildCommissionComment("门店间推佣金", commissionConfig.commissionType, commissionConfig.storeSimpleValue, goodsQty)
);
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
}
@@ -549,26 +603,34 @@ public class DealerOrderSettlement10584Task {
return order.getPayPrice();
}
private BigDecimal calcMoney(BigDecimal base, BigDecimal rate) {
if (base == null || rate == null) {
private BigDecimal calcMoneyByCommissionType(BigDecimal baseAmount, BigDecimal value, int goodsQty, int scale, Integer commissionType) {
if (value == null || value.signum() <= 0) {
return BigDecimal.ZERO;
}
return base.multiply(rate).setScale(2, RoundingMode.HALF_UP);
int qty = Math.max(1, goodsQty);
// 10: 固定金额(按件)20: 百分比(按订单金额)
if (commissionType != null && commissionType == 10) {
return value.multiply(BigDecimal.valueOf(qty)).setScale(scale, RoundingMode.HALF_UP);
}
return baseAmount != null ? baseAmount.multiply(value).setScale(scale, RoundingMode.HALF_UP) : BigDecimal.ZERO;
}
private BigDecimal calcDividendMoney(BigDecimal base, BigDecimal rate) {
if (base == null || rate == null) {
return BigDecimal.ZERO;
private String buildCommissionComment(String label, Integer commissionType, BigDecimal value, int goodsQty) {
if (commissionType != null && commissionType == 10) {
return label + "(type=amount,amount=" + value + ",qty=" + Math.max(1, goodsQty) + ")";
}
return base.multiply(rate).setScale(DIVIDEND_SCALE, RoundingMode.HALF_UP);
return label + "(type=rate,rate=" + value + ")";
}
private ShopGoods findOrderSingleGoods(ShopOrder order) {
private OrderGoodsInfo findOrderSingleGoodsInfo(ShopOrder order) {
if (order == null) {
return null;
return new OrderGoodsInfo(null, 1);
}
Integer goodsId = order.getFormId();
Integer totalNum = order.getTotalNum();
if (goodsId == null) {
ShopOrderGoods orderGoods = shopOrderGoodsService.getOne(
new LambdaQueryWrapper<ShopOrderGoods>()
@@ -578,74 +640,126 @@ public class DealerOrderSettlement10584Task {
.last("limit 1")
);
goodsId = orderGoods != null ? orderGoods.getGoodsId() : null;
if (totalNum == null && orderGoods != null) {
totalNum = orderGoods.getTotalNum();
}
}
int qty = totalNum != null && totalNum > 0 ? totalNum : 1;
if (goodsId == null) {
return null;
return new OrderGoodsInfo(null, qty);
}
return shopGoodsService.getOne(
ShopGoods goods = shopGoodsService.getOne(
new LambdaQueryWrapper<ShopGoods>()
.eq(ShopGoods::getTenantId, TENANT_ID)
.eq(ShopGoods::getGoodsId, goodsId)
.last("limit 1")
);
return new OrderGoodsInfo(goods, qty);
}
private CommissionConfig resolveCommissionConfig(ShopGoods goods) {
// commissionType10固定金额20百分比默认/兼容历史)
Integer commissionType = goods != null && goods.getCommissionType() != null ? goods.getCommissionType() : 20;
if (commissionType != 10 && commissionType != 20) {
commissionType = 20;
}
private CommissionRateConfig resolveCommissionRateConfig(ShopGoods goods) {
if (goods == null) {
// 无商品信息:回退旧逻辑
return new CommissionRateConfig(RATE_0_10, RATE_0_05, BigDecimal.ZERO, RATE_0_02, RATE_0_01);
// 无商品信息:回退旧逻辑(按比率)
return CommissionConfig.defaultRate();
}
boolean anyConfigured =
(goods.getFirstMoney() != null && goods.getFirstMoney().signum() > 0)
|| (goods.getSecondMoney() != null && goods.getSecondMoney().signum() > 0)
|| (goods.getThirdMoney() != null && goods.getThirdMoney().signum() > 0)
|| (goods.getFirstDividend() != null && goods.getFirstDividend().signum() > 0)
|| (goods.getSecondDividend() != null && goods.getSecondDividend().signum() > 0);
if (!anyConfigured) {
// 商品未配置新分润字段回退旧逻辑避免历史数据字段默认0导致“全不结算”
return new CommissionRateConfig(RATE_0_10, RATE_0_05, BigDecimal.ZERO, RATE_0_02, RATE_0_01);
if (commissionType == 10) {
// 固定金额模式:字段值>0按金额结算(按件)未配置则视为0。
return new CommissionConfig(
commissionType,
safePositive(goods.getFirstMoney()),
safePositive(goods.getSecondMoney()),
safePositive(goods.getThirdMoney()),
safePositive(goods.getFirstDividend()),
safePositive(goods.getSecondDividend())
);
}
// 商品配置了新分润字段:只按“配置值>0”的比例结算未配置则视为0由运营侧完全控制
BigDecimal dealerDirectRate = safeRatePositive(goods.getFirstMoney());
BigDecimal dealerSimpleRate = safeRatePositive(goods.getSecondMoney());
BigDecimal dealerThirdRate = safeRatePositive(goods.getThirdMoney());
BigDecimal storeDirectRate = safeRatePositive(goods.getFirstDividend());
BigDecimal storeSimpleRate = safeRatePositive(goods.getSecondDividend());
return new CommissionRateConfig(dealerDirectRate, dealerSimpleRate, dealerThirdRate, storeDirectRate, storeSimpleRate);
// 比率模式:默认用旧规则;商品字段值>0则覆盖默认比率。
BigDecimal dealerDirectValue = RATE_0_10;
BigDecimal dealerSimpleValue = RATE_0_05;
BigDecimal dealerThirdValue = BigDecimal.ZERO;
BigDecimal storeDirectValue = RATE_0_02;
BigDecimal storeSimpleValue = RATE_0_01;
if (safePositive(goods.getFirstMoney()).signum() > 0) {
dealerDirectValue = goods.getFirstMoney();
}
if (safePositive(goods.getSecondMoney()).signum() > 0) {
dealerSimpleValue = goods.getSecondMoney();
}
if (safePositive(goods.getThirdMoney()).signum() > 0) {
dealerThirdValue = goods.getThirdMoney();
}
if (safePositive(goods.getFirstDividend()).signum() > 0) {
storeDirectValue = goods.getFirstDividend();
}
if (safePositive(goods.getSecondDividend()).signum() > 0) {
storeSimpleValue = goods.getSecondDividend();
}
private BigDecimal safeRate(BigDecimal rate) {
return rate != null ? rate : BigDecimal.ZERO;
return new CommissionConfig(
commissionType,
dealerDirectValue,
dealerSimpleValue,
dealerThirdValue,
storeDirectValue,
storeSimpleValue
);
}
private BigDecimal safeRatePositive(BigDecimal rate) {
return rate != null && rate.signum() > 0 ? rate : BigDecimal.ZERO;
private BigDecimal safeValue(BigDecimal v) {
return v != null ? v : BigDecimal.ZERO;
}
private static class CommissionRateConfig {
private final BigDecimal dealerDirectRate;
private final BigDecimal dealerSimpleRate;
private final BigDecimal dealerThirdRate;
private final BigDecimal storeDirectRate;
private final BigDecimal storeSimpleRate;
private BigDecimal safePositive(BigDecimal v) {
return v != null && v.signum() > 0 ? v : BigDecimal.ZERO;
}
private CommissionRateConfig(
BigDecimal dealerDirectRate,
BigDecimal dealerSimpleRate,
BigDecimal dealerThirdRate,
BigDecimal storeDirectRate,
BigDecimal storeSimpleRate
private static class OrderGoodsInfo {
private final ShopGoods goods;
private final int quantity;
private OrderGoodsInfo(ShopGoods goods, int quantity) {
this.goods = goods;
this.quantity = Math.max(1, quantity);
}
}
private static class CommissionConfig {
private final Integer commissionType;
private final BigDecimal dealerDirectValue;
private final BigDecimal dealerSimpleValue;
private final BigDecimal dealerThirdValue;
private final BigDecimal storeDirectValue;
private final BigDecimal storeSimpleValue;
private static CommissionConfig defaultRate() {
return new CommissionConfig(20, RATE_0_10, RATE_0_05, BigDecimal.ZERO, RATE_0_02, RATE_0_01);
}
private CommissionConfig(
Integer commissionType,
BigDecimal dealerDirectValue,
BigDecimal dealerSimpleValue,
BigDecimal dealerThirdValue,
BigDecimal storeDirectValue,
BigDecimal storeSimpleValue
) {
this.dealerDirectRate = dealerDirectRate != null ? dealerDirectRate : BigDecimal.ZERO;
this.dealerSimpleRate = dealerSimpleRate != null ? dealerSimpleRate : BigDecimal.ZERO;
this.dealerThirdRate = dealerThirdRate != null ? dealerThirdRate : BigDecimal.ZERO;
this.storeDirectRate = storeDirectRate != null ? storeDirectRate : BigDecimal.ZERO;
this.storeSimpleRate = storeSimpleRate != null ? storeSimpleRate : BigDecimal.ZERO;
this.commissionType = commissionType != null ? commissionType : 20;
this.dealerDirectValue = dealerDirectValue != null ? dealerDirectValue : BigDecimal.ZERO;
this.dealerSimpleValue = dealerSimpleValue != null ? dealerSimpleValue : BigDecimal.ZERO;
this.dealerThirdValue = dealerThirdValue != null ? dealerThirdValue : BigDecimal.ZERO;
this.storeDirectValue = storeDirectValue != null ? storeDirectValue : BigDecimal.ZERO;
this.storeSimpleValue = storeSimpleValue != null ? storeSimpleValue : BigDecimal.ZERO;
}
}