文件
xionggeshop/addons/shopro/tests/commission_verify.php

273 行
10 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?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;
}