getRecallGiftTiers(5); $this->assertCount(3, $tiers); $this->assertEquals(9, $tiers[0]['amount']); $this->assertEquals(19, $tiers[1]['amount']); $this->assertEquals(29, $tiers[2]['amount']); $this->assertEquals(70, $tiers[0]['bonus_percent']); $this->assertEquals(85, $tiers[1]['bonus_percent']); $this->assertEquals(100, $tiers[2]['bonus_percent']); } /** @test */ public function it_selects_tiers_based_on_average_recharge() { // avgRecharge = 15,初档应为第一个 >= 15 的档位 = 19 $tiers = $this->getRecallGiftTiers(15); $this->assertCount(3, $tiers); $this->assertEquals(19, $tiers[0]['amount']); $this->assertEquals(29, $tiers[1]['amount']); $this->assertEquals(39, $tiers[2]['amount']); } /** @test */ public function it_selects_last_three_tiers_when_avg_above_maximum() { // avgRecharge = 100 > 99,取最后三位:59, 79, 99 $tiers = $this->getRecallGiftTiers(100); $this->assertCount(3, $tiers); $this->assertEquals(59, $tiers[0]['amount']); $this->assertEquals(79, $tiers[1]['amount']); $this->assertEquals(99, $tiers[2]['amount']); $this->assertEquals(70, $tiers[0]['bonus_percent']); // 初档 $this->assertEquals(85, $tiers[1]['bonus_percent']); // 中档 $this->assertEquals(100, $tiers[2]['bonus_percent']); // 高档 } /** @test */ public function it_handles_exact_boundary_values() { // avgRecharge = 9,恰好等于初档 $tiers = $this->getRecallGiftTiers(9); $this->assertEquals(9, $tiers[0]['amount']); // avgRecharge = 29,初档应为 29 $tiers = $this->getRecallGiftTiers(29); $this->assertEquals(29, $tiers[0]['amount']); $this->assertEquals(39, $tiers[1]['amount']); $this->assertEquals(49, $tiers[2]['amount']); } /** @test */ public function it_handles_avg_between_tiers() { // avgRecharge = 25,第一个 >= 25 的是 29 $tiers = $this->getRecallGiftTiers(25); $this->assertEquals(29, $tiers[0]['amount']); $this->assertEquals(39, $tiers[1]['amount']); $this->assertEquals(49, $tiers[2]['amount']); } /** @test */ public function distribution_sums_to_100_percent() { $distribution = [7, 9, 11, 13, 14, 21, 25]; $this->assertEquals(100, array_sum($distribution)); } /** @test */ public function daily_amounts_match_distribution() { // 如果总奖励 = $10.00,按比例分布 $totalBonus = 10.00; $distribution = [7, 9, 11, 13, 14, 21, 25]; $dayAmounts = []; for ($i = 0; $i < 7; $i++) { $dayAmounts[] = round($totalBonus * $distribution[$i] / 100, 2); } $this->assertEquals(0.70, $dayAmounts[0]); // Day 1: 7% $this->assertEquals(0.90, $dayAmounts[1]); // Day 2: 9% $this->assertEquals(1.10, $dayAmounts[2]); // Day 3: 11% $this->assertEquals(1.30, $dayAmounts[3]); // Day 4: 13% $this->assertEquals(1.40, $dayAmounts[4]); // Day 5: 14% $this->assertEquals(2.10, $dayAmounts[5]); // Day 6: 21% $this->assertEquals(2.50, $dayAmounts[6]); // Day 7: 25% // 总和应等于 $10.00(允许浮点误差) $this->assertEquals(10.00, round(array_sum($dayAmounts), 2)); } /** @test */ public function daily_amounts_match_distribution_realistic_bonus() { // payAmt=19, bonusPercent=70% → totalBonus = 19 * 0.70 = 13.30 $payAmt = 19.00; $bonusPercent = 70; $totalBonus = round($payAmt * $bonusPercent / 100, 2); $this->assertEquals(13.30, $totalBonus); $distribution = [7, 9, 11, 13, 14, 21, 25]; $dayAmounts = []; for ($i = 0; $i < 7; $i++) { $dayAmounts[] = round($totalBonus * $distribution[$i] / 100, 2); } $this->assertEquals(0.93, $dayAmounts[0]); // 13.30 * 7% = 0.931 $this->assertEquals(1.20, $dayAmounts[1]); // 13.30 * 9% = 1.197 $this->assertEquals(1.46, $dayAmounts[2]); // 13.30 * 11% = 1.463 $this->assertEquals(1.73, $dayAmounts[3]); // 13.30 * 13% = 1.729 $this->assertEquals(1.86, $dayAmounts[4]); // 13.30 * 14% = 1.862 $this->assertEquals(2.79, $dayAmounts[5]); // 13.30 * 21% = 2.793 $this->assertEquals(3.33, $dayAmounts[6]); // 13.30 * 25% = 3.325 } /** @test */ public function inactive_days_calculation_is_correct() { // 模拟:最后一次充值在 7 天前 $lastDate = date('Y-m-d', strtotime('-7 days')); $today = date('Y-m-d'); $diffDays = (strtotime($today) - strtotime($lastDate)) / 86400; $inactiveDays = max(0, (int)$diffDays - 1); // 7天前充值 → diffDays=7 → inactiveDays=6 // 但需要连续7天未充值,所以 lastPay 应是8天前 $this->assertEquals(6, $inactiveDays); // 8天前充值 → diffDays=8 → inactiveDays=7(满足条件) $lastDate = date('Y-m-d', strtotime('-8 days')); $diffDays = (strtotime($today) - strtotime($lastDate)) / 86400; $inactiveDays = max(0, (int)$diffDays - 1); $this->assertEquals(7, $inactiveDays); } /** @test */ public function day_offset_from_recharge_to_signin() { // 如果充值在 2026-05-14 $createdAt = '2026-05-14'; // 签到第1天 = 充值次日 = 2026-05-15 $day1Target = date('Ymd', strtotime($createdAt . ' +1 days')); $this->assertEquals('20260515', $day1Target); // 签到第7天 = 充值 + 7天 = 2026-05-21 $day7Target = date('Ymd', strtotime($createdAt . ' +7 days')); $this->assertEquals('20260521', $day7Target); } /** @test */ public function days_passed_calculation() { // 充值当天 $createdAt = '2026-05-14'; $today = '2026-05-14'; $daysPassed = floor((strtotime($today) - strtotime($createdAt)) / 86400); $this->assertEquals(0, $daysPassed); // 还不能签到 // 充值次日 $today = '2026-05-15'; $daysPassed = floor((strtotime($today) - strtotime($createdAt)) / 86400); $this->assertEquals(1, $daysPassed); // 可以签到第1天 // 充值7天后 $today = '2026-05-21'; $daysPassed = floor((strtotime($today) - strtotime($createdAt)) / 86400); $this->assertEquals(7, $daysPassed); // 可以签到第7天 // 充值8天后(过期) $today = '2026-05-22'; $daysPassed = floor((strtotime($today) - strtotime($createdAt)) / 86400); $this->assertEquals(8, $daysPassed); // 超过7天,过期 } /** @test */ public function claim_day_mapping() { // 按实际签到次数确定天数,不再依赖自然日 // 已签到0次 → day=1 (第1次签到) → dayIndex=0 → 7% $claimedDaysCount = 0; $day = $claimedDaysCount + 1; $dayIndex = $day - 1; // 0 $distribution = [7, 9, 11, 13, 14, 21, 25]; $this->assertEquals(0, $dayIndex); $this->assertEquals(7, $distribution[$dayIndex]); // 已签到1次 → day=2 (第2次签到) → dayIndex=1 → 9% $claimedDaysCount = 1; $day = $claimedDaysCount + 1; $dayIndex = $day - 1; // 1 $this->assertEquals(1, $dayIndex); $this->assertEquals(9, $distribution[$dayIndex]); // 已签到3次 → day=4 (第4次签到) → dayIndex=3 → 13% $claimedDaysCount = 3; $day = $claimedDaysCount + 1; $dayIndex = $day - 1; // 3 $this->assertEquals(3, $dayIndex); $this->assertEquals(13, $distribution[$dayIndex]); // 已签到6次 → day=7 (第7次签到) → dayIndex=6 → 25% $claimedDaysCount = 6; $day = $claimedDaysCount + 1; $dayIndex = $day - 1; // 6 $this->assertEquals(6, $dayIndex); $this->assertEquals(25, $distribution[$dayIndex]); } /** @test */ public function claimed_count_determines_next_day() { // 签到次数直接影响下一次能签第几天 // 已签到0次,下次签第1天 $claimedMask = 0; // 0b0000000 $claimedCount = 0; for ($i = 0; $i < 7; $i++) { if ($claimedMask & (1 << $i)) { $claimedCount++; } } $nextDay = $claimedCount + 1; $this->assertEquals(1, $nextDay); // 已签到2次(第1、2天),下次签第3天 $claimedMask = (1 << 0) | (1 << 1); // 0b0000011 = 3 $claimedCount = 0; for ($i = 0; $i < 7; $i++) { if ($claimedMask & (1 << $i)) { $claimedCount++; } } $nextDay = $claimedCount + 1; $this->assertEquals(3, $nextDay); $this->assertEquals(2, $claimedCount); // 已签到7次,所有签完 $claimedMask = (1 << 7) - 1; // 0b1111111 = 127 $claimedCount = 0; for ($i = 0; $i < 7; $i++) { if ($claimedMask & (1 << $i)) { $claimedCount++; } } $nextDay = $claimedCount + 1; $this->assertEquals(7, $claimedCount); $this->assertEquals(8, $nextDay); // > 7,不能再签 } /** @test */ public function only_one_claim_per_day() { // 如果今天已经签过,不能再次签到 $lastClaimDate = '2026-05-15'; $todayDate = '2026-05-15'; $this->assertTrue($lastClaimDate === $todayDate); // 今天已签 $lastClaimDate = '2026-05-14'; $todayDate = '2026-05-15'; $this->assertFalse($lastClaimDate === $todayDate); // 今天未签 } /** @test */ public function claimed_bits_match_seven_days_view() { // sevenDays 用 bit0 表示 day1,bit1 表示 day2 ... // claim 中用 dayIndex = day-1,也映射到 bit0=day1, bit1=day2 // 模拟已领取 day1, day2, day3 $claimedMask = (1 << 0) | (1 << 1) | (1 << 2); // 0b0000111 = 7 $this->assertTrue((bool)($claimedMask & (1 << 0))); // day1 已领 $this->assertTrue((bool)($claimedMask & (1 << 1))); // day2 已领 $this->assertTrue((bool)($claimedMask & (1 << 2))); // day3 已领 $this->assertFalse((bool)($claimedMask & (1 << 3))); // day4 未领 $this->assertFalse((bool)($claimedMask & (1 << 6))); // day7 未领 // 所有 7 天都已领完 $allClaimedMask = (1 << 7) - 1; // 0b1111111 = 127 $this->assertEquals(127, $allClaimedMask); $isAllClaimed = ($claimedMask & $allClaimedMask) == $allClaimedMask; $this->assertFalse($isAllClaimed); // 只领了3天 $fullMask = 0b1111111; $this->assertTrue(($fullMask & $allClaimedMask) == $allClaimedMask); } /** * 复制控制器中的 getRecallGiftTiers 逻辑用于纯逻辑测试 */ private function getRecallGiftTiers($avgRecharge) { $amounts = [9, 19, 29, 39, 49, 59, 79, 99]; $bonusPercents = [70, 85, 100]; $index = count($amounts); foreach ($amounts as $i => $amt) { if ($avgRecharge <= $amt) { $index = $i; break; } } if ($index > count($amounts) - 3) { $index = count($amounts) - 3; } $tiers = []; for ($i = 0; $i < 3; $i++) { $tiers[] = [ 'amount' => $amounts[$index + $i], 'bonus_percent' => $bonusPercents[$i], ]; } return $tiers; } }