refactor(task): 优化门店分佣逻辑按type字段判定
- 替换原有的角色shop判定逻辑,改为按ShopDealerUser.type=1判定门店分红用户 - 新增User和UserMapper依赖注入用于获取用户基础信息 - 添加幂等检查机制避免同一订单重复发放佣金 - 在创建分销商账户时补充基础信息防止字段约束导致插入失败 - 优化SQL查询逻辑,直接关联shop_dealer_user表而非查询系统角色表 - 更新日志信息中的描述文字以反映新的判定方式 - 添加对已存在订单记录的回填功能,支持补发门店分佣时更新分红字段
This commit is contained in:
@@ -67,7 +67,7 @@ public class ShopDealerReferee implements Serializable {
|
|||||||
@Schema(description = "推荐关系层级(弃用)")
|
@Schema(description = "推荐关系层级(弃用)")
|
||||||
private Integer level;
|
private Integer level;
|
||||||
|
|
||||||
@Schema(description = "上级是否门店角色(shop)")
|
@Schema(description = "上级是否门店分红用户(ShopDealerUser.type=1;扩展字段)")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Boolean isShopRole;
|
private Boolean isShopRole;
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public interface ShopDealerRefereeMapper extends BaseMapper<ShopDealerReferee> {
|
|||||||
/**
|
/**
|
||||||
* 查询指定用户的推荐关系链路(按 level 升序),并计算每个上级是否包含门店角色(shop)。
|
* 查询指定用户的推荐关系链路(按 level 升序),并计算每个上级是否包含门店角色(shop)。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于定时任务分佣:避免逐个查询 sys_user_role。
|
* 用于定时任务分佣:按 ShopDealerUser.type=1 判定“门店分红”上级,避免查询 core 的 sys_user_role。
|
||||||
* <p>
|
* <p>
|
||||||
* 注意:对应XML里为了兼容 MyBatis-Plus 的 SQL 解析,limit 使用字面量拼接(${limit}),
|
* 注意:对应XML里为了兼容 MyBatis-Plus 的 SQL 解析,limit 使用字面量拼接(${limit}),
|
||||||
* 这里的 limit 必须由服务端常量传入(不要透传用户输入)。
|
* 这里的 limit 必须由服务端常量传入(不要透传用户输入)。
|
||||||
@@ -47,7 +47,7 @@ public interface ShopDealerRefereeMapper extends BaseMapper<ShopDealerReferee> {
|
|||||||
@Param("limit") Integer limit);
|
@Param("limit") Integer limit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询指定用户的一级推荐人(level=1),并计算该推荐人是否包含门店角色(shop)。
|
* 查询指定用户的一级推荐人(level=1),并按 ShopDealerUser.type=1 判定该推荐人是否为“门店分红”上级。
|
||||||
*/
|
*/
|
||||||
ShopDealerReferee selectFirstLevelRefereeWithShopRole(@Param("tenantId") Integer tenantId,
|
ShopDealerReferee selectFirstLevelRefereeWithShopRole(@Param("tenantId") Integer tenantId,
|
||||||
@Param("userId") Integer userId);
|
@Param("userId") Integer userId);
|
||||||
|
|||||||
@@ -2,27 +2,13 @@
|
|||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!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">
|
<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 -->
|
||||||
<sql id="selectSql">
|
<sql id="selectSql">
|
||||||
SELECT DISTINCT a.*,
|
SELECT DISTINCT a.*,
|
||||||
d.nickname AS dealerName,
|
d.nickname AS dealerName,
|
||||||
d.avatar AS dealerAvatar,
|
d.avatar AS dealerAvatar,
|
||||||
d.phone AS dealerPhone,
|
d.phone AS dealerPhone,
|
||||||
CASE WHEN sr.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole,
|
CASE WHEN du.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole,
|
||||||
u.nickname,
|
u.nickname,
|
||||||
u.avatar,
|
u.avatar,
|
||||||
u.alias,
|
u.alias,
|
||||||
@@ -30,9 +16,11 @@
|
|||||||
u.is_admin as isAdmin
|
u.is_admin as isAdmin
|
||||||
FROM shop_dealer_referee a
|
FROM shop_dealer_referee a
|
||||||
INNER JOIN gxwebsoft_core.sys_user d ON a.dealer_id = d.user_id AND d.deleted = 0
|
INNER JOIN gxwebsoft_core.sys_user d ON a.dealer_id = d.user_id AND d.deleted = 0
|
||||||
LEFT JOIN (
|
LEFT JOIN shop_dealer_user du
|
||||||
<include refid="shopRoleUserIds"/>
|
ON du.user_id = a.dealer_id
|
||||||
) sr ON sr.user_id = a.dealer_id
|
AND du.tenant_id = a.tenant_id
|
||||||
|
AND du.type = 1
|
||||||
|
AND (du.is_delete = 0 OR du.is_delete IS NULL)
|
||||||
INNER JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id AND u.deleted = 0
|
INNER JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id AND u.deleted = 0
|
||||||
<where>
|
<where>
|
||||||
<if test="param.tenantId != null">
|
<if test="param.tenantId != null">
|
||||||
@@ -83,11 +71,13 @@
|
|||||||
a.user_id AS userId,
|
a.user_id AS userId,
|
||||||
a.level,
|
a.level,
|
||||||
a.tenant_id AS tenantId,
|
a.tenant_id AS tenantId,
|
||||||
CASE WHEN sr.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole
|
CASE WHEN du.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole
|
||||||
FROM shop_dealer_referee a
|
FROM shop_dealer_referee a
|
||||||
LEFT JOIN (
|
LEFT JOIN shop_dealer_user du
|
||||||
<include refid="shopRoleUserIds"/>
|
ON du.user_id = a.dealer_id
|
||||||
) sr ON sr.user_id = a.dealer_id
|
AND du.tenant_id = a.tenant_id
|
||||||
|
AND du.type = 1
|
||||||
|
AND (du.is_delete = 0 OR du.is_delete IS NULL)
|
||||||
WHERE a.tenant_id = #{tenantId}
|
WHERE a.tenant_id = #{tenantId}
|
||||||
AND a.user_id = #{userId}
|
AND a.user_id = #{userId}
|
||||||
ORDER BY a.level ASC, a.id DESC
|
ORDER BY a.level ASC, a.id DESC
|
||||||
@@ -106,11 +96,13 @@
|
|||||||
a.user_id AS userId,
|
a.user_id AS userId,
|
||||||
a.level,
|
a.level,
|
||||||
a.tenant_id AS tenantId,
|
a.tenant_id AS tenantId,
|
||||||
CASE WHEN sr.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole
|
CASE WHEN du.user_id IS NULL THEN 0 ELSE 1 END AS isShopRole
|
||||||
FROM shop_dealer_referee a
|
FROM shop_dealer_referee a
|
||||||
LEFT JOIN (
|
LEFT JOIN shop_dealer_user du
|
||||||
<include refid="shopRoleUserIds"/>
|
ON du.user_id = a.dealer_id
|
||||||
) sr ON sr.user_id = a.dealer_id
|
AND du.tenant_id = a.tenant_id
|
||||||
|
AND du.type = 1
|
||||||
|
AND (du.is_delete = 0 OR du.is_delete IS NULL)
|
||||||
WHERE a.tenant_id = #{tenantId}
|
WHERE a.tenant_id = #{tenantId}
|
||||||
AND a.user_id = #{userId}
|
AND a.user_id = #{userId}
|
||||||
AND a.level = 1
|
AND a.level = 1
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.gxwebsoft.shop.entity.ShopDealerOrder;
|
|||||||
import com.gxwebsoft.shop.entity.ShopDealerReferee;
|
import com.gxwebsoft.shop.entity.ShopDealerReferee;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.common.system.mapper.UserMapper;
|
||||||
import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper;
|
import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
||||||
@@ -66,6 +68,9 @@ public class DealerOrderSettlement10584Task {
|
|||||||
@Resource
|
@Resource
|
||||||
private ShopDealerRefereeMapper shopDealerRefereeMapper;
|
private ShopDealerRefereeMapper shopDealerRefereeMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每30秒执行一次。
|
* 每30秒执行一次。
|
||||||
*/
|
*/
|
||||||
@@ -139,7 +144,7 @@ public class DealerOrderSettlement10584Task {
|
|||||||
// 1) 直推/简推(shop_dealer_referee)
|
// 1) 直推/简推(shop_dealer_referee)
|
||||||
DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount);
|
DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount);
|
||||||
|
|
||||||
// 2) 角色为shop的推荐人(shop_dealer_referee 链路向上查找 role=shop 的上级)
|
// 2) 门店分红上级(按 ShopDealerUser.type=1,在 shop_dealer_referee 链路向上查找)
|
||||||
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount);
|
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount);
|
||||||
|
|
||||||
// 3) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
|
// 3) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
|
||||||
@@ -195,7 +200,7 @@ public class DealerOrderSettlement10584Task {
|
|||||||
|
|
||||||
private ShopRoleCommission settleShopRoleRefereeCommission(ShopOrder order, BigDecimal baseAmount) {
|
private ShopRoleCommission settleShopRoleRefereeCommission(ShopOrder order, BigDecimal baseAmount) {
|
||||||
List<Integer> shopRoleReferees = findFirstTwoShopRoleRefereesByDealerReferee(order.getUserId());
|
List<Integer> shopRoleReferees = findFirstTwoShopRoleRefereesByDealerReferee(order.getUserId());
|
||||||
log.info("门店(角色shop)上级链路结果 - orderNo={}, buyerUserId={}, shopRoleReferees={}",
|
log.info("门店(type=1)上级链路结果 - orderNo={}, buyerUserId={}, shopRoleReferees={}",
|
||||||
order.getOrderNo(), order.getUserId(), shopRoleReferees);
|
order.getOrderNo(), order.getUserId(), shopRoleReferees);
|
||||||
if (shopRoleReferees.isEmpty()) {
|
if (shopRoleReferees.isEmpty()) {
|
||||||
return ShopRoleCommission.empty();
|
return ShopRoleCommission.empty();
|
||||||
@@ -205,7 +210,7 @@ public class DealerOrderSettlement10584Task {
|
|||||||
// 门店直推:2%
|
// 门店直推:2%
|
||||||
BigDecimal money = calcMoney(baseAmount, RATE_0_02);
|
BigDecimal money = calcMoney(baseAmount, RATE_0_02);
|
||||||
log.info("门店直推/简推发放(仅1人) - orderNo={}, storeDirectUserId={}, money={}", order.getOrderNo(), shopRoleReferees.get(0), money);
|
log.info("门店直推/简推发放(仅1人) - orderNo={}, storeDirectUserId={}, money={}", order.getOrderNo(), shopRoleReferees.get(0), money);
|
||||||
creditDealerCommission(shopRoleReferees.get(0), money, order, order.getUserId(), "门店直推佣金(角色shop,仅1人,2%)");
|
creditDealerCommission(shopRoleReferees.get(0), money, order, order.getUserId(), "门店直推佣金(type=1,仅1人,2%)");
|
||||||
return new ShopRoleCommission(shopRoleReferees.get(0), money, null, BigDecimal.ZERO);
|
return new ShopRoleCommission(shopRoleReferees.get(0), money, null, BigDecimal.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +219,8 @@ public class DealerOrderSettlement10584Task {
|
|||||||
BigDecimal storeSimpleMoney = calcMoney(baseAmount, RATE_0_01);
|
BigDecimal storeSimpleMoney = calcMoney(baseAmount, RATE_0_01);
|
||||||
log.info("门店直推/门店简推发放 - orderNo={}, storeDirectUserId={}, storeDirectMoney={}, storeSimpleUserId={}, storeSimpleMoney={}",
|
log.info("门店直推/门店简推发放 - orderNo={}, storeDirectUserId={}, storeDirectMoney={}, storeSimpleUserId={}, storeSimpleMoney={}",
|
||||||
order.getOrderNo(), shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
order.getOrderNo(), shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
||||||
creditDealerCommission(shopRoleReferees.get(0), storeDirectMoney, order, order.getUserId(), "门店直推佣金(角色shop,第1个,2%)");
|
creditDealerCommission(shopRoleReferees.get(0), storeDirectMoney, order, order.getUserId(), "门店直推佣金(type=1,第1个,2%)");
|
||||||
creditDealerCommission(shopRoleReferees.get(1), storeSimpleMoney, order, order.getUserId(), "门店简推佣金(角色shop,第2个,1%)");
|
creditDealerCommission(shopRoleReferees.get(1), storeSimpleMoney, order, order.getUserId(), "门店简推佣金(type=1,第2个,1%)");
|
||||||
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,10 +232,10 @@ public class DealerOrderSettlement10584Task {
|
|||||||
List<Integer> result = new ArrayList<>(2);
|
List<Integer> result = new ArrayList<>(2);
|
||||||
Set<Integer> visited = new HashSet<>();
|
Set<Integer> visited = new HashSet<>();
|
||||||
|
|
||||||
// 优先:直接从该买家(userId)的多级关系(level=1/2/3/...)里,按 level 升序找到最近的两级门店(角色shop)。
|
// 优先:直接从该买家(userId)的多级关系(level=1/2/3/...)里,按 level 升序找到最近的两级门店(type=1)。
|
||||||
// 背景:部分数据只维护“买家 -> 上级们”的多级(level)记录,但不一定维护“上级 -> 更上级”的记录;
|
// 背景:部分数据只维护“买家 -> 上级们”的多级(level)记录,但不一定维护“上级 -> 更上级”的记录;
|
||||||
// 若仅做链路向上查找,会导致门店直推/简推无法命中,收益与资金明细都不会写入。
|
// 若仅做链路向上查找,会导致门店直推/简推无法命中,收益与资金明细都不会写入。
|
||||||
// 注意:isShopRole 为扩展字段(非表字段),需用自定义SQL带出。
|
// 注意:isShopRole 为扩展字段(非表字段),由自定义SQL按 ShopDealerUser.type=1 计算带出。
|
||||||
List<ShopDealerReferee> relList = shopDealerRefereeMapper.selectRefereeChainWithShopRole(
|
List<ShopDealerReferee> relList = shopDealerRefereeMapper.selectRefereeChainWithShopRole(
|
||||||
TENANT_ID, buyerUserId, MAX_REFEREE_CHAIN_DEPTH
|
TENANT_ID, buyerUserId, MAX_REFEREE_CHAIN_DEPTH
|
||||||
);
|
);
|
||||||
@@ -247,7 +252,7 @@ public class DealerOrderSettlement10584Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兜底:若只维护了 level=1 的推荐关系,则尝试按“查两次/多次”的方式一路向上找门店角色。
|
// 兜底:若只维护了 level=1 的推荐关系,则尝试按“查两次/多次”的方式一路向上找门店(type=1)。
|
||||||
Integer current = buyerUserId;
|
Integer current = buyerUserId;
|
||||||
for (int i = 0; i < MAX_REFEREE_CHAIN_DEPTH && current != null; i++) {
|
for (int i = 0; i < MAX_REFEREE_CHAIN_DEPTH && current != null; i++) {
|
||||||
ShopDealerReferee parentRel = shopDealerRefereeMapper.selectFirstLevelRefereeWithShopRole(TENANT_ID, current);
|
ShopDealerReferee parentRel = shopDealerRefereeMapper.selectFirstLevelRefereeWithShopRole(TENANT_ID, current);
|
||||||
@@ -256,14 +261,14 @@ public class DealerOrderSettlement10584Task {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("门店(角色shop)链路向上 - buyerOrChildUserId={}, parentId={}", current, parentId);
|
log.debug("门店(type=1)链路向上 - buyerOrChildUserId={}, parentId={}", current, parentId);
|
||||||
|
|
||||||
if (!visited.add(parentId)) {
|
if (!visited.add(parentId)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentRel != null && Boolean.TRUE.equals(parentRel.getIsShopRole())) {
|
if (parentRel != null && Boolean.TRUE.equals(parentRel.getIsShopRole())) {
|
||||||
log.debug("门店(角色shop)命中 - parentId={}", parentId);
|
log.debug("门店(type=1)命中 - parentId={}", parentId);
|
||||||
result.add(parentId);
|
result.add(parentId);
|
||||||
if (result.size() >= 2) {
|
if (result.size() >= 2) {
|
||||||
break;
|
break;
|
||||||
@@ -281,6 +286,28 @@ public class DealerOrderSettlement10584Task {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 幂等:同一订单同一类型佣金,避免重复发放(用于任务重跑/补发场景)
|
||||||
|
LambdaQueryWrapper<ShopDealerCapital> idempotentQw = new LambdaQueryWrapper<ShopDealerCapital>()
|
||||||
|
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerCapital::getOrderNo, order.getOrderNo())
|
||||||
|
.eq(ShopDealerCapital::getUserId, dealerUserId);
|
||||||
|
if (comments != null) {
|
||||||
|
// 以“佣金类型前缀”做幂等键,避免 comments 细节文案调整导致重复发放。
|
||||||
|
String commentPrefix = comments;
|
||||||
|
int idx = comments.indexOf('(');
|
||||||
|
if (idx > 0) {
|
||||||
|
commentPrefix = comments.substring(0, idx) + "(";
|
||||||
|
}
|
||||||
|
idempotentQw.likeRight(ShopDealerCapital::getComments, commentPrefix);
|
||||||
|
} else {
|
||||||
|
idempotentQw.isNull(ShopDealerCapital::getComments);
|
||||||
|
}
|
||||||
|
boolean alreadyCredited = shopDealerCapitalService.count(idempotentQw) > 0;
|
||||||
|
if (alreadyCredited) {
|
||||||
|
log.info("佣金已入账,跳过 - orderNo={}, toDealerUserId={}, money={}, comments={}", order.getOrderNo(), dealerUserId, money, comments);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log.info("佣金入账 - orderNo={}, toDealerUserId={}, money={}, comments={}", order.getOrderNo(), dealerUserId, money, comments);
|
log.info("佣金入账 - orderNo={}, toDealerUserId={}, money={}, comments={}", order.getOrderNo(), dealerUserId, money, comments);
|
||||||
|
|
||||||
// 先累加佣金到分销商账户(避免并发下丢失更新,用SQL自增)
|
// 先累加佣金到分销商账户(避免并发下丢失更新,用SQL自增)
|
||||||
@@ -314,12 +341,25 @@ public class DealerOrderSettlement10584Task {
|
|||||||
newDealerUser.setMoney(BigDecimal.ZERO);
|
newDealerUser.setMoney(BigDecimal.ZERO);
|
||||||
newDealerUser.setFreezeMoney(BigDecimal.ZERO);
|
newDealerUser.setFreezeMoney(BigDecimal.ZERO);
|
||||||
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
||||||
|
// 尽量补齐基础信息,避免表字段 NOT NULL 导致插入失败(插入失败会让门店分佣“找到了人但入不了账”)。
|
||||||
|
try {
|
||||||
|
User sysUser = userMapper.selectByIdIgnoreTenant(dealerUserId);
|
||||||
|
if (sysUser != null) {
|
||||||
|
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
|
||||||
|
newDealerUser.setMobile(sysUser.getPhone());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 拉取基础信息失败不应阻断结算;后续 update 若失败会有 warn 日志。
|
||||||
|
log.warn("创建分销商账户时读取sys_user失败 - tenantId={}, dealerUserId={}", TENANT_ID, dealerUserId, e);
|
||||||
|
}
|
||||||
newDealerUser.setCreateTime(java.time.LocalDateTime.now());
|
newDealerUser.setCreateTime(java.time.LocalDateTime.now());
|
||||||
newDealerUser.setUpdateTime(java.time.LocalDateTime.now());
|
newDealerUser.setUpdateTime(java.time.LocalDateTime.now());
|
||||||
try {
|
try {
|
||||||
shopDealerUserService.save(newDealerUser);
|
shopDealerUserService.save(newDealerUser);
|
||||||
} catch (Exception ignore) {
|
} catch (Exception e) {
|
||||||
// 并发下可能已被其他线程/实例创建,忽略后继续重试入账。
|
// 并发下可能已被其他线程/实例创建;也可能是字段约束导致插入失败。
|
||||||
|
// 继续重试 update,若仍失败会输出 warn,便于定位原因。
|
||||||
|
log.warn("创建分销商账户失败 - tenantId={}, dealerUserId={}, orderNo={}", TENANT_ID, dealerUserId, order.getOrderNo(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,13 +395,35 @@ public class DealerOrderSettlement10584Task {
|
|||||||
ShopRoleCommission shopRoleCommission
|
ShopRoleCommission shopRoleCommission
|
||||||
) {
|
) {
|
||||||
// 幂等:同一订单只写一条(依赖 order_no + tenant_id 作为业务唯一)
|
// 幂等:同一订单只写一条(依赖 order_no + tenant_id 作为业务唯一)
|
||||||
boolean exists = shopDealerOrderService.count(
|
ShopDealerOrder existed = shopDealerOrderService.getOne(
|
||||||
new LambdaQueryWrapper<ShopDealerOrder>()
|
new LambdaQueryWrapper<ShopDealerOrder>()
|
||||||
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
|
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
|
||||||
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo())
|
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo())
|
||||||
) > 0;
|
.last("limit 1")
|
||||||
if (exists) {
|
);
|
||||||
|
if (existed != null) {
|
||||||
|
// 允许“补发”门店分佣时回填分红字段,避免订单已结算但分红字段一直为空,影响排查/对账。
|
||||||
|
LambdaUpdateWrapper<ShopDealerOrder> uw = new LambdaUpdateWrapper<ShopDealerOrder>()
|
||||||
|
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo());
|
||||||
|
boolean needUpdate = false;
|
||||||
|
if (existed.getFirstDividendUser() == null && shopRoleCommission.storeDirectUserId != null) {
|
||||||
|
uw.set(ShopDealerOrder::getFirstDividendUser, shopRoleCommission.storeDirectUserId);
|
||||||
|
uw.set(ShopDealerOrder::getFirstDividend, shopRoleCommission.storeDirectMoney);
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (existed.getSecondDividendUser() == null && shopRoleCommission.storeSimpleUserId != null) {
|
||||||
|
uw.set(ShopDealerOrder::getSecondDividendUser, shopRoleCommission.storeSimpleUserId);
|
||||||
|
uw.set(ShopDealerOrder::getSecondDividend, shopRoleCommission.storeSimpleMoney);
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
if (needUpdate) {
|
||||||
|
shopDealerOrderService.update(uw);
|
||||||
|
log.info("ShopDealerOrder已存在,回填门店分红字段 - orderNo={}, firstDividendUser={}, secondDividendUser={}",
|
||||||
|
order.getOrderNo(), shopRoleCommission.storeDirectUserId, shopRoleCommission.storeSimpleUserId);
|
||||||
|
} else {
|
||||||
log.info("ShopDealerOrder已存在,跳过写入 - orderNo={}", order.getOrderNo());
|
log.info("ShopDealerOrder已存在,跳过写入 - orderNo={}", order.getOrderNo());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user