From 5f9b407e646b7666bb0a68abcde95977afb7ac93 Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 17 Sep 2026 14:04:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(partner):=20=E5=85=A8=E7=BD=91=E5=B9=B3?= =?UTF-8?q?=E7=BA=A7=E5=A5=96=E6=94=AF=E6=8C=81=E9=80=90=E4=BB=A310%?= =?UTF-8?q?=E7=BA=A7=E8=81=94=E9=80=92=E5=87=8F=E8=87=B30.01=E5=85=83?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/partner/MakerPeerReward.php | 48 +-- .../service/partner/PeerShareReward.php | 82 +++-- application/admin/view/index/login.html | 23 +- application/extra/site.php | 2 +- tests/partner/MakerPeerTest.php | 73 +++-- tests/partner/PeerShareTest.php | 300 ++++++++++++++++++ 6 files changed, 450 insertions(+), 78 deletions(-) create mode 100644 tests/partner/PeerShareTest.php diff --git a/addons/shopro/service/partner/MakerPeerReward.php b/addons/shopro/service/partner/MakerPeerReward.php index 414a4dc..1c79d02 100644 --- a/addons/shopro/service/partner/MakerPeerReward.php +++ b/addons/shopro/service/partner/MakerPeerReward.php @@ -3,7 +3,7 @@ namespace addons\shopro\service\partner; /** - * 创客平级(支持两代级联:第1代10%,第2代上级收益10%)/ 被超越 5%(只一层) + * 创客平级(逐代 10% 递减级联,最低发放 0.01 元后截断)/ 被超越 5%(只一层) */ class MakerPeerReward { @@ -12,7 +12,10 @@ class MakerPeerReward $bonus = PartnerConfig::makerBonus(); $peerRate = (float) PartnerConfig::get('partner_maker_peer_rate', 0.10); $overRate = (float) PartnerConfig::get('partner_maker_overtaken_rate', 0.05); - $maxPeerGenerations = (int) PartnerConfig::get('partner_maker_peer_max_generations', 2); + $maxPeerGenerations = (int) PartnerConfig::get('partner_maker_peer_max_generations', 50); + if ($maxPeerGenerations <= 0) { + $maxPeerGenerations = 50; + } $paid = $diffResult['paid'] ?? []; $indexByUser = []; foreach ($chain as $i => $node) { @@ -40,6 +43,9 @@ class MakerPeerReward $peer = $chain[$peerIdx]; $amount = round($currentBase * $peerRate, 2); + if ($amount < 0.01) { + break; + } if (!$active->isActive($peer['agent'] ?? $peer)) { PartnerRewardService::writeSkip( @@ -52,31 +58,29 @@ class MakerPeerReward "平级创客(第{$gen}代) 30 日无自购" ); } else { - if ($amount > 0) { - PartnerRewardService::account( - $order, - $buyerId, - $peer['user_id'], - 'maker_peer', - 'maker', - $amount, - $currentBase, - [ - 'generation' => $gen, - 'formula' => "平级(第{$gen}代) {$currentBase} × " . ($peerRate * 100) . "% = {$amount}", - 'from_user_id' => $sourceUserId, - 'level' => $item['level'], - ] - ); - } + PartnerRewardService::account( + $order, + $buyerId, + $peer['user_id'], + 'maker_peer', + 'maker', + $amount, + $currentBase, + [ + 'generation' => $gen, + 'formula' => "平级(第{$gen}代) {$currentBase} × " . ($peerRate * 100) . "% = {$amount}", + 'from_user_id' => $sourceUserId, + 'level' => $item['level'], + ] + ); } - // 下一代平级的寻源起点与计算基数:以当前平级创客为基础,基数变为当前平级奖金(即 6.00) + // 下一代平级的寻源起点与计算基数:以当前平级创客为基础,基数变为当前平级奖金 $currentFrom = $peerIdx; $currentBase = $amount; $sourceUserId = $peer['user_id']; - if ($currentBase <= 0) { + if ($currentBase < 0.01) { break; } } @@ -98,7 +102,7 @@ class MakerPeerReward PartnerRewardService::writeSkip($order, $buyerId, $lower['user_id'], 'maker_overtaken', 'maker', 'inactive_30d', '被超越创客 30 日无自购'); return; } - if ($amount <= 0) { + if ($amount < 0.01) { return; } PartnerRewardService::account($order, $buyerId, $lower['user_id'], 'maker_overtaken', 'maker', $amount, $highest['cap'], [ diff --git a/addons/shopro/service/partner/PeerShareReward.php b/addons/shopro/service/partner/PeerShareReward.php index a2deafa..8ddbf5c 100644 --- a/addons/shopro/service/partner/PeerShareReward.php +++ b/addons/shopro/service/partner/PeerShareReward.php @@ -3,7 +3,7 @@ namespace addons\shopro\service\partner; /** - * 酒道馆 / 区县平级 10%,基数为本单该下级的团队奖 + * 酒道馆 / 区县平级 10% 逐代递减级联,最低发放 0.01 元截断 */ class PeerShareReward { @@ -22,29 +22,67 @@ class PeerShareReward protected function peer($order, $buyerId, $chain, $from, $flag, $type, $role, $rate, ActiveService $active, $label) { - $start = intval($from['index']) + 1; - $peer = null; - for ($i = $start; $i < count($chain); $i++) { - if (intval($chain[$i][$flag] ?? 0)) { - $peer = $chain[$i]; + $maxGenerations = intval(PartnerConfig::get("partner_{$role}_peer_max_generations", 50)); + if ($maxGenerations <= 0) { + $maxGenerations = 50; + } + + $currentFrom = intval($from['index']); + $currentBase = (float) $from['amount']; + $sourceUserId = $from['user_id']; + + for ($gen = 1; $gen <= $maxGenerations; $gen++) { + $peerIdx = -1; + for ($i = $currentFrom + 1; $i < count($chain); $i++) { + if (intval($chain[$i][$flag] ?? 0)) { + $peerIdx = $i; + break; + } + } + if ($peerIdx < 0) { + break; + } + + $peer = $chain[$peerIdx]; + $amount = round($currentBase * $rate, 2); + if ($amount < 0.01) { + break; + } + + if (!$active->isActive($peer['agent'] ?? $peer)) { + PartnerRewardService::writeSkip( + $order, + $buyerId, + $peer['user_id'], + $type, + $role, + 'inactive_30d', + "{$label}平级(第{$gen}代) 30 日无自购" + ); + } else { + PartnerRewardService::account( + $order, + $buyerId, + $peer['user_id'], + $type, + $role, + $amount, + $currentBase, + [ + 'generation' => $gen, + 'formula' => "{$label}平级(第{$gen}代) {$currentBase} × " . ($rate * 100) . "% = {$amount}", + 'from_user_id' => $sourceUserId, + ] + ); + } + + $currentFrom = $peerIdx; + $currentBase = $amount; + $sourceUserId = $peer['user_id']; + + if ($currentBase < 0.01) { break; } } - if (!$peer) { - return; - } - $base = (float) $from['amount']; - $amount = round($base * $rate, 2); - if (!$active->isActive($peer['agent'] ?? $peer)) { - PartnerRewardService::writeSkip($order, $buyerId, $peer['user_id'], $type, $role, 'inactive_30d', "{$label}平级 30 日无自购"); - return; - } - if ($amount <= 0) { - return; - } - PartnerRewardService::account($order, $buyerId, $peer['user_id'], $type, $role, $amount, $base, [ - 'formula' => "{$label}平级 {$base} × " . ($rate * 100) . "% = {$amount}", - 'from_user_id' => $from['user_id'], - ]); } } diff --git a/application/admin/view/index/login.html b/application/admin/view/index/login.html index a2bf27d..37e25ec 100755 --- a/application/admin/view/index/login.html +++ b/application/admin/view/index/login.html @@ -31,6 +31,7 @@ body.login-body { margin: 0; + overflow-x: hidden; color: var(--text); font-family: var(--font-ui); background: var(--ink); @@ -244,10 +245,13 @@ margin: 0 0 4px; font-style: normal; font-size: 12px; - letter-spacing: 0.14em; + letter-spacing: 0.08em; color: var(--brass-deep); font-family: var(--font-mono); - text-transform: uppercase; + } + + .login-greet-text em { + letter-spacing: 0.14em; } .login-greet.has-user em { @@ -475,16 +479,25 @@ } .login-brand { - min-height: 240px; - padding: 28px 24px 24px; + min-height: 0; + justify-content: flex-start; + gap: 18px; + padding: 24px 20px 20px; + } + + .login-brand::after { + font-size: 160px; + right: -6%; + bottom: -28%; } .brand-copy { - padding: 20px 0 8px; + padding: 4px 0 0; } .brand-copy h1 { font-size: 32px; + margin-bottom: 0; } .brand-copy p, diff --git a/application/extra/site.php b/application/extra/site.php index 9f282bb..3e4597f 100755 --- a/application/extra/site.php +++ b/application/extra/site.php @@ -4,7 +4,7 @@ return [ 'name' => '我的网站', 'beian' => '', 'cdnurl' => '', - 'version' => '1.0.1', + 'version' => '1.0.2', 'timezone' => 'Asia/Shanghai', 'forbiddenip' => '', 'languages' => [ diff --git a/tests/partner/MakerPeerTest.php b/tests/partner/MakerPeerTest.php index 97647f3..c971986 100644 --- a/tests/partner/MakerPeerTest.php +++ b/tests/partner/MakerPeerTest.php @@ -49,11 +49,11 @@ class MakerPeerTest public function run() { echo "========================================================\n"; - echo " 创客两代平级级联收益计算测试 (Maker Peer Cascading) \n"; + echo " 创客多代平级级联至0.01截断测试 (Maker Peer to 0.01) \n"; echo "========================================================\n\n"; $this->testThreePeerCascade(); - $this->testFourPeerCapLimit(); + $this->testMultiPeerUntilCentCutoff(); $this->testInactiveIntermediaryCascade(); $this->testV1PeerCascade(); $this->testMultiSegmentPeers(); @@ -139,13 +139,19 @@ class MakerPeerTest } /** - * 测试 2:4 平级创客深度封顶(A -> B -> C -> D,均为 V3) - * 规则验证: - * 第3代创客 D 应严格封顶截断,不产生任何平级奖记录 + * 测试 2:多代平级持续向上拿,最少达到 0.01 元后截断 + * 链条:买家(7) -> A(2) -> B(3) -> C(4) -> D(5) -> E(6) -> F(8) (均为 V3,基数 60) + * 计算递推: + * A: 极差 60.00 + * B(3): 第1代 60 * 10% = 6.00 + * C(4): 第2代 6 * 10% = 0.60 + * D(5): 第3代 0.6 * 10% = 0.06 + * E(6): 第4代 0.06 * 10% = 0.006 -> round 0.01 元(平台最小分币) + * F(8): 第5代 0.01 * 10% = 0.001 -> round 0.00 < 0.01,严格截断,不发放 */ - public function testFourPeerCapLimit() + public function testMultiPeerUntilCentCutoff() { - $testName = "场景2: 4个平级创客(A->B->C->D) 第3代D严格封顶截断不分佣"; + $testName = "场景2: V3创客多代平级(6.00 -> 0.60 -> 0.06 -> 0.01) 到0.01后上层截断"; Db::startTrans(); try { @@ -156,6 +162,8 @@ class MakerPeerTest ['user_id' => 3, 'level' => 4], ['user_id' => 4, 'level' => 4], ['user_id' => 5, 'level' => 4], + ['user_id' => 6, 'level' => 4], + ['user_id' => 8, 'level' => 4], ]; $diffResult = [ @@ -169,13 +177,20 @@ class MakerPeerTest $reward = new MakerPeerReward(); $reward->run($order, $buyerId, $chain, $diffResult, $active); - $dReward = Db::name('shopro_partner_reward') - ->where('order_id', 9902) - ->where('user_id', 5) - ->where('reward_type', 'maker_peer') - ->find(); + $b = Db::name('shopro_partner_reward')->where('order_id', 9902)->where('user_id', 3)->find(); + $c = Db::name('shopro_partner_reward')->where('order_id', 9902)->where('user_id', 4)->find(); + $d = Db::name('shopro_partner_reward')->where('order_id', 9902)->where('user_id', 5)->find(); + $e = Db::name('shopro_partner_reward')->where('order_id', 9902)->where('user_id', 6)->find(); + $f = Db::name('shopro_partner_reward')->where('order_id', 9902)->where('user_id', 8)->find(); - $this->assert(empty($dReward), $testName, "第3代平级创客D应无平级奖记录"); + $bAmt = $b ? floatval($b['amount']) : 0; + $cAmt = $c ? floatval($c['amount']) : 0; + $dAmt = $d ? floatval($d['amount']) : 0; + $eAmt = $e ? floatval($e['amount']) : 0; + + $pass = ($bAmt == 6.00) && ($cAmt == 0.60) && ($dAmt == 0.06) && ($eAmt == 0.01) && empty($f); + + $this->assert($pass, $testName, "B={$bAmt}(6), C={$cAmt}(0.6), D={$dAmt}(0.06), E={$eAmt}(0.01), F=" . (empty($f) ? '已截断' : '未截断')); } catch (\Exception $e) { $this->assert(false, $testName, "异常: " . $e->getMessage()); } finally { @@ -241,15 +256,17 @@ class MakerPeerTest } /** - * 测试 4:V1 等级(level 2,基数 20 元)两代级联 + * 测试 4:V1 等级(level 2,基数 20 元)多代级联 * 规则验证: - * A(2): 极差 20.00 + * A: 极差 20.00 * B(3): 第1代 20 * 10% = 2.00 * C(4): 第2代 2 * 10% = 0.20 + * D(5): 第3代 0.2 * 10% = 0.02 + * E(6): 第4代 0.02 * 10% = 0.002 -> 0.00 < 0.01 截断,不发放 */ public function testV1PeerCascade() { - $testName = "场景4: V1等级(基数20元)两代级联 B得2.00元, C得0.20元"; + $testName = "场景4: V1等级(基数20元)多代级联 (2.00 -> 0.20 -> 0.02) 到0.01以下截断"; Db::startTrans(); try { @@ -259,6 +276,8 @@ class MakerPeerTest ['user_id' => 2, 'level' => 2], // Level 2 基数 20 (V1) ['user_id' => 3, 'level' => 2], ['user_id' => 4, 'level' => 2], + ['user_id' => 5, 'level' => 2], + ['user_id' => 6, 'level' => 2], ]; $diffResult = [ @@ -272,13 +291,18 @@ class MakerPeerTest $reward = new MakerPeerReward(); $reward->run($order, $buyerId, $chain, $diffResult, $active); - $bReward = Db::name('shopro_partner_reward')->where('order_id', 9904)->where('user_id', 3)->find(); - $cReward = Db::name('shopro_partner_reward')->where('order_id', 9904)->where('user_id', 4)->find(); + $b = Db::name('shopro_partner_reward')->where('order_id', 9904)->where('user_id', 3)->find(); + $c = Db::name('shopro_partner_reward')->where('order_id', 9904)->where('user_id', 4)->find(); + $d = Db::name('shopro_partner_reward')->where('order_id', 9904)->where('user_id', 5)->find(); + $e = Db::name('shopro_partner_reward')->where('order_id', 9904)->where('user_id', 6)->find(); - $bAmount = $bReward ? floatval($bReward['amount']) : 0; - $cAmount = $cReward ? floatval($cReward['amount']) : 0; + $bAmt = $b ? floatval($b['amount']) : 0; + $cAmt = $c ? floatval($c['amount']) : 0; + $dAmt = $d ? floatval($d['amount']) : 0; - $this->assert($bAmount == 2.00 && $cAmount == 0.20, $testName, "B金额={$bAmount}(期2.00), C金额={$cAmount}(期0.20)"); + $pass = ($bAmt == 2.00) && ($cAmt == 0.20) && ($dAmt == 0.02) && empty($e); + + $this->assert($pass, $testName, "B={$bAmt}(2), C={$cAmt}(0.2), D={$dAmt}(0.02), E=" . (empty($e) ? '已截断' : '未截断')); } catch (\Exception $e) { $this->assert(false, $testName, "异常: " . $e->getMessage()); } finally { @@ -289,13 +313,6 @@ class MakerPeerTest /** * 测试 5:多段平级并存(V1平级 + V3平级在同一推荐链) * 链条:买家(7) -> A(2, V1) -> B(3, V1) -> C(4, V3) -> D(5, V3) -> E(6, V3) - * diff paid: A (level 2, cap 20), C (level 4, cap 60) - * 期望: - * A(2): 极差 20.00 - * B(3): V1第1代平级 = 20 * 10% = 2.00 - * C(4): 极差 60 - 20 = 40.00 - * D(5): V3第1代平级 = 60 * 10% = 6.00 - * E(6): V3第2代平级 = 6 * 10% = 0.60 */ public function testMultiSegmentPeers() { diff --git a/tests/partner/PeerShareTest.php b/tests/partner/PeerShareTest.php new file mode 100644 index 0000000..0511670 --- /dev/null +++ b/tests/partner/PeerShareTest.php @@ -0,0 +1,300 @@ +activeMap[$userId] = $isActive; + } + + public function isActive($agent, $atTime = null) + { + $userId = is_array($agent) ? ($agent['user_id'] ?? 0) : ($agent->user_id ?? 0); + if (isset($this->activeMap[$userId])) { + return $this->activeMap[$userId]; + } + return true; + } +} + +class PeerShareTest +{ + private $passed = 0; + private $failed = 0; + + public function run() + { + echo "========================================================\n"; + echo " 酒道馆/区县平级逐代递减级联至0.01截断测试 (Peer Share) \n"; + echo "========================================================\n\n"; + + $this->testVenuePeerCascadeToCent(); + $this->testCountyPeerWithVenueCascadeToCent(); + $this->testCountyPeerNoVenueCascadeToCent(); + $this->testVenuePeerInactiveIntermediary(); + + echo "\n--------------------------------------------------------\n"; + echo "测试执行结果: 通过 {$this->passed} 项, 失败 {$this->failed} 项\n"; + echo "========================================================\n"; + return $this->failed === 0 ? 0 : 1; + } + + private function assert($condition, $testName, $detail = '') + { + if ($condition) { + echo " [PASS] {$testName}\n"; + $this->passed++; + } else { + echo " [FAIL] {$testName} - {$detail}\n"; + $this->failed++; + } + } + + /** + * 测试 1:酒道馆平级逐代递减级联(基数 50 元) + * 链条:买家(7) -> 馆A(2) -> 馆B(3) -> 馆C(4) -> 馆D(5) -> 馆E(6) -> 馆F(8) + * 预期: + * 馆B(3): 第1代 50 * 10% = 5.00 + * 馆C(4): 第2代 5 * 10% = 0.50 + * 馆D(5): 第3代 0.5 * 10% = 0.05 + * 馆E(6): 第4代 0.05 * 10% = 0.005 -> round 0.01 + * 馆F(8): 第5代 0.01 * 10% = 0.001 -> round 0.00 < 0.01,截断不发放 + */ + public function testVenuePeerCascadeToCent() + { + $testName = "酒道馆: 平级逐代递减(5.00 -> 0.50 -> 0.05 -> 0.01) 到0.01截断"; + + Db::startTrans(); + try { + $order = ['id' => 9911, 'user_id' => 7]; + $buyerId = 7; + $chain = [ + ['user_id' => 2, 'is_venue' => 1], // 馆A + ['user_id' => 3, 'is_venue' => 1], // 馆B + ['user_id' => 4, 'is_venue' => 1], // 馆C + ['user_id' => 5, 'is_venue' => 1], // 馆D + ['user_id' => 6, 'is_venue' => 1], // 馆E + ['user_id' => 8, 'is_venue' => 1], // 馆F + ]; + + $teamResult = [ + 'venue' => ['user_id' => 2, 'amount' => 50.0, 'index' => 0], + 'county' => null + ]; + + $active = new MockPeerActiveService(); + $reward = new PeerShareReward(); + $reward->run($order, $buyerId, $chain, $teamResult, $active); + + $b = Db::name('shopro_partner_reward')->where('order_id', 9911)->where('user_id', 3)->where('reward_type', 'venue_peer')->find(); + $c = Db::name('shopro_partner_reward')->where('order_id', 9911)->where('user_id', 4)->where('reward_type', 'venue_peer')->find(); + $d = Db::name('shopro_partner_reward')->where('order_id', 9911)->where('user_id', 5)->where('reward_type', 'venue_peer')->find(); + $e = Db::name('shopro_partner_reward')->where('order_id', 9911)->where('user_id', 6)->where('reward_type', 'venue_peer')->find(); + $f = Db::name('shopro_partner_reward')->where('order_id', 9911)->where('user_id', 8)->where('reward_type', 'venue_peer')->find(); + + $bAmt = $b ? floatval($b['amount']) : 0; + $cAmt = $c ? floatval($c['amount']) : 0; + $dAmt = $d ? floatval($d['amount']) : 0; + $eAmt = $e ? floatval($e['amount']) : 0; + + $pass = ($bAmt == 5.00) && ($cAmt == 0.50) && ($dAmt == 0.05) && ($eAmt == 0.01) && empty($f); + + $this->assert($pass, $testName, "B={$bAmt}(5), C={$cAmt}(0.5), D={$dAmt}(0.05), E={$eAmt}(0.01), F=" . (empty($f) ? '已截断' : '未截断')); + } catch (\Exception $e) { + $this->assert(false, $testName, "异常: " . $e->getMessage()); + } finally { + Db::rollback(); + } + } + + /** + * 测试 2:区县代理(有馆拆分场景,基数 50 元)逐代递减级联至 0.01 截断 + * 预期: + * 县B(3): 第1代 50 * 10% = 5.00 + * 县C(4): 第2代 0.50 + * 县D(5): 第3代 0.05 + * 县E(6): 第4代 0.01 + * 县F(8): 截断 + */ + public function testCountyPeerWithVenueCascadeToCent() + { + $testName = "区县代理(有馆): 平级逐代递减(5.00 -> 0.50 -> 0.05 -> 0.01) 到0.01截断"; + + Db::startTrans(); + try { + $order = ['id' => 9912, 'user_id' => 7]; + $buyerId = 7; + $chain = [ + ['user_id' => 2, 'is_county' => 1], + ['user_id' => 3, 'is_county' => 1], + ['user_id' => 4, 'is_county' => 1], + ['user_id' => 5, 'is_county' => 1], + ['user_id' => 6, 'is_county' => 1], + ['user_id' => 8, 'is_county' => 1], + ]; + + $teamResult = [ + 'venue' => ['user_id' => 1, 'amount' => 50.0, 'index' => -1], + 'county' => ['user_id' => 2, 'amount' => 50.0, 'index' => 0] + ]; + + $active = new MockPeerActiveService(); + $reward = new PeerShareReward(); + $reward->run($order, $buyerId, $chain, $teamResult, $active); + + $b = Db::name('shopro_partner_reward')->where('order_id', 9912)->where('user_id', 3)->where('reward_type', 'county_peer')->find(); + $c = Db::name('shopro_partner_reward')->where('order_id', 9912)->where('user_id', 4)->where('reward_type', 'county_peer')->find(); + $d = Db::name('shopro_partner_reward')->where('order_id', 9912)->where('user_id', 5)->where('reward_type', 'county_peer')->find(); + $e = Db::name('shopro_partner_reward')->where('order_id', 9912)->where('user_id', 6)->where('reward_type', 'county_peer')->find(); + $f = Db::name('shopro_partner_reward')->where('order_id', 9912)->where('user_id', 8)->where('reward_type', 'county_peer')->find(); + + $bAmt = $b ? floatval($b['amount']) : 0; + $cAmt = $c ? floatval($c['amount']) : 0; + $dAmt = $d ? floatval($d['amount']) : 0; + $eAmt = $e ? floatval($e['amount']) : 0; + + $pass = ($bAmt == 5.00) && ($cAmt == 0.50) && ($dAmt == 0.05) && ($eAmt == 0.01) && empty($f); + + $this->assert($pass, $testName, "B={$bAmt}(5), C={$cAmt}(0.5), D={$dAmt}(0.05), E={$eAmt}(0.01), F=" . (empty($f) ? '已截断' : '未截断')); + } catch (\Exception $e) { + $this->assert(false, $testName, "异常: " . $e->getMessage()); + } finally { + Db::rollback(); + } + } + + /** + * 测试 3:区县代理(无馆独享全额 100 元)逐代递减级联至 0.01 截断 + * 预期: + * 县B(3): 第1代 100 * 10% = 10.00 + * 县C(4): 第2代 10 * 10% = 1.00 + * 县D(5): 第3代 1 * 10% = 0.10 + * 县E(6): 第4代 0.10 * 10% = 0.01 + * 县F(8): 第5代 0.01 * 10% = 0.001 -> 0.00 截断 + */ + public function testCountyPeerNoVenueCascadeToCent() + { + $testName = "区县代理(无馆): 平级逐代递减(10.00 -> 1.00 -> 0.10 -> 0.01) 到0.01截断"; + + Db::startTrans(); + try { + $order = ['id' => 9913, 'user_id' => 7]; + $buyerId = 7; + $chain = [ + ['user_id' => 2, 'is_county' => 1], + ['user_id' => 3, 'is_county' => 1], + ['user_id' => 4, 'is_county' => 1], + ['user_id' => 5, 'is_county' => 1], + ['user_id' => 6, 'is_county' => 1], + ['user_id' => 8, 'is_county' => 1], + ]; + + $teamResult = [ + 'venue' => null, + 'county' => ['user_id' => 2, 'amount' => 100.0, 'index' => 0] + ]; + + $active = new MockPeerActiveService(); + $reward = new PeerShareReward(); + $reward->run($order, $buyerId, $chain, $teamResult, $active); + + $b = Db::name('shopro_partner_reward')->where('order_id', 9913)->where('user_id', 3)->where('reward_type', 'county_peer')->find(); + $c = Db::name('shopro_partner_reward')->where('order_id', 9913)->where('user_id', 4)->where('reward_type', 'county_peer')->find(); + $d = Db::name('shopro_partner_reward')->where('order_id', 9913)->where('user_id', 5)->where('reward_type', 'county_peer')->find(); + $e = Db::name('shopro_partner_reward')->where('order_id', 9913)->where('user_id', 6)->where('reward_type', 'county_peer')->find(); + $f = Db::name('shopro_partner_reward')->where('order_id', 9913)->where('user_id', 8)->where('reward_type', 'county_peer')->find(); + + $bAmt = $b ? floatval($b['amount']) : 0; + $cAmt = $c ? floatval($c['amount']) : 0; + $dAmt = $d ? floatval($d['amount']) : 0; + $eAmt = $e ? floatval($e['amount']) : 0; + + $pass = ($bAmt == 10.00) && ($cAmt == 1.00) && ($dAmt == 0.10) && ($eAmt == 0.01) && empty($f); + + $this->assert($pass, $testName, "B={$bAmt}(10), C={$cAmt}(1), D={$dAmt}(0.1), E={$eAmt}(0.01), F=" . (empty($f) ? '已截断' : '未截断')); + } catch (\Exception $e) { + $this->assert(false, $testName, "异常: " . $e->getMessage()); + } finally { + Db::rollback(); + } + } + + /** + * 测试 4:酒道馆中间平级未自购跳过场景 + */ + public function testVenuePeerInactiveIntermediary() + { + $testName = "酒道馆: 中间平级馆B未自购被跳过,上级馆C仍正常获得第2代平级0.50元"; + + Db::startTrans(); + try { + $order = ['id' => 9914, 'user_id' => 7]; + $buyerId = 7; + $chain = [ + ['user_id' => 2, 'is_venue' => 1], + ['user_id' => 3, 'is_venue' => 1], + ['user_id' => 4, 'is_venue' => 1], + ]; + + $teamResult = [ + 'venue' => ['user_id' => 2, 'amount' => 50.0, 'index' => 0], + 'county' => null + ]; + + $active = new MockPeerActiveService(); + $active->setActive(3, false); // 馆B 未自购 + $active->setActive(4, true); // 馆C 活跃 + + $reward = new PeerShareReward(); + $reward->run($order, $buyerId, $chain, $teamResult, $active); + + $bSkip = Db::name('shopro_partner_reward') + ->where('order_id', 9914) + ->where('user_id', 3) + ->where('status', -1) + ->find(); + + $cReward = Db::name('shopro_partner_reward') + ->where('order_id', 9914) + ->where('user_id', 4) + ->where('status', 1) + ->find(); + + $bSkipped = !empty($bSkip) && floatval($bSkip['amount']) == 0; + $cReceived = !empty($cReward) && floatval($cReward['amount']) == 0.50; + + $this->assert($bSkipped && $cReceived, $testName, "B被跳过记录, C正常结算0.50元"); + } catch (\Exception $e) { + $this->assert(false, $testName, "异常: " . $e->getMessage()); + } finally { + Db::rollback(); + } + } +} + +// 执行测试 +if (php_sapi_name() === 'cli') { + $tester = new PeerShareTest(); + exit($tester->run()); +}