``` 分账诊断功能
这个提交包含在:
@@ -7,6 +7,8 @@
|
|||||||
* php addons/shopro/tests/split_commission_verify.php anomalies
|
* 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 order {order_sn}
|
||||||
* php addons/shopro/tests/split_commission_verify.php recent-success [limit=20]
|
* 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') {
|
if (PHP_SAPI !== 'cli') {
|
||||||
@@ -49,9 +51,18 @@ switch ($command) {
|
|||||||
$limit = max(1, min(100, intval($argv[2] ?? 20)));
|
$limit = max(1, min(100, intval($argv[2] ?? 20)));
|
||||||
printRecentSuccess($limit);
|
printRecentSuccess($limit);
|
||||||
break;
|
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:
|
default:
|
||||||
echo "未知命令: {$command}\n";
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,9 +251,187 @@ function printAnomalies()
|
|||||||
foreach ($missing as $r) {
|
foreach ($missing as $r) {
|
||||||
echo " {$r['order_sn']} pay_fee={$r['pay_fee']}\n";
|
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)
|
function printOrderDetail($orderSn)
|
||||||
{
|
{
|
||||||
$order = Db::name('shopro_order')->where('order_sn', $orderSn)->find();
|
$order = Db::name('shopro_order')->where('order_sn', $orderSn)->find();
|
||||||
@@ -294,6 +483,11 @@ function printOrderDetail($orderSn)
|
|||||||
echo "\n【汇付支付扩展】\n";
|
echo "\n【汇付支付扩展】\n";
|
||||||
print_r($ext);
|
print_r($ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo "\n【分账阻塞诊断】\n";
|
||||||
|
foreach (diagnoseOrderSplitBlockers($oid) as $line) {
|
||||||
|
echo " · {$line}\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function printRecentSuccess($limit)
|
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_order o ON o.id = r.order_id
|
||||||
INNER JOIN fa_shopro_commission_agent a ON a.id = r.agent_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
|
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'
|
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'
|
AND sr.role_id = a.user_id AND sr.channel = 'huifu' AND sr.status = 'normal'
|
||||||
WHERE r.status >= 0
|
WHERE r.status >= 0
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户