273 行
10 KiB
PHP
273 行
10 KiB
PHP
<?php
|
||
/**
|
||
* 分销佣金测试 / 验证脚本
|
||
*
|
||
* 用法(项目根目录):
|
||
* php addons/shopro/tests/commission_verify.php scenarios # 打印测试步骤 + 预期每人佣金
|
||
* php addons/shopro/tests/commission_verify.php calc # 仅跑内置场景计算器
|
||
* php addons/shopro/tests/commission_verify.php verify-item 123 # 对照订单明细ID的实际佣金记录
|
||
* php addons/shopro/tests/commission_verify.php list-config # 打印当前分销配置与等级档位
|
||
*/
|
||
|
||
if (PHP_SAPI !== 'cli') {
|
||
http_response_code(403);
|
||
exit("请通过命令行运行\n");
|
||
}
|
||
|
||
define('APP_PATH', __DIR__ . '/../../../application/');
|
||
define('ROOT_PATH', __DIR__ . '/../../../');
|
||
|
||
require __DIR__ . '/CommissionScenarioCalculator.php';
|
||
|
||
use addons\shopro\tests\CommissionScenarioCalculator;
|
||
use think\Db;
|
||
|
||
$argv = $GLOBALS['argv'] ?? [];
|
||
$command = $argv[1] ?? 'scenarios';
|
||
|
||
if ($command === 'calc') {
|
||
runCalcOnly();
|
||
exit(0);
|
||
}
|
||
|
||
if (!is_file(APP_PATH . 'admin/command/Install/install.lock')) {
|
||
fwrite(STDERR, "项目未安装,无法连接数据库。可先用: php addons/shopro/tests/commission_verify.php calc\n");
|
||
if ($command === 'scenarios') {
|
||
runScenarios(false);
|
||
}
|
||
exit($command === 'scenarios' ? 0 : 1);
|
||
}
|
||
|
||
require ROOT_PATH . 'thinkphp/base.php';
|
||
\think\App::initCommon();
|
||
|
||
switch ($command) {
|
||
case 'scenarios':
|
||
runScenarios(true);
|
||
break;
|
||
case 'verify-item':
|
||
$itemId = intval($argv[2] ?? 0);
|
||
if (!$itemId) {
|
||
fwrite(STDERR, "请指定订单明细ID: verify-item {order_item_id}\n");
|
||
exit(1);
|
||
}
|
||
verifyOrderItem($itemId);
|
||
break;
|
||
case 'list-config':
|
||
listConfig();
|
||
break;
|
||
default:
|
||
echo "未知命令: {$command}\n";
|
||
echo "可用: scenarios | calc | verify-item {id} | list-config\n";
|
||
exit(1);
|
||
}
|
||
|
||
function runCalcOnly()
|
||
{
|
||
runScenarios(false);
|
||
}
|
||
|
||
function runScenarios($withDbHint)
|
||
{
|
||
$scenarios = CommissionScenarioCalculator::builtInScenarios();
|
||
echo "========================================\n";
|
||
echo " 分销佣金测试场景 · 预期结果\n";
|
||
echo "========================================\n\n";
|
||
echo "【统一测试参数】\n";
|
||
echo " 订单结算金额: 1000 元(商品价 × 1)\n";
|
||
echo " 分销层级: 三级\n";
|
||
echo " 等级 L1: 关系链10%/0%/0%,级差档40元,管理奖5%\n";
|
||
echo " 等级 L2: 关系链10%/5%/0%,级差档80元,管理奖3%\n";
|
||
echo " 等级 L3: 关系链10%/5%/3%,级差档120元,管理奖2%\n";
|
||
echo " 上级链: C(直推/L1) → B(二级/L2) → A(三级/L3)\n\n";
|
||
|
||
foreach ($scenarios as $key => $scenario) {
|
||
echo str_repeat('-', 60) . "\n";
|
||
echo "{$scenario['title']}\n";
|
||
echo str_repeat('-', 60) . "\n";
|
||
echo "操作步骤:\n";
|
||
foreach ($scenario['steps'] as $i => $step) {
|
||
echo " {$step}\n";
|
||
}
|
||
echo "\n预期佣金:\n";
|
||
|
||
$result = CommissionScenarioCalculator::calculate($scenario['options']);
|
||
$byPerson = [];
|
||
foreach ($result['rewards'] as $row) {
|
||
$name = $row['name'];
|
||
if (!isset($byPerson[$name])) {
|
||
$byPerson[$name] = [];
|
||
}
|
||
$byPerson[$name][] = $row;
|
||
}
|
||
|
||
printf(" %-16s %-10s %-6s %s\n", '人员', '类型', '层级', '金额(元)');
|
||
foreach ($result['rewards'] as $row) {
|
||
$typeMap = [
|
||
'chain' => '推广佣金',
|
||
'differential' => '级差奖',
|
||
'management' => '管理奖',
|
||
'repurchase_self' => '复购返现',
|
||
];
|
||
$typeName = $typeMap[$row['award_type']] ?? $row['award_type'];
|
||
printf(" %-16s %-10s %-6s %.2f\n", $row['name'], $typeName, $row['commission_level'], $row['commission']);
|
||
}
|
||
echo "\n 合计发放: {$result['total']} 元\n";
|
||
|
||
echo "\n 按人汇总:\n";
|
||
foreach ($byPerson as $name => $rows) {
|
||
$sum = 0;
|
||
$parts = [];
|
||
foreach ($rows as $r) {
|
||
$sum += $r['commission'];
|
||
$parts[] = ($r['award_type'] === 'chain' ? '推广' : ($r['award_type'] === 'differential' ? '级差' : ($r['award_type'] === 'management' ? '管理' : '复购'))) . $r['commission'];
|
||
}
|
||
echo " {$name}: " . implode(' + ', $parts) . " = " . round($sum, 2) . " 元\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
echo str_repeat('=', 60) . "\n";
|
||
echo "验证 SQL(支付后执行):\n";
|
||
echo " SELECT r.id, r.agent_id, u.nickname, r.commission, r.commission_level, r.commission_rules, r.status\n";
|
||
echo " FROM fa_shopro_commission_reward r\n";
|
||
echo " LEFT JOIN fa_user u ON u.id = r.agent_id\n";
|
||
echo " WHERE r.order_item_id = {订单明细ID}\n";
|
||
echo " ORDER BY r.id;\n\n";
|
||
|
||
if ($withDbHint) {
|
||
echo "当前环境配置:\n";
|
||
listConfig(false);
|
||
}
|
||
}
|
||
|
||
function listConfig($withHeader = true)
|
||
{
|
||
if ($withHeader) {
|
||
echo "=== 分销配置 ===\n";
|
||
}
|
||
$keys = [
|
||
'chain_commission_enable', 'differential_commission_enable', 'management_award_enable',
|
||
'repurchase_referrer_enable', 'repurchase_self_enable', 'repurchase_self_type', 'repurchase_self_amount',
|
||
'commission_level', 'self_buy', 'commission_price_type',
|
||
];
|
||
$rows = Db::name('shopro_commission_config')->where('name', 'in', $keys)->column('value', 'name');
|
||
foreach ($keys as $k) {
|
||
echo " {$k}: " . ($rows[$k] ?? '(未配置)') . "\n";
|
||
}
|
||
echo "\n=== 分销商等级档位 ===\n";
|
||
$levels = Db::name('shopro_commission_agent_level')->order('level asc')->select();
|
||
foreach ($levels as $lv) {
|
||
$rules = is_string($lv['commission_rules']) ? json_decode($lv['commission_rules'], true) : $lv['commission_rules'];
|
||
$rules = is_array($rules) ? $rules : [];
|
||
echo " [{$lv['level']}] {$lv['name']}: ";
|
||
echo "链 " . ($rules['commission_1'] ?? 0) . "/" . ($rules['commission_2'] ?? 0) . "/" . ($rules['commission_3'] ?? 0) . "%";
|
||
echo " | 级差档 " . ($rules['differential_amount'] ?? 0) . " 元";
|
||
echo " | 管理奖 " . ($rules['management_rate'] ?? 0) . "%\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
function verifyOrderItem($itemId)
|
||
{
|
||
echo "=== 验证订单明细 #{$itemId} ===\n\n";
|
||
|
||
$item = Db::name('shopro_order_item')->where('id', $itemId)->find();
|
||
if (!$item) {
|
||
fwrite(STDERR, "订单明细不存在\n");
|
||
exit(1);
|
||
}
|
||
|
||
$order = Db::name('shopro_order')->where('id', $item['order_id'])->find();
|
||
$buyer = Db::name('user')->where('id', $item['user_id'])->field('id,nickname,parent_user_id')->find();
|
||
$goodsCommission = Db::name('shopro_commission_goods')->where('goods_id', $item['goods_id'])->find();
|
||
|
||
echo "订单: {$order['order_sn']} | 买家: {$buyer['nickname']}({$buyer['id']})\n";
|
||
echo "商品ID: {$item['goods_id']} | 数量: {$item['goods_num']} | pay_price: {$item['pay_price']}\n";
|
||
if ($goodsCommission) {
|
||
echo "分销商品: 参与=" . ($goodsCommission['status'] ?? 0);
|
||
echo " 级差=" . ($goodsCommission['differential_enable'] ?? 0);
|
||
echo " 管理=" . ($goodsCommission['management_enable'] ?? 0);
|
||
echo " 复购=" . ($goodsCommission['repurchase_enable'] ?? 0) . "\n";
|
||
} else {
|
||
echo "分销商品: 未配置\n";
|
||
}
|
||
|
||
$commissionOrder = Db::name('shopro_commission_order')->where('order_item_id', $itemId)->find();
|
||
if (!$commissionOrder) {
|
||
echo "\n[WARN] 尚无分销订单记录,请确认已支付且商品参与分销\n";
|
||
exit(0);
|
||
}
|
||
echo "分销订单ID: {$commissionOrder['id']} | 结算金额: {$commissionOrder['amount']} | 自购: {$commissionOrder['self_buy']}\n";
|
||
|
||
$priorOrders = Db::name('shopro_order')
|
||
->where('user_id', $item['user_id'])
|
||
->where('status', 'in', [1, 2])
|
||
->where('id', '<', $item['order_id'])
|
||
->count();
|
||
echo "是否复购: " . ($priorOrders > 0 ? '是' : '否') . " (历史有效订单 {$priorOrders} 笔)\n\n";
|
||
|
||
$rewards = Db::name('shopro_commission_reward')
|
||
->alias('r')
|
||
->join('user u', 'u.id = r.agent_id', 'LEFT')
|
||
->where('r.order_item_id', $itemId)
|
||
->field('r.*, u.nickname')
|
||
->order('r.id asc')
|
||
->select();
|
||
|
||
if (!$rewards) {
|
||
echo "[WARN] 无佣金记录\n";
|
||
exit(0);
|
||
}
|
||
|
||
$typeMap = [
|
||
'chain' => '推广佣金',
|
||
'differential' => '级差奖',
|
||
'management' => '管理奖',
|
||
'repurchase_self' => '复购返现',
|
||
];
|
||
|
||
echo "实际佣金记录:\n";
|
||
printf(" %-6s %-8s %-14s %-8s %-6s %s\n", 'ID', '用户ID', '昵称', '类型', '层级', '金额');
|
||
$total = 0;
|
||
foreach ($rewards as $r) {
|
||
$rules = json_decode($r['commission_rules'], true);
|
||
$type = $rules['award_type'] ?? 'chain';
|
||
if ($type === 'chain' && intval($r['commission_level']) === 0) {
|
||
$type = 'repurchase_self';
|
||
}
|
||
$typeName = $typeMap[$type] ?? $type;
|
||
printf(" %-6s %-8s %-14s %-8s %-6s %.2f\n", $r['id'], $r['agent_id'], $r['nickname'] ?? '-', $typeName, $r['commission_level'], $r['commission']);
|
||
$total += floatval($r['commission']);
|
||
}
|
||
echo "\n 实际合计: " . round($total, 2) . " 元\n";
|
||
|
||
$chain = buildAgentChain(intval($commissionOrder['agent_id']), intval(Db::name('shopro_commission_config')->where('name', 'commission_level')->value('value') ?: 3));
|
||
if ($chain) {
|
||
echo "\n上级链(从分销订单 agent_id 起): " . implode(' → ', array_column($chain, 'name')) . "\n";
|
||
}
|
||
|
||
echo "\n[INFO] 可将等级档位改为与内置场景一致后,用 scenarios 命令对照预期\n";
|
||
}
|
||
|
||
function buildAgentChain($startUserId, $maxLevel)
|
||
{
|
||
$chain = [];
|
||
$userId = $startUserId;
|
||
$depth = 1;
|
||
while ($userId && $depth <= $maxLevel) {
|
||
$agent = Db::name('shopro_commission_agent')->where('user_id', $userId)->where('status', 'normal')->find();
|
||
if (!$agent) {
|
||
break;
|
||
}
|
||
$user = Db::name('user')->where('id', $userId)->field('id,nickname,parent_user_id')->find();
|
||
$chain[] = [
|
||
'name' => ($user['nickname'] ?? $userId) . "(L{$depth})",
|
||
'user_id' => $userId,
|
||
];
|
||
$userId = intval($user['parent_user_id'] ?? 0);
|
||
$depth++;
|
||
}
|
||
return $chain;
|
||
}
|