feat(commission): 完善分销订单支付时数据同步机制

- 新增 syncExistingCommissionOrderOnPay 方法处理已存在分销单的支付同步场景
- 添加 encodeCommissionRulesSnapshot 和 commissionRulesSnapshotEquals 辅助方法
- 优化佣金规则快照存储方式,统一JSON编码格式
- 增加已入账奖励数量检查,防止对已结算订单进行错误同步

fix(wallet-settlement): 调整分账和佣金结算的余额处理逻辑

- 新增 shouldSettleCommissionViaWalletHook 和 shouldSettleRegionalViaWalletHook
  方法控制分账成功后的钱包入账行为
- 修改 shouldDeferRegionalWallet 调用为 shouldSettleRegionalViaWalletHook
- 修复分账成功时仍可能重复入钱包的问题
- 统一佣金和区域代理的钩子结算判断逻辑

refactor(hook): 优化佣金钩子触发条件和订单查询逻辑

- 在 CommissionHook 中使用新的钱包结算判断方法
- 修正 orderFinish 事件中的订单项遍历逻辑
- 增加订单有效性检查,避免空值异常
- 统一各事件中的结算条件判断

test(sql): 更新分账佣金验证SQL脚本

- 修正注释说明,明确分账成功不入余额的控制逻辑
- 补充区域代理佣金缺失汇付接收方的查询
- 重构单笔订单深度核验脚本,提供更详细的订单分析信息
- 增加分账成功与钱包流水的对比检查逻辑
```
这个提交包含在:
可乐加冰
2026-10-22 11:44:24 +08:00
父节点 b457b9b434
当前提交 b5142ff780
共有 5 个文件被更改,包括 345 次插入 和 43 次删除

查看文件

@@ -127,7 +127,7 @@ GROUP BY pay_type;
-- 网关分账:fa_shopro_split_order_receiver + receiver 表(汇付 huifu_id / 微信 openid)
-- 业务佣金:fa_shopro_commission_reward / fa_shopro_regional_agent_reward
-- 钱包入账:fa_shopro_user_wallet_log type=commission_income / regional_agent_income
-- 代码说明:shouldSkipWallet() 尚未接入 Reward,分账开启时仍可能同时入账钱包(需重点核对)
-- 代码说明:SplitWalletSettlement + shouldSettle*ViaWalletHook 控制分账成功不入余额;§9 仍用于核对历史数据
SELECT '=== 7. 分账成功订单明细(最近 30 笔)===' AS section;
@@ -298,11 +298,12 @@ LIMIT 20;
SELECT '=== 12. 有推广/区域佣金但缺少汇付接收方(导致无法分账)===' AS section;
-- 推广:r.agent_id 即分销商 user_id(非 commission_agent 表主键)
SELECT DISTINCT
r.order_id,
o.order_sn,
'commission' AS reward_source,
a.user_id AS beneficiary_user_id,
r.agent_id AS beneficiary_user_id,
u.mobile,
ho.huifu_id AS onboard_huifu_id,
sr.id AS split_receiver_id
@@ -318,6 +319,26 @@ WHERE r.status >= 0
AND sr.id IS NULL
LIMIT 30;
SELECT DISTINCT
rr.order_id,
o.order_sn,
'regional_agent' AS reward_source,
rr.agent_user_id AS beneficiary_user_id,
u.mobile,
ho.huifu_id AS onboard_huifu_id,
sr.id AS split_receiver_id
FROM fa_shopro_regional_agent_reward rr
INNER JOIN fa_shopro_order o ON o.id = rr.order_id
INNER JOIN fa_user u ON u.id = rr.agent_user_id
LEFT JOIN fa_shopro_huifu_onboard ho ON ho.user_id = rr.agent_user_id AND ho.onboard_status = 'approved'
LEFT JOIN fa_shopro_split_receiver sr ON sr.role_type = 'regional_agent'
AND sr.role_id = rr.agent_user_id AND sr.channel = 'huifu' AND sr.status = 'normal'
WHERE rr.status >= 0
AND o.pay_type = 'huifu_wechat'
AND o.paytime >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 90 DAY))
AND sr.id IS NULL
LIMIT 30;
SELECT '=== 13. 延时分账标记:支付扩展 delay_acct(近90天汇付单抽样)===' AS section;
SELECT
@@ -343,37 +364,160 @@ FROM fa_shopro_split_receiver
GROUP BY channel, role_type;
-- =============================================================================
-- 五、单笔订单深度核验(把 @order_sn 改成实际订单号后执行)
-- 五、单笔订单深度核验
-- 用法:只改下面一行 @order_id 或 @order_sn,然后整段执行
-- 预设:77 = 598单 202605495163454091004900;74 = 分账成功样例 202609215157744091003800
-- =============================================================================
/*
SET @order_sn = '2025010100000001';
SELECT o.* FROM fa_shopro_order o WHERE o.order_sn = @order_sn;
SET @order_id = 77;
-- SET @order_sn = '202605495163454091004900';
-- SET @order_id = (SELECT id FROM fa_shopro_order WHERE order_sn = @order_sn LIMIT 1);
SELECT * FROM fa_shopro_split_order WHERE order_sn = @order_sn;
SELECT sor.*, sr.receiver_account, sr.channel
FROM fa_shopro_split_order_receiver sor
SELECT '=== 5.1 订单主表 ===' AS section;
-- 确认/完成时间在 ext JSON(confirm_time / finish_time),非独立字段
SELECT id, order_sn, status, pay_type, pay_fee, total_fee,
FROM_UNIXTIME(paytime) AS pay_time,
FROM_UNIXTIME(
NULLIF(CAST(JSON_UNQUOTE(JSON_EXTRACT(ext, '$.confirm_time')) AS UNSIGNED), 0)
) AS confirm_time,
FROM_UNIXTIME(
NULLIF(CAST(JSON_UNQUOTE(JSON_EXTRACT(ext, '$.finish_time')) AS UNSIGNED), 0)
) AS finish_time,
FROM_UNIXTIME(updatetime) AS order_updated,
ext
FROM fa_shopro_order WHERE id = @order_id;
SELECT '=== 5.2 订单商品行 ===' AS section;
SELECT id AS order_item_id, goods_id, goods_title, goods_price, goods_num,
pay_price, discount_fee, ext
FROM fa_shopro_order_item WHERE order_id = @order_id;
SELECT '=== 5.3 汇付支付扩展 delay_acct ===' AS section;
SELECT order_id, delay_acct_flag, confirm_status, confirm_hf_seq_id, hf_seq_id, trans_stat,
LEFT(extra, 200) AS extra_preview
FROM fa_shopro_huifu_pay_ext WHERE order_id = @order_id;
SELECT '=== 5.4 分账单(全部状态)===' AS section;
SELECT id, channel, status,
CASE status WHEN 0 THEN '待分账' WHEN 1 THEN '分账中' WHEN 2 THEN '成功'
WHEN 3 THEN '部分成功' WHEN 4 THEN '失败' ELSE status END AS status_label,
total_amount, split_amount, confirm_hf_seq_id,
FROM_UNIXTIME(createtime) AS created_at,
LEFT(wx_result, 180) AS result_msg
FROM fa_shopro_split_order WHERE order_id = @order_id
ORDER BY id DESC;
SELECT '=== 5.5 分账接收方明细 ===' AS section;
SELECT so.order_sn, sor.role_type, sor.amount, sor.description, sor.status AS line_status,
sr.receiver_account, sr.receiver_type, sr.channel, sr.receiver_name, sr.role_id AS user_id
FROM fa_shopro_split_order so
INNER JOIN fa_shopro_split_order_receiver sor ON sor.split_order_id = so.id
LEFT JOIN fa_shopro_split_receiver sr ON sr.id = sor.receiver_id
WHERE sor.split_order_id IN (SELECT id FROM fa_shopro_split_order WHERE order_sn = @order_sn);
WHERE so.order_id = @order_id
ORDER BY so.id DESC, sor.id;
SELECT * FROM fa_shopro_commission_reward WHERE order_id = (SELECT id FROM fa_shopro_order WHERE order_sn = @order_sn LIMIT 1);
SELECT rr.*
SELECT '=== 5.6 分销单 + 推广佣金 ===' AS section;
SELECT co.id AS commission_order_id, co.order_item_id, co.amount AS dist_base,
co.commission_price_type, co.commission_event,
co.commission_reward_status, FROM_UNIXTIME(co.createtime) AS co_created,
FROM_UNIXTIME(co.commission_time) AS co_settled,
LEFT(co.commission_rules, 120) AS rules_preview
FROM fa_shopro_commission_order co
WHERE co.order_id = @order_id;
SELECT r.id AS reward_id, r.agent_id AS user_id, u.nickname, u.mobile,
r.commission_level, r.commission, r.status,
CASE r.status WHEN 0 THEN '待入账' WHEN 1 THEN '已入账' WHEN -1 THEN '已退回' ELSE r.status END AS status_label,
r.commission_rules, FROM_UNIXTIME(r.createtime) AS reward_created
FROM fa_shopro_commission_reward r
LEFT JOIN fa_user u ON u.id = r.agent_id
WHERE r.order_id = @order_id
ORDER BY r.id;
SELECT '=== 5.7 区域代理单 + 区域佣金 ===' AS section;
SELECT ro.id AS regional_order_id, ro.amount AS regional_base, ro.commission_event,
ro.commission_reward_status, FROM_UNIXTIME(ro.createtime) AS ro_created
FROM fa_shopro_regional_agent_order ro
WHERE ro.order_id = @order_id;
SELECT rr.id AS reward_id, rr.agent_user_id AS user_id, u.nickname, u.mobile,
rr.commission, rr.status,
CASE rr.status WHEN 0 THEN '待入账' WHEN 1 THEN '已入账' WHEN -1 THEN '已退回' ELSE rr.status END AS status_label,
FROM_UNIXTIME(rr.createtime) AS reward_created
FROM fa_shopro_regional_agent_reward rr
INNER JOIN fa_shopro_regional_agent_order ro ON ro.id = rr.regional_order_id
WHERE ro.order_id = (SELECT id FROM fa_shopro_order WHERE order_sn = @order_sn LIMIT 1);
LEFT JOIN fa_user u ON u.id = rr.agent_user_id
WHERE rr.order_id = @order_id
ORDER BY rr.id;
SELECT wl.*
SELECT '=== 5.8 钱包流水(佣金/区域)===' AS section;
SELECT wl.id, wl.user_id, wl.type, wl.wallet, wl.item_id,
FROM_UNIXTIME(wl.createtime) AS log_time, LEFT(wl.memo, 80) AS memo
FROM fa_shopro_user_wallet_log wl
WHERE wl.wallet_type = 'money'
AND wl.type IN ('commission_income', 'regional_agent_income', 'commission_back', 'regional_agent_back')
AND (
wl.item_id IN (SELECT id FROM fa_shopro_commission_reward WHERE order_id = (SELECT id FROM fa_shopro_order WHERE order_sn = @order_sn LIMIT 1))
OR wl.item_id IN (
SELECT rr.id FROM fa_shopro_regional_agent_reward rr
INNER JOIN fa_shopro_regional_agent_order ro ON ro.id = rr.regional_order_id
WHERE ro.order_id = (SELECT id FROM fa_shopro_order WHERE order_sn = @order_sn LIMIT 1)
)
);
wl.item_id IN (SELECT id FROM fa_shopro_commission_reward WHERE order_id = @order_id)
OR wl.item_id IN (SELECT id FROM fa_shopro_regional_agent_reward WHERE order_id = @order_id)
)
ORDER BY wl.id;
SELECT * FROM fa_shopro_huifu_pay_ext WHERE order_id = (SELECT id FROM fa_shopro_order WHERE order_sn = @order_sn LIMIT 1);
*/
SELECT '=== 5.9 已入账佣金但无钱包流水(应为通道分账/仅记 total_income)===' AS section;
SELECT 'commission' AS src, r.id AS reward_id, r.agent_id AS user_id, r.commission,
IF(wl.id IS NULL, '无 wallet_log(符合 gateway)', '有 wallet_log(可能双发)') AS wallet_check
FROM fa_shopro_commission_reward r
LEFT JOIN fa_shopro_user_wallet_log wl ON wl.wallet_type = 'money'
AND wl.type = 'commission_income' AND wl.item_id = r.id
WHERE r.order_id = @order_id AND r.status = 1
UNION ALL
SELECT 'regional', rr.id, rr.agent_user_id, rr.commission,
IF(wl.id IS NULL, '无 wallet_log(符合 gateway)', '有 wallet_log(可能双发)')
FROM fa_shopro_regional_agent_reward rr
LEFT JOIN fa_shopro_user_wallet_log wl ON wl.wallet_type = 'money'
AND wl.type = 'regional_agent_income' AND wl.item_id = rr.id
WHERE rr.order_id = @order_id AND rr.status = 1;
SELECT '=== 5.10 本单结论(与 §9 同逻辑)===' AS section;
SELECT
o.order_sn,
o.pay_fee,
IFNULL(so.split_amount, 0) AS gateway_split_sum,
IFNULL(cr.dist_sum, 0) AS dist_reward_sum,
IFNULL(rr.reg_sum, 0) AS regional_reward_sum,
IFNULL(wl.w_comm, 0) AS wallet_commission,
IFNULL(wl.w_reg, 0) AS wallet_regional,
CASE
WHEN IFNULL(wl.w_comm, 0) + IFNULL(wl.w_reg, 0) > 0 AND IFNULL(so.split_amount, 0) > 0
THEN '⚠ 网关已分账且钱包也有流水'
WHEN IFNULL(so.split_amount, 0) > 0
AND IFNULL(wl.w_comm, 0) + IFNULL(wl.w_reg, 0) = 0
THEN '✓ 分账成功且未进钱包(或仅记收益)'
WHEN IFNULL(so.id, 0) = 0 OR so.status NOT IN (2, 3)
THEN '未分账成功:佣金可能在钱包或仍待结算'
ELSE '请人工核对'
END AS check_hint
FROM fa_shopro_order o
LEFT JOIN (
SELECT order_id, MAX(id) AS id, MAX(split_amount) AS split_amount, MAX(status) AS status
FROM fa_shopro_split_order WHERE channel = 'huifu' GROUP BY order_id
) so ON so.order_id = o.id
LEFT JOIN (
SELECT order_id, SUM(commission) AS dist_sum FROM fa_shopro_commission_reward WHERE status >= 0 GROUP BY order_id
) cr ON cr.order_id = o.id
LEFT JOIN (
SELECT order_id, SUM(commission) AS reg_sum FROM fa_shopro_regional_agent_reward WHERE status >= 0 GROUP BY order_id
) rr ON rr.order_id = o.id
LEFT JOIN (
SELECT order_id,
SUM(CASE WHEN wl.type = 'commission_income' THEN wl.wallet ELSE 0 END) AS w_comm,
SUM(CASE WHEN wl.type = 'regional_agent_income' THEN wl.wallet ELSE 0 END) AS w_reg
FROM (
SELECT cr.order_id, wl.type, wl.wallet
FROM fa_shopro_commission_reward cr
INNER JOIN fa_shopro_user_wallet_log wl ON wl.item_id = cr.id AND wl.type = 'commission_income' AND wl.wallet_type = 'money'
UNION ALL
SELECT rr.order_id, wl.type, wl.wallet
FROM fa_shopro_regional_agent_reward rr
INNER JOIN fa_shopro_user_wallet_log wl ON wl.item_id = rr.id AND wl.type = 'regional_agent_income' AND wl.wallet_type = 'money'
) wl GROUP BY order_id
) wl ON wl.order_id = o.id
WHERE o.id = @order_id;