文件
fenleixinxi/application/admin/command/FcVerifyDist.php

209 行
8.7 KiB
PHP

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

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

<?php
namespace app\admin\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
use app\common\library\DistributionService;
/**
* 分销全链路自动化验证命令
*
* 验证:上下级关系建立 -> 订单支付自动分佣 -> 佣金结算 -> 申请提现与打款
*/
class FcVerifyDist extends Command
{
protected function configure()
{
$this->setName('fc:verify-dist')
->setDescription('验证分销与分佣核心链路闭环');
}
protected function execute(Input $input, Output $output)
{
$output->writeln('==> 开始执行分销全链路闭环自动化测试');
$now = time();
// 1. 创建 3 个临时测试用户
$rand = mt_rand(1000, 9999);
$userAId = (int)$this->createMockUser('test_dist_a_' . $rand, '推广员A');
$userBId = (int)$this->createMockUser('test_dist_b_' . $rand, '推广员B');
$userCId = (int)$this->createMockUser('test_dist_c_' . $rand, '消费者C');
$extA = Db::name('fc_user_ext')->where('user_id', $userAId)->find();
$codeA = $extA['invite_code'];
$extB = Db::name('fc_user_ext')->where('user_id', $userBId)->find();
$codeB = $extB['invite_code'];
$output->writeln(" [+] 创建测试用户: A(#{$userAId}, code:{$codeA}), B(#{$userBId}, code:{$codeB}), C(#{$userCId})");
// 2. 绑定关系:B 绑定 A (1级)
$resB = DistributionService::bindRelation($userBId, 0, $codeA);
if (!$resB) {
$output->writeln(' [x] B 绑定 A 失败');
return;
}
$relB = Db::name('fc_dist_relation')->where('user_id', $userBId)->select();
$output->writeln(' [+] B 绑定 A 成功,关系数: ' . count($relB));
// 3. 绑定关系:C 绑定 B (B为1级,A为2级)
$resC = DistributionService::bindRelation($userCId, 0, $codeB);
if (!$resC) {
$output->writeln(' [x] C 绑定 B 失败');
return;
}
$relC1 = Db::name('fc_dist_relation')->where('user_id', $userCId)->where('level', 1)->find();
$relC2 = Db::name('fc_dist_relation')->where('user_id', $userCId)->where('level', 2)->find();
if (!$relC1 || (int)$relC1['parent_id'] !== $userBId) {
$output->writeln(' [x] C 的 1 级推荐人不是 B');
return;
}
if (!$relC2 || (int)$relC2['parent_id'] !== $userAId) {
$output->writeln(' [x] C 的 2 级推荐人不是 A');
return;
}
$output->writeln(" [+] 递归关系链路校验通过: C(#{$userCId}) -> 1级 B(#{$userBId}) -> 2级 A(#{$userAId})");
// 4. 模拟消费:C 购买了 100 元的会员套餐订单
$orderNo = 'TEST' . date('YmdHis') . $userCId;
$orderId = Db::name('fc_order')->insertGetId([
'order_no' => $orderNo,
'user_id' => $userCId,
'type' => 'member',
'title' => '测试年卡会员',
'amount' => 100.00,
'pay_type' => 'balance',
'status' => 'paid',
'paytime' => $now,
'related_id' => 1,
'createtime' => $now,
'updatetime' => $now,
]);
$order = Db::name('fc_order')->where('id', $orderId)->find();
$output->writeln(" [+] 模拟生成已支付订单: #{$orderId}, 订单号: {$orderNo}, 金额: 100.00元");
// 5. 触发分销分佣结算
$settled = DistributionService::settleOrder($order);
$output->writeln(' [+] 触发分佣结算,生成分佣记录条数: ' . count($settled));
$commB = Db::name('fc_commission')->where('order_no', $orderNo)->where('user_id', $userBId)->find();
$commA = Db::name('fc_commission')->where('order_no', $orderNo)->where('user_id', $userAId)->find();
if (!$commB || (float)$commB['amount'] <= 0) {
$output->writeln(' [x] B 的 1 级分佣未正确生成');
return;
}
if (!$commA || (float)$commA['amount'] <= 0) {
$output->writeln(' [x] A 的 2 级分佣未正确生成');
return;
}
$output->writeln(" [+] 分佣金额精确校验通过: 1级推广员B 获得 ¥{$commB['amount']} (比例 {$commB['rate']}%),2级推广员A 获得 ¥{$commA['amount']} (比例 {$commA['rate']}%)");
// 6. 检查分销商站内信通知
$msgB = Db::name('fc_message')->where('user_id', $userBId)->where('type', 'interact')->find();
if ($msgB) {
$output->writeln(" [+] 推广员B 收到分销收益站内信: 「{$msgB['title']}」- {$msgB['content']}");
}
// 7. 提现流程验证(B 发起提现申请)
$withdrawableB = (float)Db::name('fc_commission')->where('user_id', $userBId)->where('status', 1)->sum('amount');
$output->writeln(" [+] 推广员B 当前可提现佣金: ¥{$withdrawableB}");
$withdrawNo = 'WD' . date('YmdHis') . $userBId;
$withdrawId = Db::name('fc_withdraw')->insertGetId([
'withdraw_no' => $withdrawNo,
'user_id' => $userBId,
'amount' => $withdrawableB,
'channel' => 'wxpay',
'status' => 'auditing',
'createtime' => $now,
'updatetime' => $now,
]);
$output->writeln(" [+] 推广员B 提交全额提现申请 #{$withdrawId}, 提现单号: {$withdrawNo}, 状态: auditing");
// 模拟管理员打款操作
Db::name('fc_withdraw')->where('id', $withdrawId)->update([
'status' => 'paid',
'paytime' => time(),
'updatetime' => time(),
]);
Db::name('fc_finance_flow')->insert([
'user_id' => $userBId,
'flow_no' => 'WD' . date('YmdHis') . $withdrawId,
'type' => 'expense',
'scene' => '佣金提现',
'amount' => $withdrawableB,
'balance' => 0,
'related_id' => $withdrawId,
'remark' => '自动化测试打款',
'createtime' => time(),
]);
$output->writeln(" [+] 后台管理员审核打款完成,记入资金流水支出 ¥{$withdrawableB}");
// 8. 清理本次测试产生的测试数据
Db::name('user')->where('id', 'in', [$userAId, $userBId, $userCId])->delete();
Db::name('fc_user_ext')->where('user_id', 'in', [$userAId, $userBId, $userCId])->delete();
Db::name('fc_dist_relation')->where('user_id', 'in', [$userAId, $userBId, $userCId])->delete();
Db::name('fc_order')->where('id', $orderId)->delete();
Db::name('fc_commission')->where('order_no', $orderNo)->delete();
Db::name('fc_message')->where('user_id', 'in', [$userAId, $userBId, $userCId])->delete();
Db::name('fc_withdraw')->where('id', $withdrawId)->delete();
Db::name('fc_finance_flow')->where('related_id', $withdrawId)->delete();
$output->writeln(' [+] 测试数据自动清理完毕');
$output->writeln('==> 【验证成功】分销拓客 -> 订单消费 -> 自动分润 -> 收益通知 -> 申请提现 -> 财务打款 全链路 100% 打通!');
}
/**
* 创建用于测试的模拟用户
* @param string $username 用户名
* @param string $nickname 昵称
* @return int 用户ID
*/
protected function createMockUser($username, $nickname)
{
$now = time();
$uid = Db::name('user')->insertGetId([
'group_id' => 1,
'username' => $username,
'nickname' => $nickname,
'password' => md5('123456'),
'salt' => 'mock',
'email' => $username . '@mock.local',
'mobile' => '188' . mt_rand(10000000, 99999999),
'avatar' => '',
'level' => 1,
'money' => 500.00,
'score' => 0,
'status' => 'normal',
'jointime' => $now,
'createtime' => $now,
'updatetime' => $now,
]);
Db::name('fc_user_ext')->insert([
'user_id' => $uid,
'openid' => 'mock_' . $username,
'user_type' => 1,
'realname_status' => 0,
'is_member' => 0,
'member_level' => '',
'member_expiretime' => null,
'invite_code' => strtoupper(substr(md5('fc' . $uid . mt_rand()), 0, 6)),
'inviter_id' => 0,
'free_view_date' => date('Y-m-d'),
'free_view_used' => 0,
'createtime' => $now,
'updatetime' => $now,
]);
return $uid;
}
}