``` 分账诊断功能
这个提交包含在:
@@ -7,6 +7,8 @@
|
||||
* php addons/shopro/tests/split_commission_verify.php anomalies
|
||||
* php addons/shopro/tests/split_commission_verify.php order {order_sn}
|
||||
* php addons/shopro/tests/split_commission_verify.php recent-success [limit=20]
|
||||
* php addons/shopro/tests/split_commission_verify.php diagnose-missing [limit=20]
|
||||
* php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --execute # 补跑汇付分账(慎用)
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
@@ -49,9 +51,18 @@ switch ($command) {
|
||||
$limit = max(1, min(100, intval($argv[2] ?? 20)));
|
||||
printRecentSuccess($limit);
|
||||
break;
|
||||
case 'diagnose-missing':
|
||||
$limit = max(1, min(50, intval($argv[2] ?? 20)));
|
||||
diagnoseMissingSplitOrders($limit);
|
||||
break;
|
||||
case 'try-split':
|
||||
$sn = $argv[2] ?? '';
|
||||
$execute = in_array('--execute', $argv, true);
|
||||
trySplitOrder($sn, $execute);
|
||||
break;
|
||||
default:
|
||||
echo "未知命令: {$command}\n";
|
||||
echo "可用: summary | anomalies | order {order_sn} | recent-success [limit]\n";
|
||||
echo "可用: summary | anomalies | order {order_sn} | recent-success [limit] | diagnose-missing [limit] | try-split {order_sn} --execute\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -240,9 +251,187 @@ function printAnomalies()
|
||||
foreach ($missing as $r) {
|
||||
echo " {$r['order_sn']} pay_fee={$r['pay_fee']}\n";
|
||||
}
|
||||
echo "\n → 执行 diagnose-missing 查看每笔未分账原因\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
function fetchMissingSplitOrders($since, $limit = 20)
|
||||
{
|
||||
return Db::name('shopro_order')->alias('o')
|
||||
->where('o.pay_type', 'huifu_wechat')
|
||||
->where('o.paytime', '>=', $since)
|
||||
->whereNotExists(function ($q) {
|
||||
$q->name('shopro_split_order')->alias('so')
|
||||
->whereRaw('so.order_id = o.id')
|
||||
->where('so.channel', 'huifu')
|
||||
->where('so.status', 'in', [1, 2, 3]);
|
||||
})
|
||||
->where(function ($q) {
|
||||
$q->whereExists(function ($sq) {
|
||||
$sq->name('shopro_commission_reward')->alias('cr')
|
||||
->whereRaw('cr.order_id = o.id')
|
||||
->where('cr.status', '>=', 0);
|
||||
})->whereOr(function ($sq) {
|
||||
$sq->name('shopro_regional_agent_reward')->alias('rr')
|
||||
->whereRaw('rr.order_id = o.id')
|
||||
->where('rr.status', '>=', 0);
|
||||
});
|
||||
})
|
||||
->field('o.id, o.order_sn, o.pay_fee, o.pay_type, o.paytime, o.transaction_id')
|
||||
->order('o.id desc')
|
||||
->limit($limit)
|
||||
->select();
|
||||
}
|
||||
|
||||
function diagnoseOrderSplitBlockers($orderId)
|
||||
{
|
||||
$reasons = [];
|
||||
$order = Db::name('shopro_order')->where('id', $orderId)->find();
|
||||
if (!$order) {
|
||||
return ['订单不存在'];
|
||||
}
|
||||
if ($order['pay_type'] !== 'huifu_wechat') {
|
||||
$reasons[] = "pay_type={$order['pay_type']},非 huifu_wechat,汇付分账不会执行";
|
||||
}
|
||||
$sc = splitConfigMap();
|
||||
if (($sc['split_enable'] ?? '0') !== '1') {
|
||||
$reasons[] = '当前 split_enable=0(若支付时也为关,则当时不会分账)';
|
||||
}
|
||||
|
||||
$ext = Db::name('shopro_huifu_pay_ext')->where('order_id', $orderId)->find();
|
||||
if (!$ext) {
|
||||
$reasons[] = '无 shopro_huifu_pay_ext 记录,可能非汇付支付或扩展未写入';
|
||||
} elseif (($ext['delay_acct_flag'] ?? 'N') !== 'Y') {
|
||||
$reasons[] = '支付时 delay_acct_flag≠Y(历史配置未开延时分账),补跑 confirm 可能被汇付拒绝';
|
||||
}
|
||||
|
||||
$calcItems = [];
|
||||
if (class_exists('\\addons\\shopro\\library\\split\\SplitCalculator')) {
|
||||
$calcItems = (new \addons\shopro\library\split\SplitCalculator())->calculate($orderId);
|
||||
}
|
||||
if (empty($calcItems)) {
|
||||
$reasons[] = 'SplitCalculator 结果为空(最常见:推广员/区域无 channel=huifu 的分账接收方)';
|
||||
$rewards = Db::name('shopro_commission_reward')->alias('r')
|
||||
->join('shopro_commission_agent a', 'a.id = r.agent_id')
|
||||
->where('r.order_id', $orderId)
|
||||
->where('r.status', '>=', 0)
|
||||
->field('r.id, r.commission, a.user_id')
|
||||
->select();
|
||||
foreach ($rewards as $rw) {
|
||||
$uid = $rw['user_id'];
|
||||
$recv = Db::name('shopro_split_receiver')
|
||||
->where('role_type', 'promoter')
|
||||
->where('role_id', $uid)
|
||||
->where('channel', 'huifu')
|
||||
->where('status', 'normal')
|
||||
->find();
|
||||
$onboard = Db::name('shopro_huifu_onboard')
|
||||
->where('user_id', $uid)
|
||||
->where('role_type', 'promoter')
|
||||
->order('id desc')
|
||||
->find();
|
||||
$ob = $onboard ? ($onboard['onboard_status'] . '/' . ($onboard['huifu_id'] ?: '-')) : '无进件';
|
||||
$rc = $recv ? $recv['receiver_account'] : '无 receiver';
|
||||
$reasons[] = " 推广员 user_id={$uid} 佣金={$rw['commission']} 进件={$ob} split_receiver={$rc}";
|
||||
}
|
||||
$regRewards = Db::name('shopro_regional_agent_reward')
|
||||
->where('order_id', $orderId)
|
||||
->where('status', '>=', 0)
|
||||
->select();
|
||||
foreach ($regRewards as $rw) {
|
||||
$uid = $rw['agent_user_id'];
|
||||
$recv = Db::name('shopro_split_receiver')
|
||||
->where('role_type', 'regional_agent')
|
||||
->where('role_id', $uid)
|
||||
->where('channel', 'huifu')
|
||||
->where('status', 'normal')
|
||||
->find();
|
||||
$onboard = Db::name('shopro_huifu_onboard')
|
||||
->where('user_id', $uid)
|
||||
->where('role_type', 'regional_agent')
|
||||
->order('id desc')
|
||||
->find();
|
||||
$ob = $onboard ? ($onboard['onboard_status'] . '/' . ($onboard['huifu_id'] ?: '-')) : '无进件';
|
||||
$rc = $recv ? $recv['receiver_account'] : '无 receiver';
|
||||
$reasons[] = " 区域 user_id={$uid} 佣金={$rw['commission']} 进件={$ob} split_receiver={$rc}";
|
||||
}
|
||||
} else {
|
||||
$sum = array_sum(array_column($calcItems, 'amount'));
|
||||
$reasons[] = '当前可算出分账明细 ' . count($calcItems) . ' 条,合计 ' . $sum . ' 元(具备补跑条件,注意 delay_acct_flag)';
|
||||
}
|
||||
|
||||
$walletCnt = 0;
|
||||
$crIds = Db::name('shopro_commission_reward')->where('order_id', $orderId)->where('status', 1)->column('id');
|
||||
$rrIds = Db::name('shopro_regional_agent_reward')->where('order_id', $orderId)->where('status', 1)->column('id');
|
||||
$allIds = array_merge($crIds ?: [], $rrIds ?: []);
|
||||
if ($allIds) {
|
||||
$walletCnt = Db::name('shopro_user_wallet_log')
|
||||
->where('item_id', 'in', $allIds)
|
||||
->where('wallet_type', 'money')
|
||||
->where('type', 'in', ['commission_income', 'regional_agent_income'])
|
||||
->count();
|
||||
}
|
||||
if ($walletCnt > 0) {
|
||||
$reasons[] = "佣金已入钱包({$walletCnt} 条流水)→ 资金目前在用户余额,非汇付分账账户";
|
||||
}
|
||||
|
||||
return $reasons;
|
||||
}
|
||||
|
||||
function diagnoseMissingSplitOrders($limit)
|
||||
{
|
||||
$since = time() - 90 * 86400;
|
||||
$rows = fetchMissingSplitOrders($since, $limit);
|
||||
echo "========================================\n";
|
||||
echo " 未分账订单诊断(近90天,最多 {$limit} 笔)\n";
|
||||
echo "========================================\n\n";
|
||||
if (empty($rows)) {
|
||||
echo "无匹配订单。\n";
|
||||
return;
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
echo str_repeat('-', 60) . "\n";
|
||||
echo "订单: {$row['order_sn']} id={$row['id']} pay_fee={$row['pay_fee']}\n";
|
||||
echo '支付: ' . date('Y-m-d H:i:s', $row['paytime']) . " pay_type={$row['pay_type']}\n";
|
||||
foreach (diagnoseOrderSplitBlockers($row['id']) as $line) {
|
||||
echo " · {$line}\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
echo "补跑(需 delay_acct_flag=Y 且接收方齐全):\n";
|
||||
echo " php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --execute\n";
|
||||
}
|
||||
|
||||
function trySplitOrder($orderSn, $execute)
|
||||
{
|
||||
if ($orderSn === '') {
|
||||
fwrite(STDERR, "请指定订单号\n");
|
||||
exit(1);
|
||||
}
|
||||
$order = Db::name('shopro_order')->where('order_sn', $orderSn)->find();
|
||||
if (!$order) {
|
||||
fwrite(STDERR, "订单不存在\n");
|
||||
exit(1);
|
||||
}
|
||||
echo "诊断:\n";
|
||||
foreach (diagnoseOrderSplitBlockers($order['id']) as $line) {
|
||||
echo " · {$line}\n";
|
||||
}
|
||||
if (!$execute) {
|
||||
echo "\n未执行分账。确认后加 --execute\n";
|
||||
return;
|
||||
}
|
||||
if (!class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
|
||||
fwrite(STDERR, "未安装 shopro_huifu\n");
|
||||
exit(1);
|
||||
}
|
||||
$ok = (new \addons\shopro_huifu\library\HuifuSplitService())->executeByOrderId($order['id']);
|
||||
echo $ok ? "\n分账执行返回: 成功\n" : "\n分账执行返回: 失败(查日志 / shopro_split_order)\n";
|
||||
}
|
||||
|
||||
function printOrderDetail($orderSn)
|
||||
{
|
||||
$order = Db::name('shopro_order')->where('order_sn', $orderSn)->find();
|
||||
@@ -294,6 +483,11 @@ function printOrderDetail($orderSn)
|
||||
echo "\n【汇付支付扩展】\n";
|
||||
print_r($ext);
|
||||
}
|
||||
|
||||
echo "\n【分账阻塞诊断】\n";
|
||||
foreach (diagnoseOrderSplitBlockers($oid) as $line) {
|
||||
echo " · {$line}\n";
|
||||
}
|
||||
}
|
||||
|
||||
function printRecentSuccess($limit)
|
||||
|
||||
@@ -305,7 +305,7 @@ FROM fa_shopro_commission_reward r
|
||||
INNER JOIN fa_shopro_order o ON o.id = r.order_id
|
||||
INNER JOIN fa_shopro_commission_agent a ON a.id = r.agent_id
|
||||
INNER JOIN fa_user u ON u.id = a.user_id
|
||||
LEFT JOIN fa_shopro_huifu_onboard ho ON ho.user_id = a.user_id AND ho.status = 'approved'
|
||||
LEFT JOIN fa_shopro_huifu_onboard ho ON ho.user_id = a.user_id AND ho.onboard_status = 'approved'
|
||||
LEFT JOIN fa_shopro_split_receiver sr ON sr.role_type = 'promoter'
|
||||
AND sr.role_id = a.user_id AND sr.channel = 'huifu' AND sr.status = 'normal'
|
||||
WHERE r.status >= 0
|
||||
|
||||
在新工单中引用
屏蔽一个用户