feat(settlement): 添加总经销商分润功能

- 引入 TOTAL_DEALER_DIVIDEND_RATE 常量用于总经销商分润计算
- 添加 findTotalDealerUserId 方法查找总经销商用户ID
- 新增 settleTotalDealerCommission 方法实现总经销商分润逻辑
- 修改 settleOneOrder 方法传入总经销商用户ID参数
- 更新 createDealerOrderRecord 方法支持总经销商分润记录
- 扩展 buildCommissionTraceComment 方法包含总经销商分润信息
- 添加 TotalDealerCommission 内部类封装总经销商分润数据
- 实现总经销商分润的幂等处理和日志记录功能
This commit is contained in:
2026-02-05 15:43:02 +08:00
parent acc543b50a
commit 1107b9144f

View File

@@ -48,6 +48,7 @@ public class DealerOrderSettlement10584Task {
private static final BigDecimal RATE_0_03 = new BigDecimal("0.03");
private static final BigDecimal RATE_0_02 = new BigDecimal("0.02");
private static final BigDecimal RATE_0_01 = new BigDecimal("0.01");
private static final BigDecimal TOTAL_DEALER_DIVIDEND_RATE = RATE_0_01;
private static final int MAX_ORDERS_PER_RUN = 50;
private static final int MAX_REFEREE_CHAIN_DEPTH = 20;
@@ -95,6 +96,10 @@ public class DealerOrderSettlement10584Task {
// Per-run caches to reduce DB chatter across orders.
Map<Integer, Integer> level1ParentCache = new HashMap<>();
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
Integer totalDealerUserId = findTotalDealerUserId();
if (totalDealerUserId == null) {
log.warn("未找到总经销商账号,订单仍可结算但不会发放总经销商分润 - tenantId={}", TENANT_ID);
}
log.info("租户{}待结算订单数: {}, orderNos(sample)={}",
TENANT_ID,
@@ -108,7 +113,7 @@ public class DealerOrderSettlement10584Task {
if (!claimOrderToSettle(order.getOrderId())) {
return;
}
settleOneOrder(order, level1ParentCache, shopRoleCache);
settleOneOrder(order, level1ParentCache, shopRoleCache, totalDealerUserId);
});
} catch (Exception e) {
log.error("订单结算失败,将回滚本订单并在下次任务重试 - orderId={}, orderNo={}", order.getOrderId(), order.getOrderNo(), e);
@@ -141,7 +146,12 @@ public class DealerOrderSettlement10584Task {
);
}
private void settleOneOrder(ShopOrder order, Map<Integer, Integer> level1ParentCache, Map<Integer, Boolean> shopRoleCache) {
private void settleOneOrder(
ShopOrder order,
Map<Integer, Integer> level1ParentCache,
Map<Integer, Boolean> shopRoleCache,
Integer totalDealerUserId
) {
if (order.getUserId() == null || order.getOrderNo() == null) {
throw new IllegalStateException("订单关键信息缺失,无法结算 - orderId=" + order.getOrderId());
}
@@ -183,8 +193,11 @@ public class DealerOrderSettlement10584Task {
// 2) 门店分红上级:从下单用户开始逐级向上找,命中 ShopDealerUser.type=1 的最近两级(直推门店/间推门店)。
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount, goodsQty, commissionConfig, level1ParentCache, shopRoleCache);
// 3) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
createDealerOrderRecord(order, baseAmount, dealerRefereeCommission, shopRoleCommission);
// 3) 总经销商分润:固定比率,每个订单都分。
TotalDealerCommission totalDealerCommission = settleTotalDealerCommission(order, baseAmount, goodsQty, totalDealerUserId);
// 4) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
createDealerOrderRecord(order, baseAmount, dealerRefereeCommission, shopRoleCommission, totalDealerCommission);
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
}
@@ -323,6 +336,40 @@ public class DealerOrderSettlement10584Task {
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
}
private TotalDealerCommission settleTotalDealerCommission(
ShopOrder order,
BigDecimal baseAmount,
int goodsQty,
Integer totalDealerUserId
) {
if (totalDealerUserId == null) {
return TotalDealerCommission.empty();
}
BigDecimal money = calcMoneyByCommissionType(baseAmount, TOTAL_DEALER_DIVIDEND_RATE, goodsQty, DIVIDEND_SCALE, 20);
log.info("总经销商分润发放 - orderNo={}, totalDealerUserId={}, rate={}, money={}",
order.getOrderNo(), totalDealerUserId, TOTAL_DEALER_DIVIDEND_RATE, money);
creditDealerCommission(
totalDealerUserId,
money,
order,
order.getUserId(),
buildCommissionComment("总经销商分润", 20, TOTAL_DEALER_DIVIDEND_RATE, goodsQty)
);
return new TotalDealerCommission(totalDealerUserId, money);
}
private Integer findTotalDealerUserId() {
ShopDealerUser dealerUser = shopDealerUserService.getOne(
new LambdaQueryWrapper<ShopDealerUser>()
.eq(ShopDealerUser::getTenantId, TENANT_ID)
.eq(ShopDealerUser::getType, 2)
.and(w -> w.eq(ShopDealerUser::getIsDelete, 0).or().isNull(ShopDealerUser::getIsDelete))
.orderByAsc(ShopDealerUser::getId)
.last("limit 1")
);
return dealerUser != null ? dealerUser.getUserId() : null;
}
/**
* 门店分红规则:
* - 门店角色为 ShopDealerUser.type=1
@@ -502,7 +549,8 @@ public class DealerOrderSettlement10584Task {
ShopOrder order,
BigDecimal baseAmount,
DealerRefereeCommission dealerRefereeCommission,
ShopRoleCommission shopRoleCommission
ShopRoleCommission shopRoleCommission,
TotalDealerCommission totalDealerCommission
) {
// 幂等:同一订单只写一条(依赖 order_no + tenant_id 作为业务唯一)
ShopDealerOrder existed = shopDealerOrderService.getOne(
@@ -580,20 +628,25 @@ public class DealerOrderSettlement10584Task {
dealerOrder.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
dealerOrder.setTenantId(TENANT_ID);
dealerOrder.setComments(buildCommissionTraceComment(dealerRefereeCommission, shopRoleCommission));
dealerOrder.setComments(buildCommissionTraceComment(dealerRefereeCommission, shopRoleCommission, totalDealerCommission));
shopDealerOrderService.save(dealerOrder);
log.info("写入ShopDealerOrder完成 - orderNo={}, firstUserId={}, secondUserId={}, firstDividendUser={}, secondDividendUser={}",
order.getOrderNo(), dealerOrder.getFirstUserId(), dealerOrder.getSecondUserId(), dealerOrder.getFirstDividendUser(), dealerOrder.getSecondDividendUser());
}
private String buildCommissionTraceComment(DealerRefereeCommission dealerRefereeCommission, ShopRoleCommission shopRoleCommission) {
private String buildCommissionTraceComment(
DealerRefereeCommission dealerRefereeCommission,
ShopRoleCommission shopRoleCommission,
TotalDealerCommission totalDealerCommission
) {
// 轻量“过程”留痕,方便排查;详细分佣以 ShopDealerCapital 为准。
return "direct=" + dealerRefereeCommission.directDealerId + ":" + dealerRefereeCommission.directMoney
+ ",simple=" + dealerRefereeCommission.simpleDealerId + ":" + dealerRefereeCommission.simpleMoney
+ ",third=" + dealerRefereeCommission.thirdDealerId + ":" + dealerRefereeCommission.thirdMoney
+ ",dividend1=" + shopRoleCommission.storeDirectUserId + ":" + shopRoleCommission.storeDirectMoney
+ ",dividend2=" + shopRoleCommission.storeSimpleUserId + ":" + shopRoleCommission.storeSimpleMoney;
+ ",dividend2=" + shopRoleCommission.storeSimpleUserId + ":" + shopRoleCommission.storeSimpleMoney
+ ",totalDealer=" + totalDealerCommission.userId + ":" + totalDealerCommission.money;
}
private BigDecimal getOrderBaseAmount(ShopOrder order) {
@@ -805,4 +858,18 @@ public class DealerOrderSettlement10584Task {
this.storeSimpleMoney = storeSimpleMoney != null ? storeSimpleMoney : BigDecimal.ZERO;
}
}
private static class TotalDealerCommission {
private final Integer userId;
private final BigDecimal money;
private static TotalDealerCommission empty() {
return new TotalDealerCommission(null, BigDecimal.ZERO);
}
private TotalDealerCommission(Integer userId, BigDecimal money) {
this.userId = userId;
this.money = money != null ? money : BigDecimal.ZERO;
}
}
}