feat(settlement): 优化经销商订单结算任务并添加案件编号搜索功能

- 在多个信用查询映射文件中添加案件编号关键词搜索条件
- 在CreditGqdjMapper中添加代码字段搜索过滤器
- 修改结算任务逻辑,将void方法改为返回详细佣金信息的对象
- 添加经销商订单记录创建功能,用于结算追踪和统计
- 增加详细的日志记录便于调试和监控
- 添加防止重复结算的幂等功能
- 重构佣金计算逻辑,支持更精确的分佣跟踪
This commit is contained in:
2026-01-23 14:11:28 +08:00
parent 16e5e31f33
commit 06c20b8418
5 changed files with 147 additions and 17 deletions

View File

@@ -62,6 +62,7 @@
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
OR a.case_number = #{param.keywords}
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>

View File

@@ -62,7 +62,8 @@
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
OR a.case_number = #{param.keywords}
)
</if>
</where>

View File

@@ -24,6 +24,9 @@
<if test="param.appellee != null">
AND a.appellee LIKE CONCAT('%', #{param.defendant appellee}, '%')
</if>
<if test="param.code != null">
AND a.code LIKE CONCAT('%', #{param.code appellee}, '%')
</if>
<if test="param.caseNumber != null">
AND a.case_number LIKE CONCAT('%', #{param.caseNumber}, '%')
</if>
@@ -65,7 +68,8 @@
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
OR a.code = #{param.keywords}
)
</if>
</where>

View File

@@ -74,7 +74,8 @@
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
OR b.name LIKE CONCAT('%', #{param.keywords}, '%')
OR a.case_number = #{param.keywords}
)
</if>
</where>

View File

@@ -6,10 +6,12 @@ import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.common.system.entity.Role;
import com.gxwebsoft.common.system.service.UserRoleService;
import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.entity.ShopDealerOrder;
import com.gxwebsoft.shop.entity.ShopDealerReferee;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerOrderService;
import com.gxwebsoft.shop.service.ShopDealerRefereeService;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopOrderService;
@@ -58,6 +60,9 @@ public class DealerOrderSettlement10584Task {
@Resource
private ShopDealerCapitalService shopDealerCapitalService;
@Resource
private ShopDealerOrderService shopDealerOrderService;
@Resource
private UserRoleService userRoleService;
@@ -73,6 +78,11 @@ public class DealerOrderSettlement10584Task {
return;
}
log.info("租户{}待结算订单数: {}, orderNos(sample)={}",
TENANT_ID,
orders.size(),
orders.stream().limit(10).map(ShopOrder::getOrderNo).toList());
// 缓存:减少同一批次内重复查角色
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
@@ -126,27 +136,37 @@ public class DealerOrderSettlement10584Task {
throw new IllegalStateException("订单金额为空或<=0无法结算 - orderId=" + order.getOrderId() + ", orderNo=" + order.getOrderNo());
}
log.info("开始结算订单 - orderId={}, orderNo={}, buyerUserId={}, payPrice={}",
order.getOrderId(), order.getOrderNo(), order.getUserId(), baseAmount);
// 1) 直推/简推shop_dealer_referee
settleDealerRefereeCommission(order, baseAmount);
DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount);
// 2) 角色为shop的推荐人shop_dealer_referee 链路向上查找 role=shop 的上级)
settleShopRoleRefereeCommission(order, baseAmount, shopRoleCache);
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount, shopRoleCache);
// 3) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
createDealerOrderRecord(order, baseAmount, dealerRefereeCommission, shopRoleCommission);
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
}
private void settleDealerRefereeCommission(ShopOrder order, BigDecimal baseAmount) {
private DealerRefereeCommission settleDealerRefereeCommission(ShopOrder order, BigDecimal baseAmount) {
// level=2 可能不可靠:按“查两次”方式获取简推(直推的上级)。
Integer directDealerId = getDealerRefereeId(order.getUserId());
Integer simpleDealerId = directDealerId != null ? getDealerRefereeId(directDealerId) : null;
BigDecimal directMoney = calcMoney(baseAmount, RATE_0_10);
BigDecimal simpleMoney = calcMoney(baseAmount, RATE_0_10);
BigDecimal directMoney = directDealerId != null ? calcMoney(baseAmount, RATE_0_10) : BigDecimal.ZERO;
boolean samePerson = directDealerId != null && directDealerId.equals(simpleDealerId);
BigDecimal simpleMoney = (simpleDealerId != null && !samePerson) ? calcMoney(baseAmount, RATE_0_10) : BigDecimal.ZERO;
log.info("分销直推/简推查询结果 - orderNo={}, buyerUserId={}, directDealerId={}, directMoney={}, simpleDealerId={}, simpleMoney={}",
order.getOrderNo(), order.getUserId(), directDealerId, directMoney, simpleDealerId, simpleMoney);
creditDealerCommission(directDealerId, directMoney, order, "直推佣金(10%)");
if (simpleDealerId != null && !simpleDealerId.equals(directDealerId)) {
creditDealerCommission(simpleDealerId, simpleMoney, order, "简推佣金(10%)");
}
creditDealerCommission(simpleDealerId, simpleMoney, order, "简推佣金(10%)");
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney);
}
private Integer getDealerRefereeId(Integer userId) {
@@ -161,23 +181,34 @@ public class DealerOrderSettlement10584Task {
.orderByDesc(ShopDealerReferee::getId)
.last("limit 1")
);
log.debug("shop_dealer_referee(level=1) 查询 - tenantId={}, userId={}, dealerId={}",
TENANT_ID, userId, rel != null ? rel.getDealerId() : null);
return rel != null ? rel.getDealerId() : null;
}
private void settleShopRoleRefereeCommission(ShopOrder order, BigDecimal baseAmount, Map<Integer, Boolean> shopRoleCache) {
private ShopRoleCommission settleShopRoleRefereeCommission(ShopOrder order, BigDecimal baseAmount, Map<Integer, Boolean> shopRoleCache) {
List<Integer> shopRoleReferees = findFirstTwoShopRoleRefereesByDealerReferee(order.getUserId(), shopRoleCache);
log.info("门店(角色shop)上级链路结果 - orderNo={}, buyerUserId={}, shopRoleReferees={}",
order.getOrderNo(), order.getUserId(), shopRoleReferees);
if (shopRoleReferees.isEmpty()) {
return;
return ShopRoleCommission.empty();
}
if (shopRoleReferees.size() == 1) {
creditDealerCommission(shopRoleReferees.get(0), calcMoney(baseAmount, RATE_0_10), order, "shop角色推荐人佣金(仅1人10%)");
return;
BigDecimal money = calcMoney(baseAmount, RATE_0_10);
log.info("门店直推/简推发放(仅1人) - orderNo={}, storeDirectUserId={}, money={}", order.getOrderNo(), shopRoleReferees.get(0), money);
creditDealerCommission(shopRoleReferees.get(0), money, order, "门店直推佣金(角色shop仅1人10%)");
return new ShopRoleCommission(shopRoleReferees.get(0), money, null, BigDecimal.ZERO);
}
// 两个或以上第一个0.02第二个0.08
creditDealerCommission(shopRoleReferees.get(0), calcMoney(baseAmount, RATE_0_02), order, "shop角色推荐人佣金(第1个2%)");
creditDealerCommission(shopRoleReferees.get(1), calcMoney(baseAmount, RATE_0_08), order, "shop角色推荐人佣金(第2个8%)");
BigDecimal storeDirectMoney = calcMoney(baseAmount, RATE_0_02);
BigDecimal storeSimpleMoney = calcMoney(baseAmount, RATE_0_08);
log.info("门店直推/门店简推发放 - orderNo={}, storeDirectUserId={}, storeDirectMoney={}, storeSimpleUserId={}, storeSimpleMoney={}",
order.getOrderNo(), shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
creditDealerCommission(shopRoleReferees.get(0), storeDirectMoney, order, "门店直推佣金(角色shop第1个2%)");
creditDealerCommission(shopRoleReferees.get(1), storeSimpleMoney, order, "门店简推佣金(角色shop第2个8%)");
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
}
private List<Integer> findFirstTwoShopRoleRefereesByDealerReferee(Integer buyerUserId, Map<Integer, Boolean> shopRoleCache) {
@@ -195,11 +226,14 @@ public class DealerOrderSettlement10584Task {
break;
}
log.debug("门店(角色shop)链路向上 - buyerOrChildUserId={}, parentId={}", current, parentId);
if (!visited.add(parentId)) {
break;
}
if (hasShopRole(parentId, shopRoleCache)) {
log.debug("门店(角色shop)命中 - parentId={}", parentId);
result.add(parentId);
if (result.size() >= 2) {
break;
@@ -220,6 +254,10 @@ public class DealerOrderSettlement10584Task {
List<Role> roles = userRoleService.listByUserId(userId);
boolean isShop = roles != null && roles.stream().anyMatch(r -> "shop".equalsIgnoreCase(r.getRoleCode()));
log.debug("角色判定 - userId={}, roles={}, isShop={}",
userId,
roles == null ? null : roles.stream().map(Role::getRoleCode).toList(),
isShop);
shopRoleCache.put(userId, isShop);
return isShop;
}
@@ -229,6 +267,8 @@ public class DealerOrderSettlement10584Task {
return;
}
log.info("佣金入账 - orderNo={}, toDealerUserId={}, money={}, comments={}", order.getOrderNo(), dealerUserId, money, comments);
// 先累加佣金到分销商账户避免并发下丢失更新用SQL自增
boolean updated = shopDealerUserService.update(
new LambdaUpdateWrapper<ShopDealerUser>()
@@ -255,6 +295,57 @@ public class DealerOrderSettlement10584Task {
shopDealerCapitalService.save(cap);
}
private void createDealerOrderRecord(
ShopOrder order,
BigDecimal baseAmount,
DealerRefereeCommission dealerRefereeCommission,
ShopRoleCommission shopRoleCommission
) {
// 幂等:同一订单只写一条(依赖 order_no + tenant_id 作为业务唯一)
boolean exists = shopDealerOrderService.count(
new LambdaQueryWrapper<ShopDealerOrder>()
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo())
) > 0;
if (exists) {
log.info("ShopDealerOrder已存在跳过写入 - orderNo={}", order.getOrderNo());
return;
}
ShopDealerOrder dealerOrder = new ShopDealerOrder();
dealerOrder.setUserId(order.getUserId()); // 买家用户ID
dealerOrder.setOrderNo(order.getOrderNo());
dealerOrder.setOrderPrice(baseAmount);
dealerOrder.setPayPrice(baseAmount);
dealerOrder.setFirstUserId(dealerRefereeCommission.directDealerId);
dealerOrder.setFirstMoney(dealerRefereeCommission.directMoney);
dealerOrder.setSecondUserId(dealerRefereeCommission.simpleDealerId);
dealerOrder.setSecondMoney(dealerRefereeCommission.simpleMoney);
// 表结构只有三级,门店(角色shop)两级放入 third + comments详细以 capital 为准)
dealerOrder.setThirdUserId(shopRoleCommission.storeDirectUserId);
dealerOrder.setThirdMoney(shopRoleCommission.storeDirectMoney);
dealerOrder.setIsSettled(1);
dealerOrder.setSettleTime(java.time.LocalDateTime.now());
dealerOrder.setTenantId(TENANT_ID);
dealerOrder.setComments(buildCommissionTraceComment(dealerRefereeCommission, shopRoleCommission));
shopDealerOrderService.save(dealerOrder);
log.info("写入ShopDealerOrder完成 - orderNo={}, firstUserId={}, secondUserId={}, thirdUserId={}",
order.getOrderNo(), dealerOrder.getFirstUserId(), dealerOrder.getSecondUserId(), dealerOrder.getThirdUserId());
}
private String buildCommissionTraceComment(DealerRefereeCommission dealerRefereeCommission, ShopRoleCommission shopRoleCommission) {
// 轻量“过程”留痕,方便排查;详细分佣以 ShopDealerCapital 为准。
return "direct=" + dealerRefereeCommission.directDealerId + ":" + dealerRefereeCommission.directMoney
+ ",simple=" + dealerRefereeCommission.simpleDealerId + ":" + dealerRefereeCommission.simpleMoney
+ ",storeDirect=" + shopRoleCommission.storeDirectUserId + ":" + shopRoleCommission.storeDirectMoney
+ ",storeSimple=" + shopRoleCommission.storeSimpleUserId + ":" + shopRoleCommission.storeSimpleMoney;
}
private BigDecimal getOrderBaseAmount(ShopOrder order) {
if (order == null) {
return null;
@@ -268,4 +359,36 @@ public class DealerOrderSettlement10584Task {
}
return base.multiply(rate).setScale(2, RoundingMode.HALF_UP);
}
private static class DealerRefereeCommission {
private final Integer directDealerId;
private final BigDecimal directMoney;
private final Integer simpleDealerId;
private final BigDecimal simpleMoney;
private DealerRefereeCommission(Integer directDealerId, BigDecimal directMoney, Integer simpleDealerId, BigDecimal simpleMoney) {
this.directDealerId = directDealerId;
this.directMoney = directMoney != null ? directMoney : BigDecimal.ZERO;
this.simpleDealerId = simpleDealerId;
this.simpleMoney = simpleMoney != null ? simpleMoney : BigDecimal.ZERO;
}
}
private static class ShopRoleCommission {
private final Integer storeDirectUserId;
private final BigDecimal storeDirectMoney;
private final Integer storeSimpleUserId;
private final BigDecimal storeSimpleMoney;
private static ShopRoleCommission empty() {
return new ShopRoleCommission(null, BigDecimal.ZERO, null, BigDecimal.ZERO);
}
private ShopRoleCommission(Integer storeDirectUserId, BigDecimal storeDirectMoney, Integer storeSimpleUserId, BigDecimal storeSimpleMoney) {
this.storeDirectUserId = storeDirectUserId;
this.storeDirectMoney = storeDirectMoney != null ? storeDirectMoney : BigDecimal.ZERO;
this.storeSimpleUserId = storeSimpleUserId;
this.storeSimpleMoney = storeSimpleMoney != null ? storeSimpleMoney : BigDecimal.ZERO;
}
}
}