Files
huangjingfen/pro_v3.5.1/tests/hjf/MemberUpgradeTest.php
apple 78de918c37 Initial commit: queue workspace
Made-with: Cursor
2026-03-21 02:55:24 +08:00

481 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace tests\hjf;
use PHPUnit\Framework\TestCase;
/**
* P5-05 会员等级升级逻辑测试
*
* 测试策略:纯逻辑单元测试,不依赖数据库,
* 通过内存数组模拟用户、订单、会员等级状态,
* 复现 MemberLevelServices 的核心升级判断逻辑。
*
* 覆盖点:
* 1. 普通→创客直推3单报单商品
* 2. 直推2单不满足创客条件
* 3. 创客→云店伞下30单 + 至少3个直推人
* 4. 伞下业绩分离直推下级已是云店level≥2其团队业绩不计入
* 5. 云店→服务商伞下100单
* 6. 服务商→分公司伞下1000单
* 7. 连续升级:满足条件时自动连升多级
* 8. 最高等级4不再继续检查
* 9. 缺少3个直推人时即使业绩达标也不升级
* 10. 业绩分离完整场景3层树结构验证
*
* Class MemberUpgradeTest
* @package tests\hjf
*/
class MemberUpgradeTest extends TestCase
{
// -----------------------------------------------------------------------
// 内存模拟的会员等级引擎(复现 MemberLevelServices 核心逻辑)
// -----------------------------------------------------------------------
/** @var array<int, array> 用户表uid => {uid, spread_uid, member_level} */
private array $users = [];
/** @var array[] 报单订单表 */
private array $orders = [];
/** 升级门槛(默认值,对应 DEFAULT_DIRECT_REQUIRE / DEFAULT_UMBRELLA_REQUIRE */
private array $directRequire = [1 => 3];
private array $umbrellaRequire = [2 => 30, 3 => 100, 4 => 1000];
/** 最低直推人数要求(云店及以上升级需 ≥3 直推) */
private int $minDirectSpreadCount = 3;
// ---- 数据构建辅助 ---------------------------------------------------
private function addUser(int $uid, int $spreadUid = 0, int $memberLevel = 0): void
{
$this->users[$uid] = [
'uid' => $uid,
'spread_uid' => $spreadUid,
'member_level' => $memberLevel,
];
}
/**
* 为指定用户添加 N 笔已支付报单订单
*/
private function addQueueOrders(int $uid, int $count): void
{
for ($i = 0; $i < $count; $i++) {
$this->orders[] = [
'uid' => $uid,
'is_queue_goods' => 1,
'paid' => 1,
'is_del' => 0,
];
}
}
// ---- 核心逻辑(镜像 MemberLevelServices ----------------------------
/**
* 检查并执行升级(复现 checkUpgrade 逻辑)
*
* @return int 升级后的等级
*/
private function checkUpgrade(int $uid): int
{
if (!isset($this->users[$uid])) {
return 0;
}
$currentLevel = (int)$this->users[$uid]['member_level'];
$nextLevel = $currentLevel + 1;
if ($nextLevel > 4) {
return $currentLevel;
}
if ($this->checkLevelCondition($uid, $currentLevel, $nextLevel)) {
$this->users[$uid]['member_level'] = $nextLevel;
// 连续升级检查
return $this->checkUpgrade($uid);
}
return $currentLevel;
}
private function checkLevelCondition(int $uid, int $currentLevel, int $nextLevel): bool
{
if ($nextLevel === 1) {
$require = $this->directRequire[1];
return $this->getDirectQueueOrderCount($uid) >= $require;
}
$umbrellaRequire = $this->umbrellaRequire[$nextLevel] ?? PHP_INT_MAX;
if ($this->getUmbrellaQueueOrderCount($uid) < $umbrellaRequire) {
return false;
}
return $this->getDirectSpreadCount($uid) >= $this->minDirectSpreadCount;
}
/** 直推报单订单数仅统计直推1层 */
private function getDirectQueueOrderCount(int $uid): int
{
$directUids = $this->getDirectUids($uid);
if (empty($directUids)) {
return 0;
}
return count(array_filter(
$this->orders,
fn($o) => in_array($o['uid'], $directUids, true)
&& $o['is_queue_goods'] === 1
&& $o['paid'] === 1
&& $o['is_del'] === 0
));
}
/** 直推人数 */
private function getDirectSpreadCount(int $uid): int
{
return count($this->getDirectUids($uid));
}
/** 伞下总报单订单数含业绩分离逻辑DFS */
private function getUmbrellaQueueOrderCount(int $uid, int $maxDepth = 8): int
{
return $this->recursiveUmbrellaCount($uid, $maxDepth);
}
private function recursiveUmbrellaCount(int $uid, int $remainDepth): int
{
if ($remainDepth <= 0) {
return 0;
}
$directChildren = $this->getDirectUids($uid);
if (empty($directChildren)) {
return 0;
}
$total = 0;
foreach ($directChildren as $childUid) {
$childLevel = (int)($this->users[$childUid]['member_level'] ?? 0);
// 业绩分离直推下级已是云店level≥2跳过其团队
if ($childLevel >= 2) {
continue;
}
// 统计该下级自身的报单订单
$total += count(array_filter(
$this->orders,
fn($o) => $o['uid'] === $childUid
&& $o['is_queue_goods'] === 1
&& $o['paid'] === 1
&& $o['is_del'] === 0
));
// 递归统计下级的伞下
$total += $this->recursiveUmbrellaCount($childUid, $remainDepth - 1);
}
return $total;
}
/** 获取直推子用户 uid 列表 */
private function getDirectUids(int $uid): array
{
return array_keys(array_filter(
$this->users,
fn($u) => (int)$u['spread_uid'] === $uid
));
}
protected function setUp(): void
{
$this->users = [];
$this->orders = [];
}
// -----------------------------------------------------------------------
// 测试用例
// -----------------------------------------------------------------------
/**
* @test
* 普通→创客直推3单满足条件
*/
public function testUpgradeToLevel1With3DirectOrders(): void
{
$this->addUser(1); // 普通会员
$this->addUser(10, 1); // 直推下级A
$this->addUser(11, 1); // 直推下级B
$this->addUser(12, 1); // 直推下级C
// 直推下级各下1单报单订单
$this->addQueueOrders(10, 1);
$this->addQueueOrders(11, 1);
$this->addQueueOrders(12, 1);
$level = $this->checkUpgrade(1);
$this->assertEquals(1, $level, '直推3单应升级到创客(level=1)');
}
/**
* @test
* 直推2单不满足创客条件
*/
public function testNotUpgradeWithOnly2DirectOrders(): void
{
$this->addUser(1);
$this->addUser(10, 1);
$this->addUser(11, 1);
$this->addQueueOrders(10, 1);
$this->addQueueOrders(11, 1); // 仅2单
$level = $this->checkUpgrade(1);
$this->assertEquals(0, $level, '直推2单不满足创客条件仍为普通会员');
}
/**
* @test
* 创客→云店伞下30单 + 3个直推人
*/
public function testUpgradeToLevel2With30UmbrellaOrders(): void
{
// uid=1 已是创客level=1有3个直推
$this->addUser(1, 0, 1);
$this->addUser(10, 1);
$this->addUser(11, 1);
$this->addUser(12, 1);
// 伞下合计30单
$this->addQueueOrders(10, 10);
$this->addQueueOrders(11, 10);
$this->addQueueOrders(12, 10);
$level = $this->checkUpgrade(1);
$this->assertEquals(2, $level, '伞下30单+3直推 → 升级到云店(level=2)');
}
/**
* @test
* 伞下29单不满足云店条件
*/
public function testNotUpgradeLevel2WithOnly29Orders(): void
{
$this->addUser(1, 0, 1);
$this->addUser(10, 1);
$this->addUser(11, 1);
$this->addUser(12, 1);
$this->addQueueOrders(10, 10);
$this->addQueueOrders(11, 10);
$this->addQueueOrders(12, 9); // 总计29单
$level = $this->checkUpgrade(1);
$this->assertEquals(1, $level, '伞下29单不满足云店条件');
}
/**
* @test
* 仅2个直推人时即使业绩达标也不升级云店
*/
public function testNotUpgradeLevel2Without3DirectSpreads(): void
{
$this->addUser(1, 0, 1);
$this->addUser(10, 1); // 仅2个直推
$this->addUser(11, 1);
$this->addQueueOrders(10, 20);
$this->addQueueOrders(11, 20); // 总计40单 >= 30但直推人只有2个
$level = $this->checkUpgrade(1);
$this->assertEquals(1, $level, '直推人数<3即使业绩达标也不升级');
}
/**
* @test
* 业绩分离直推下级已是云店level=2其团队业绩不计入上级伞下
*/
public function testUmbrellaPerformanceSeparation(): void
{
/*
* 树结构:
* uid=1 (创客 level=1)
* ├─ uid=10 (云店 level=2) ← 业绩分离其下单不计入uid=1
* │ └─ uid=100 (普通) → 下了20单不计入uid=1
* ├─ uid=11 (普通 level=0) → 下了15单计入uid=1
* └─ uid=12 (普通 level=0) → 下了15单计入uid=1
*/
$this->addUser(1, 0, 1); // 待检查升级
$this->addUser(10, 1, 2); // 已是云店,业绩分离
$this->addUser(100, 10, 0); // 10的下级
$this->addUser(11, 1, 0);
$this->addUser(12, 1, 0);
$this->addQueueOrders(100, 20); // 这20单不应计入uid=1
$this->addQueueOrders(11, 15); // 计入
$this->addQueueOrders(12, 15); // 计入
$umbrella = $this->getUmbrellaQueueOrderCount(1);
$this->assertEquals(30, $umbrella, '业绩分离后仅计入11+12的30单');
// uid=1 有3个直推10/11/12伞下=30满足云店条件
$level = $this->checkUpgrade(1);
$this->assertEquals(2, $level, '业绩分离+30单达标升级到云店');
}
/**
* @test
* 业绩分离:整个已分离的子树递归不计入
*/
public function testUmbrellaPerformanceSeparationDeepTree(): void
{
/*
* uid=1 (创客) 的伞下树:
* uid=10 (云店 level=2) → 业绩分离,整个子树不计入
* └─ uid=101 → 下50单
* └─ uid=201 → 下50单
* uid=11 (普通) → 下10单计入
* uid=12 (普通) → 下10单计入
* uid=13 (普通) → 下10单计入
*/
$this->addUser(1, 0, 1);
$this->addUser(10, 1, 2); // 云店,分离
$this->addUser(101, 10, 0); // 10的子
$this->addUser(201, 101, 0); // 101的子
$this->addUser(11, 1, 0);
$this->addUser(12, 1, 0);
$this->addUser(13, 1, 0);
$this->addQueueOrders(101, 50); // 不计入uid=1
$this->addQueueOrders(201, 50); // 不计入uid=1
$this->addQueueOrders(11, 10); // 计入
$this->addQueueOrders(12, 10); // 计入
$this->addQueueOrders(13, 10); // 计入
$umbrella = $this->getUmbrellaQueueOrderCount(1);
$this->assertEquals(30, $umbrella, '深层分离树中仅计入未分离分支的30单');
}
/**
* @test
* 云店→服务商伞下100单 + 3个直推
*/
public function testUpgradeToLevel3(): void
{
$this->addUser(1, 0, 2); // 云店
for ($i = 10; $i <= 13; $i++) {
$this->addUser($i, 1, 0);
}
// 伞下合计100单
$this->addQueueOrders(10, 40);
$this->addQueueOrders(11, 30);
$this->addQueueOrders(12, 20);
$this->addQueueOrders(13, 10);
$level = $this->checkUpgrade(1);
$this->assertEquals(3, $level, '伞下100单 → 升级到服务商(level=3)');
}
/**
* @test
* 服务商→分公司伞下1000单 + 3个直推
*/
public function testUpgradeToLevel4(): void
{
$this->addUser(1, 0, 3); // 服务商
for ($i = 10; $i <= 13; $i++) {
$this->addUser($i, 1, 0);
}
// 伞下合计1000单
$this->addQueueOrders(10, 400);
$this->addQueueOrders(11, 300);
$this->addQueueOrders(12, 200);
$this->addQueueOrders(13, 100);
$level = $this->checkUpgrade(1);
$this->assertEquals(4, $level, '伞下1000单 → 升级到分公司(level=4)');
}
/**
* @test
* 已是最高等级level=4时不再检查
*/
public function testMaxLevelNoFurtherUpgrade(): void
{
$this->addUser(1, 0, 4); // 已是分公司
// 无论伞下多少单,等级不变
for ($i = 10; $i <= 14; $i++) {
$this->addUser($i, 1, 0);
$this->addQueueOrders($i, 1000);
}
$level = $this->checkUpgrade(1);
$this->assertEquals(4, $level, '已是最高等级,不再升级');
}
/**
* @test
* 连续升级:满足条件时一次 checkUpgrade 可连升多级
* 场景:普通会员同时满足创客条件,
* 但不满足云店条件伞下不足30→ 只升到创客
*/
public function testNoContinuousUpgradeWhenNextLevelNotMet(): void
{
$this->addUser(1, 0, 0); // 普通
$this->addUser(10, 1, 0);
$this->addUser(11, 1, 0);
$this->addUser(12, 1, 0);
// 满足创客直推3单但伞下仅3单 < 30不满足云店
$this->addQueueOrders(10, 1);
$this->addQueueOrders(11, 1);
$this->addQueueOrders(12, 1);
$level = $this->checkUpgrade(1);
$this->assertEquals(1, $level, '只连升到创客,云店条件不满足则停止');
}
/**
* @test
* 直推单数统计仅计算直推1层不递归
* 验证:直推的下级(孙子级)的订单不计入直推订单数
*/
public function testDirectOrderCountIsShallowOnly(): void
{
$this->addUser(1, 0, 0);
$this->addUser(10, 1, 0); // 直推
$this->addUser(100, 10, 0); // 孙子级(不是直推)
// 孙子级下2单直推层无订单
$this->addQueueOrders(100, 2);
$directCount = $this->getDirectQueueOrderCount(1);
$this->assertEquals(0, $directCount, '孙子级订单不计入直推订单数');
}
/**
* @test
* 同一用户下多单仅计算为1人的订单数不影响升级统计正确性
*/
public function testMultipleOrdersFromSameUser(): void
{
$this->addUser(1, 0, 0);
$this->addUser(10, 1, 0);
$this->addUser(11, 1, 0);
$this->addUser(12, 1, 0);
// 直推下级每人多单,直推订单数 = 订单总数(按订单统计,非按人统计)
$this->addQueueOrders(10, 3); // 3单
$this->addQueueOrders(11, 0); // 0单
$this->addQueueOrders(12, 0); // 0单
$directCount = $this->getDirectQueueOrderCount(1);
$this->assertEquals(3, $directCount, '直推订单数按订单计算,同一人多单分别统计');
// 总计3单 ≥ 3满足创客条件
$level = $this->checkUpgrade(1);
$this->assertEquals(1, $level, '直推3单来自同一人满足创客升级');
}
}