refactor(settlement): 优化经销商订单结算逻辑
- 移除不必要的UserRoleService依赖注入 - 将定时任务执行频率从每20秒调整为每30秒 - 删除过期的shopRoleCache缓存机制 - 重构推荐关系查询逻辑,支持多级关系和门店角色识别 - 添加对两种数据形态的兼容处理(level=1/2多级关系 vs 仅level=1关系) - 修改佣金分配逻辑,区分直推佣金和推荐奖金 - 优化门店角色佣金计算,调整为直推2%和简推1% - 添加分销商账户自动创建机制,确保佣金能够正常发放 - 更新资金明细记录,准确关联到对应的用户ID - 新增自定义SQL查询方法,一次性获取推荐关系链路和门店角色信息 - 扩展ShopDealerReferee实体,增加isShopRole扩展字段 - 优化MyBatis XML映射,使用LEFT JOIN预加载角色信息避免N+1查询问题
This commit is contained in:
@@ -64,9 +64,13 @@ public class ShopDealerReferee implements Serializable {
|
||||
@TableField(exist = false)
|
||||
private Boolean isAdmin;
|
||||
|
||||
@Schema(description = "推荐关系层级(1,2,3)")
|
||||
@Schema(description = "推荐关系层级(弃用)")
|
||||
private Integer level;
|
||||
|
||||
@Schema(description = "上级是否门店角色(shop)")
|
||||
@TableField(exist = false)
|
||||
private Boolean isShopRole;
|
||||
|
||||
@Schema(description = "来源(如 goods_share)")
|
||||
// NOTE: 表 shop_dealer_referee 若未新增该字段,需要 exist=false,避免 MyBatis-Plus 自动生成SQL时报 Unknown column。
|
||||
@TableField(exist = false)
|
||||
|
||||
@@ -34,4 +34,22 @@ public interface ShopDealerRefereeMapper extends BaseMapper<ShopDealerReferee> {
|
||||
*/
|
||||
List<ShopDealerReferee> selectListRel(@Param("param") ShopDealerRefereeParam param);
|
||||
|
||||
/**
|
||||
* 查询指定用户的推荐关系链路(按 level 升序),并计算每个上级是否包含门店角色(shop)。
|
||||
* <p>
|
||||
* 用于定时任务分佣:避免逐个查询 sys_user_role。
|
||||
* <p>
|
||||
* 注意:对应XML里为了兼容 MyBatis-Plus 的 SQL 解析,limit 使用字面量拼接(${limit}),
|
||||
* 这里的 limit 必须由服务端常量传入(不要透传用户输入)。
|
||||
*/
|
||||
List<ShopDealerReferee> selectRefereeChainWithShopRole(@Param("tenantId") Integer tenantId,
|
||||
@Param("userId") Integer userId,
|
||||
@Param("limit") Integer limit);
|
||||
|
||||
/**
|
||||
* 查询指定用户的一级推荐人(level=1),并计算该推荐人是否包含门店角色(shop)。
|
||||
*/
|
||||
ShopDealerReferee selectFirstLevelRefereeWithShopRole(@Param("tenantId") Integer tenantId,
|
||||
@Param("userId") Integer userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,27 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper">
|
||||
|
||||
<!--
|
||||
MyBatis-Plus 3.4.x uses JSqlParser 3.2 to parse and rewrite SQL (e.g. for tenant/pagination).
|
||||
JSqlParser 3.2 has limited MySQL expression support and may fail on constructs like:
|
||||
- `EXISTS(subquery)` in SELECT items
|
||||
- `MAX((cond) OR (cond))` in derived tables
|
||||
This derived table keeps the SQL parse-friendly: one row per user_id that has role=shop.
|
||||
-->
|
||||
<sql id="shopRoleUserIds">
|
||||
SELECT DISTINCT ur.user_id
|
||||
FROM gxwebsoft_core.sys_user_role ur
|
||||
LEFT JOIN gxwebsoft_core.sys_role r ON ur.role_id = r.role_id AND r.deleted = 0
|
||||
WHERE (LOWER(TRIM(r.role_code)) = 'shop' OR LOWER(TRIM(r.role_name)) = 'shop')
|
||||
</sql>
|
||||
|
||||
<!-- 关联查询sql -->
|
||||
<sql id="selectSql">
|
||||
SELECT DISTINCT a.*,
|
||||
d.nickname AS dealerName,
|
||||
d.avatar AS dealerAvatar,
|
||||
d.phone AS dealerPhone,
|
||||
CASE WHEN sr.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole,
|
||||
u.nickname,
|
||||
u.avatar,
|
||||
u.alias,
|
||||
@@ -15,8 +30,14 @@
|
||||
u.is_admin as isAdmin
|
||||
FROM shop_dealer_referee a
|
||||
INNER JOIN gxwebsoft_core.sys_user d ON a.dealer_id = d.user_id AND d.deleted = 0
|
||||
LEFT JOIN (
|
||||
<include refid="shopRoleUserIds"/>
|
||||
) sr ON sr.user_id = a.dealer_id
|
||||
INNER JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id AND u.deleted = 0
|
||||
<where>
|
||||
<if test="param.tenantId != null">
|
||||
AND a.tenant_id = #{param.tenantId}
|
||||
</if>
|
||||
<if test="param.id != null">
|
||||
AND a.id = #{param.id}
|
||||
</if>
|
||||
@@ -55,4 +76,46 @@
|
||||
<include refid="selectSql"></include>
|
||||
</select>
|
||||
|
||||
<!-- 定时任务:查某个用户的推荐关系链路(按 level 升序),并带出是否门店角色 -->
|
||||
<select id="selectRefereeChainWithShopRole" resultType="com.gxwebsoft.shop.entity.ShopDealerReferee">
|
||||
SELECT a.id,
|
||||
a.dealer_id AS dealerId,
|
||||
a.user_id AS userId,
|
||||
a.level,
|
||||
a.tenant_id AS tenantId,
|
||||
CASE WHEN sr.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole
|
||||
FROM shop_dealer_referee a
|
||||
LEFT JOIN (
|
||||
<include refid="shopRoleUserIds"/>
|
||||
) sr ON sr.user_id = a.dealer_id
|
||||
WHERE a.tenant_id = #{tenantId}
|
||||
AND a.user_id = #{userId}
|
||||
ORDER BY a.level ASC, a.id DESC
|
||||
<!--
|
||||
MyBatis-Plus TenantLineInnerInterceptor uses JSqlParser to parse SQL.
|
||||
Some versions can't parse "LIMIT ?" (prepared placeholder), so use a literal here.
|
||||
`limit` is an Integer passed from server-side constants (no user input).
|
||||
-->
|
||||
LIMIT ${limit}
|
||||
</select>
|
||||
|
||||
<!-- 定时任务:查某个用户的一级推荐关系(level=1),并带出是否门店角色 -->
|
||||
<select id="selectFirstLevelRefereeWithShopRole" resultType="com.gxwebsoft.shop.entity.ShopDealerReferee">
|
||||
SELECT a.id,
|
||||
a.dealer_id AS dealerId,
|
||||
a.user_id AS userId,
|
||||
a.level,
|
||||
a.tenant_id AS tenantId,
|
||||
CASE WHEN sr.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole
|
||||
FROM shop_dealer_referee a
|
||||
LEFT JOIN (
|
||||
<include refid="shopRoleUserIds"/>
|
||||
) sr ON sr.user_id = a.dealer_id
|
||||
WHERE a.tenant_id = #{tenantId}
|
||||
AND a.user_id = #{userId}
|
||||
AND a.level = 1
|
||||
ORDER BY a.id DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -3,13 +3,12 @@ package com.gxwebsoft.shop.task;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
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.mapper.ShopDealerRefereeMapper;
|
||||
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
||||
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
||||
import com.gxwebsoft.shop.service.ShopDealerRefereeService;
|
||||
@@ -65,12 +64,12 @@ public class DealerOrderSettlement10584Task {
|
||||
private ShopDealerOrderService shopDealerOrderService;
|
||||
|
||||
@Resource
|
||||
private UserRoleService userRoleService;
|
||||
private ShopDealerRefereeMapper shopDealerRefereeMapper;
|
||||
|
||||
/**
|
||||
* 每20秒执行一次。
|
||||
* 每30秒执行一次。
|
||||
*/
|
||||
@Scheduled(cron = "0/20 * * * * ?")
|
||||
@Scheduled(cron = "0/30 * * * * ?")
|
||||
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
|
||||
public void settleTenant10584Orders() {
|
||||
try {
|
||||
@@ -84,9 +83,6 @@ public class DealerOrderSettlement10584Task {
|
||||
orders.size(),
|
||||
orders.stream().limit(10).map(ShopOrder::getOrderNo).toList());
|
||||
|
||||
// 缓存:减少同一批次内重复查角色
|
||||
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
|
||||
|
||||
for (ShopOrder order : orders) {
|
||||
try {
|
||||
transactionTemplate.executeWithoutResult(status -> {
|
||||
@@ -94,7 +90,7 @@ public class DealerOrderSettlement10584Task {
|
||||
if (!claimOrderToSettle(order.getOrderId())) {
|
||||
return;
|
||||
}
|
||||
settleOneOrder(order, shopRoleCache);
|
||||
settleOneOrder(order);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("订单结算失败,将回滚本订单并在下次任务重试 - orderId={}, orderNo={}", order.getOrderId(), order.getOrderNo(), e);
|
||||
@@ -127,7 +123,7 @@ public class DealerOrderSettlement10584Task {
|
||||
);
|
||||
}
|
||||
|
||||
private void settleOneOrder(ShopOrder order, Map<Integer, Boolean> shopRoleCache) {
|
||||
private void settleOneOrder(ShopOrder order) {
|
||||
if (order.getUserId() == null || order.getOrderNo() == null) {
|
||||
throw new IllegalStateException("订单关键信息缺失,无法结算 - orderId=" + order.getOrderId());
|
||||
}
|
||||
@@ -144,7 +140,7 @@ public class DealerOrderSettlement10584Task {
|
||||
DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount);
|
||||
|
||||
// 2) 角色为shop的推荐人(shop_dealer_referee 链路向上查找 role=shop 的上级)
|
||||
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount, shopRoleCache);
|
||||
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount);
|
||||
|
||||
// 3) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
|
||||
createDealerOrderRecord(order, baseAmount, dealerRefereeCommission, shopRoleCommission);
|
||||
@@ -153,9 +149,14 @@ public class DealerOrderSettlement10584Task {
|
||||
}
|
||||
|
||||
private DealerRefereeCommission settleDealerRefereeCommission(ShopOrder order, BigDecimal baseAmount) {
|
||||
// level=2 可能不可靠:按“查两次”方式获取简推(直推的上级)。
|
||||
Integer directDealerId = getDealerRefereeId(order.getUserId());
|
||||
Integer simpleDealerId = directDealerId != null ? getDealerRefereeId(directDealerId) : null;
|
||||
// 兼容两种数据形态:
|
||||
// 1) 同一 userId 下有 level=1/2 的多级关系(直接按 level 取);
|
||||
// 2) 仅维护 level=1(用“查两次”回退获取上级)。
|
||||
Integer directDealerId = getDealerRefereeId(order.getUserId(), 1);
|
||||
Integer simpleDealerId = getDealerRefereeId(order.getUserId(), 2);
|
||||
if (simpleDealerId == null && directDealerId != null) {
|
||||
simpleDealerId = getDealerRefereeId(directDealerId, 1);
|
||||
}
|
||||
|
||||
BigDecimal directMoney = directDealerId != null ? calcMoney(baseAmount, RATE_0_10) : BigDecimal.ZERO;
|
||||
// 允许同一条线内同一个人同时拿到“直推 + 简推”(即使 directDealerId == simpleDealerId 也照常发放两笔)
|
||||
@@ -164,13 +165,18 @@ public class DealerOrderSettlement10584Task {
|
||||
log.info("分销直推/简推查询结果 - orderNo={}, buyerUserId={}, directDealerId={}, directMoney={}, simpleDealerId={}, simpleMoney={}",
|
||||
order.getOrderNo(), order.getUserId(), directDealerId, directMoney, simpleDealerId, simpleMoney);
|
||||
|
||||
creditDealerCommission(directDealerId, directMoney, order, "直推佣金(10%)");
|
||||
creditDealerCommission(simpleDealerId, simpleMoney, order, "简推佣金(5%)");
|
||||
// 直推:对方=买家;推荐奖(5%):对方=直推分销商(便于在资金明细中看出“来自哪个下级分销商/团队订单”)
|
||||
creditDealerCommission(directDealerId, directMoney, order, order.getUserId(), "直推佣金(10%)");
|
||||
creditDealerCommission(simpleDealerId, simpleMoney, order, directDealerId, "推荐奖(5%)");
|
||||
|
||||
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney);
|
||||
}
|
||||
|
||||
private Integer getDealerRefereeId(Integer userId) {
|
||||
return getDealerRefereeId(userId, 1);
|
||||
}
|
||||
|
||||
private Integer getDealerRefereeId(Integer userId, int level) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -178,17 +184,17 @@ public class DealerOrderSettlement10584Task {
|
||||
new LambdaQueryWrapper<ShopDealerReferee>()
|
||||
.eq(ShopDealerReferee::getTenantId, TENANT_ID)
|
||||
.eq(ShopDealerReferee::getUserId, userId)
|
||||
.eq(ShopDealerReferee::getLevel, 1)
|
||||
.eq(ShopDealerReferee::getLevel, level)
|
||||
.orderByDesc(ShopDealerReferee::getId)
|
||||
.last("limit 1")
|
||||
);
|
||||
log.debug("shop_dealer_referee(level=1) 查询 - tenantId={}, userId={}, dealerId={}",
|
||||
TENANT_ID, userId, rel != null ? rel.getDealerId() : null);
|
||||
log.debug("shop_dealer_referee(level={}) 查询 - tenantId={}, userId={}, dealerId={}",
|
||||
level, TENANT_ID, userId, rel != null ? rel.getDealerId() : null);
|
||||
return rel != null ? rel.getDealerId() : null;
|
||||
}
|
||||
|
||||
private ShopRoleCommission settleShopRoleRefereeCommission(ShopOrder order, BigDecimal baseAmount, Map<Integer, Boolean> shopRoleCache) {
|
||||
List<Integer> shopRoleReferees = findFirstTwoShopRoleRefereesByDealerReferee(order.getUserId(), shopRoleCache);
|
||||
private ShopRoleCommission settleShopRoleRefereeCommission(ShopOrder order, BigDecimal baseAmount) {
|
||||
List<Integer> shopRoleReferees = findFirstTwoShopRoleRefereesByDealerReferee(order.getUserId());
|
||||
log.info("门店(角色shop)上级链路结果 - orderNo={}, buyerUserId={}, shopRoleReferees={}",
|
||||
order.getOrderNo(), order.getUserId(), shopRoleReferees);
|
||||
if (shopRoleReferees.isEmpty()) {
|
||||
@@ -196,23 +202,24 @@ public class DealerOrderSettlement10584Task {
|
||||
}
|
||||
|
||||
if (shopRoleReferees.size() == 1) {
|
||||
BigDecimal money = calcMoney(baseAmount, RATE_0_10);
|
||||
// 门店直推:2%
|
||||
BigDecimal money = calcMoney(baseAmount, RATE_0_02);
|
||||
log.info("门店直推/简推发放(仅1人) - orderNo={}, storeDirectUserId={}, money={}", order.getOrderNo(), shopRoleReferees.get(0), money);
|
||||
creditDealerCommission(shopRoleReferees.get(0), money, order, "门店直推佣金(角色shop,仅1人,10%)");
|
||||
creditDealerCommission(shopRoleReferees.get(0), money, order, order.getUserId(), "门店直推佣金(角色shop,仅1人,2%)");
|
||||
return new ShopRoleCommission(shopRoleReferees.get(0), money, null, BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
// 两个或以上:第一个0.02,第二个0.08
|
||||
// 两个或以上:第一个0.02,第二个0.01
|
||||
BigDecimal storeDirectMoney = calcMoney(baseAmount, RATE_0_02);
|
||||
BigDecimal storeSimpleMoney = calcMoney(baseAmount, RATE_0_01);
|
||||
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个,1%)");
|
||||
creditDealerCommission(shopRoleReferees.get(0), storeDirectMoney, order, order.getUserId(), "门店直推佣金(角色shop,第1个,2%)");
|
||||
creditDealerCommission(shopRoleReferees.get(1), storeSimpleMoney, order, order.getUserId(), "门店简推佣金(角色shop,第2个,1%)");
|
||||
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
||||
}
|
||||
|
||||
private List<Integer> findFirstTwoShopRoleRefereesByDealerReferee(Integer buyerUserId, Map<Integer, Boolean> shopRoleCache) {
|
||||
private List<Integer> findFirstTwoShopRoleRefereesByDealerReferee(Integer buyerUserId) {
|
||||
if (buyerUserId == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -220,9 +227,31 @@ public class DealerOrderSettlement10584Task {
|
||||
List<Integer> result = new ArrayList<>(2);
|
||||
Set<Integer> visited = new HashSet<>();
|
||||
|
||||
// 优先:直接从该买家(userId)的多级关系(level=1/2/3/...)里,按 level 升序找到最近的两级门店(角色shop)。
|
||||
// 背景:部分数据只维护“买家 -> 上级们”的多级(level)记录,但不一定维护“上级 -> 更上级”的记录;
|
||||
// 若仅做链路向上查找,会导致门店直推/简推无法命中,收益与资金明细都不会写入。
|
||||
// 注意:isShopRole 为扩展字段(非表字段),需用自定义SQL带出。
|
||||
List<ShopDealerReferee> relList = shopDealerRefereeMapper.selectRefereeChainWithShopRole(
|
||||
TENANT_ID, buyerUserId, MAX_REFEREE_CHAIN_DEPTH
|
||||
);
|
||||
for (ShopDealerReferee rel : relList) {
|
||||
Integer dealerId = rel != null ? rel.getDealerId() : null;
|
||||
if (dealerId == null || !visited.add(dealerId)) {
|
||||
continue;
|
||||
}
|
||||
if (Boolean.TRUE.equals(rel.getIsShopRole())) {
|
||||
result.add(dealerId);
|
||||
if (result.size() >= 2) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:若只维护了 level=1 的推荐关系,则尝试按“查两次/多次”的方式一路向上找门店角色。
|
||||
Integer current = buyerUserId;
|
||||
for (int i = 0; i < MAX_REFEREE_CHAIN_DEPTH && current != null; i++) {
|
||||
Integer parentId = getDealerRefereeId(current);
|
||||
ShopDealerReferee parentRel = shopDealerRefereeMapper.selectFirstLevelRefereeWithShopRole(TENANT_ID, current);
|
||||
Integer parentId = parentRel != null ? parentRel.getDealerId() : null;
|
||||
if (parentId == null) {
|
||||
break;
|
||||
}
|
||||
@@ -233,7 +262,7 @@ public class DealerOrderSettlement10584Task {
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasShopRole(parentId, shopRoleCache)) {
|
||||
if (parentRel != null && Boolean.TRUE.equals(parentRel.getIsShopRole())) {
|
||||
log.debug("门店(角色shop)命中 - parentId={}", parentId);
|
||||
result.add(parentId);
|
||||
if (result.size() >= 2) {
|
||||
@@ -247,23 +276,7 @@ public class DealerOrderSettlement10584Task {
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasShopRole(Integer userId, Map<Integer, Boolean> shopRoleCache) {
|
||||
Boolean cached = shopRoleCache.get(userId);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void creditDealerCommission(Integer dealerUserId, BigDecimal money, ShopOrder order, String comments) {
|
||||
private void creditDealerCommission(Integer dealerUserId, BigDecimal money, ShopOrder order, Integer toUserId, String comments) {
|
||||
if (dealerUserId == null || money == null || money.signum() <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -280,9 +293,48 @@ public class DealerOrderSettlement10584Task {
|
||||
);
|
||||
|
||||
if (!updated) {
|
||||
log.warn("发放佣金失败:未找到分销商账户 - tenantId={}, dealerUserId={}, orderNo={}", TENANT_ID, dealerUserId, order.getOrderNo());
|
||||
// 门店角色用户可能未开通分销账户:此时门店直推/简推会“找到了人但入不了账”,收益与明细都不会写入。
|
||||
// 这里补偿创建账户后再尝试入账一次。
|
||||
ShopDealerUser existed = shopDealerUserService.getOne(
|
||||
new LambdaQueryWrapper<ShopDealerUser>()
|
||||
.eq(ShopDealerUser::getTenantId, TENANT_ID)
|
||||
.eq(ShopDealerUser::getUserId, dealerUserId)
|
||||
.last("limit 1")
|
||||
);
|
||||
if (existed == null) {
|
||||
ShopDealerUser newDealerUser = new ShopDealerUser();
|
||||
newDealerUser.setTenantId(TENANT_ID);
|
||||
newDealerUser.setUserId(dealerUserId);
|
||||
newDealerUser.setType(0);
|
||||
newDealerUser.setIsDelete(0);
|
||||
newDealerUser.setSortNumber(0);
|
||||
newDealerUser.setFirstNum(0);
|
||||
newDealerUser.setSecondNum(0);
|
||||
newDealerUser.setThirdNum(0);
|
||||
newDealerUser.setMoney(BigDecimal.ZERO);
|
||||
newDealerUser.setFreezeMoney(BigDecimal.ZERO);
|
||||
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
||||
newDealerUser.setCreateTime(java.time.LocalDateTime.now());
|
||||
newDealerUser.setUpdateTime(java.time.LocalDateTime.now());
|
||||
try {
|
||||
shopDealerUserService.save(newDealerUser);
|
||||
} catch (Exception ignore) {
|
||||
// 并发下可能已被其他线程/实例创建,忽略后继续重试入账。
|
||||
}
|
||||
}
|
||||
|
||||
updated = shopDealerUserService.update(
|
||||
new LambdaUpdateWrapper<ShopDealerUser>()
|
||||
.eq(ShopDealerUser::getTenantId, TENANT_ID)
|
||||
.eq(ShopDealerUser::getUserId, dealerUserId)
|
||||
.setSql("money = IFNULL(money,0) + " + money.toPlainString())
|
||||
.setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
|
||||
);
|
||||
if (!updated) {
|
||||
log.warn("发放佣金失败:未找到/创建分销商账户 - tenantId={}, dealerUserId={}, orderNo={}", TENANT_ID, dealerUserId, order.getOrderNo());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ShopDealerCapital cap = new ShopDealerCapital();
|
||||
cap.setUserId(dealerUserId);
|
||||
@@ -290,7 +342,7 @@ public class DealerOrderSettlement10584Task {
|
||||
cap.setFlowType(10);
|
||||
cap.setMoney(money);
|
||||
cap.setComments(comments);
|
||||
cap.setToUserId(order.getUserId());
|
||||
cap.setToUserId(toUserId);
|
||||
cap.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
|
||||
cap.setTenantId(TENANT_ID);
|
||||
shopDealerCapitalService.save(cap);
|
||||
@@ -332,6 +384,8 @@ public class DealerOrderSettlement10584Task {
|
||||
|
||||
dealerOrder.setIsSettled(1);
|
||||
dealerOrder.setSettleTime(java.time.LocalDateTime.now());
|
||||
// ShopDealerCapital 关联查询会拿 shop_dealer_order.month(覆盖 a.month),这里需要同步填充。
|
||||
dealerOrder.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
|
||||
dealerOrder.setTenantId(TENANT_ID);
|
||||
|
||||
dealerOrder.setComments(buildCommissionTraceComment(dealerRefereeCommission, shopRoleCommission));
|
||||
|
||||
Reference in New Issue
Block a user