|
@@ -118,6 +118,7 @@ class AutomaticRewardGrantTest extends TestCase
|
|
|
$this->migration()->up();
|
|
$this->migration()->up();
|
|
|
$this->rulesMigration()->up();
|
|
$this->rulesMigration()->up();
|
|
|
(require dirname(__DIR__, 2) . '/database/migrations/2026_11_05_160000_add_backflow_granted_amounts.php')->up();
|
|
(require dirname(__DIR__, 2) . '/database/migrations/2026_11_05_160000_add_backflow_granted_amounts.php')->up();
|
|
|
|
|
+ (require dirname(__DIR__, 2) . '/database/migrations/2026_11_08_120000_add_rebate_granted_amounts.php')->up();
|
|
|
DB::table('rebate_rules')->insert(['category' => 'self:lottery', 'level' => 1, 'min_effective_amount' => 0, 'percentage' => '0.5', 'turnover_multiple' => 0]);
|
|
DB::table('rebate_rules')->insert(['category' => 'self:lottery', 'level' => 1, 'min_effective_amount' => 0, 'percentage' => '0.5', 'turnover_multiple' => 0]);
|
|
|
DB::table('backflow_rules')->insert(['category' => 'self:lottery', 'min_loss_amount' => 100, 'reward_amount' => 35, 'turnover_multiple' => 0]);
|
|
DB::table('backflow_rules')->insert(['category' => 'self:lottery', 'min_loss_amount' => 100, 'reward_amount' => 35, 'turnover_multiple' => 0]);
|
|
|
$this->request('/admin/rebate');
|
|
$this->request('/admin/rebate');
|
|
@@ -684,12 +685,97 @@ class AutomaticRewardGrantTest extends TestCase
|
|
|
$this->assertSame(2, DB::table('balance_logs')->where('change_type', '回水')->count());
|
|
$this->assertSame(2, DB::table('balance_logs')->where('change_type', '回水')->count());
|
|
|
$this->assertSame(2, DB::table('site_messages')->count(), 'Closing without another credit must not send a notice');
|
|
$this->assertSame(2, DB::table('site_messages')->count(), 'Closing without another credit must not send a notice');
|
|
|
|
|
|
|
|
- // 笔笔返仍须等当天结束
|
|
|
|
|
- $rebate = $this->rebate(['date' => Carbon::now('America/New_York')->format('Y-m-d')]);
|
|
|
|
|
|
|
+ // 未来日期仍不能提前发放。
|
|
|
|
|
+ $rebate = $this->rebate(['date' => Carbon::now('America/New_York')->addDay()->format('Y-m-d')]);
|
|
|
$this->expectExceptionMessage('未到发放时间');
|
|
$this->expectExceptionMessage('未到发放时间');
|
|
|
RewardGrantService::grant('rebate', $rebate, 'operator-one');
|
|
RewardGrantService::grant('rebate', $rebate, 'operator-one');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function test_manual_rebate_pays_3033_basis_then_differences_and_auto_finishes_after_day_end(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ DB::table('rebate_rules')->update(['percentage' => 1, 'turnover_multiple' => 2]);
|
|
|
|
|
+ $id = $this->rebate(['date' => '2026-10-25']);
|
|
|
|
|
+ $this->fact('1001', 'self:lottery', '2026-10-25 13:00:00', '3033', '-3033');
|
|
|
|
|
+ $this->enable('rebate');
|
|
|
|
|
+ $this->assertSame(0, $this->runRewards()['rebate']['granted'], 'Automatic payout still waits for day end');
|
|
|
|
|
+ $this->request('/admin/rebate/submit', ['id' => $id], 'POST');
|
|
|
|
|
+ $controller = new RebateController();
|
|
|
|
|
+ $this->assertSame(0, $controller->store()->getData(true)['code']);
|
|
|
|
|
+ $row = DB::table('rebates')->where('id', $id)->first();
|
|
|
|
|
+ $this->assertSame(2, (int) $row->status);
|
|
|
|
|
+ $this->assertEquals(30.33, $row->granted_amount);
|
|
|
|
|
+ $this->assertEquals(60.66, $row->granted_need_flow);
|
|
|
|
|
+ $this->assertEquals(50.33, DB::table('wallets')->value('available_balance'));
|
|
|
|
|
+ $this->assertFalse(RebateService::grantFields((array) $row)['can_grant']);
|
|
|
|
|
+ $this->assertSame(-3, $controller->store()->getData(true)['code'], 'No difference cannot pay twice');
|
|
|
|
|
+
|
|
|
|
|
+ $this->fact('1001', 'self:lottery', '2026-10-25 14:00:00', '1000', '-1000');
|
|
|
|
|
+ (new RewardBaseSyncService())->run('2026-10-25', '2026-10-25');
|
|
|
|
|
+ $fields = RebateService::grantFields((array) DB::table('rebates')->where('id', $id)->first());
|
|
|
|
|
+ $this->assertSame(['40.33', '30.33', '10.00', true, '部分发放'], [$fields['amount'], $fields['granted_amount'], $fields['pending_amount'], $fields['can_grant'], $fields['status_text']]);
|
|
|
|
|
+ DB::statement("CREATE TRIGGER reject_partial_notice BEFORE INSERT ON bot_site_message_recipients BEGIN SELECT RAISE(ABORT, 'notice unavailable'); END");
|
|
|
|
|
+ $this->assertNotSame(0, $controller->store()->getData(true)['code']);
|
|
|
|
|
+ $this->assertEquals(50.33, DB::table('wallets')->value('available_balance'));
|
|
|
|
|
+ $this->assertEquals(30.33, DB::table('rebates')->where('id', $id)->value('granted_amount'));
|
|
|
|
|
+ $this->assertSame(1, DB::table('site_messages')->count());
|
|
|
|
|
+ $this->assertSame(1, DB::table('wallet_bonus')->count());
|
|
|
|
|
+ DB::statement('DROP TRIGGER reject_partial_notice');
|
|
|
|
|
+ $this->assertSame(0, $controller->store()->getData(true)['code']);
|
|
|
|
|
+ $this->assertEquals(60.33, DB::table('wallets')->value('available_balance'));
|
|
|
|
|
+ $this->assertEquals(80.66, DB::table('wallet_bonus')->sum('need_flow'));
|
|
|
|
|
+ $this->assertSame(['30.33', '10.00'], DB::table('balance_logs')->orderBy('id')->pluck('amount')->map(fn ($v) => bcadd($v, '0', 2))->all());
|
|
|
|
|
+ $contents = DB::table('site_message_translations')->where('language', 'zh')->orderBy('id')->pluck('content')->all();
|
|
|
|
|
+ $this->assertStringContainsString('本次已发放 30.33', $contents[0]);
|
|
|
|
|
+ $this->assertStringContainsString('本次已发放 10.00', $contents[1]);
|
|
|
|
|
+
|
|
|
|
|
+ $this->fact('1001', 'self:lottery', '2026-10-25 14:20:00', '333', '-333');
|
|
|
|
|
+ (new RewardBaseSyncService())->run('2026-10-25', '2026-10-25');
|
|
|
|
|
+ Carbon::setTestNow(Carbon::parse('2026-10-26 14:30:00', 'Asia/Shanghai'));
|
|
|
|
|
+ $this->assertSame(1, $this->runRewards()['rebate']['granted']);
|
|
|
|
|
+ $row = DB::table('rebates')->where('id', $id)->first();
|
|
|
|
|
+ $this->assertSame(1, (int) $row->status);
|
|
|
|
|
+ $this->assertEquals(43.66, $row->granted_amount);
|
|
|
|
|
+ $this->assertEquals(87.32, $row->granted_need_flow);
|
|
|
|
|
+ $this->assertEquals(63.66, DB::table('wallets')->value('available_balance'));
|
|
|
|
|
+ $this->assertSame(3, DB::table('site_messages')->count());
|
|
|
|
|
+ $this->assertSame(0, $this->runRewards()['rebate']['granted']);
|
|
|
|
|
+ $this->assertSame(3, DB::table('balance_logs')->count());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function test_rebate_day_end_without_a_difference_does_not_credit_or_notify_again(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $id = $this->rebate(['date' => '2026-10-25']);
|
|
|
|
|
+ $this->assertTrue(RewardGrantService::grant('rebate', $id, 'operator-one'));
|
|
|
|
|
+ DB::table('rebate_rules')->update(['percentage' => '0.1', 'turnover_multiple' => 3]);
|
|
|
|
|
+ Carbon::setTestNow(Carbon::parse('2026-10-26 14:30:00', 'Asia/Shanghai'));
|
|
|
|
|
+ $this->enable('rebate');
|
|
|
|
|
+ $this->assertSame(1, $this->runRewards()['rebate']['granted']);
|
|
|
|
|
+ $row = DB::table('rebates')->where('id', $id)->first();
|
|
|
|
|
+ $this->assertSame(1, (int) $row->status);
|
|
|
|
|
+ $this->assertEquals(4, $row->granted_amount, 'A lower calculation never reclaims a previous credit');
|
|
|
|
|
+ $this->assertEquals(0, $row->granted_need_flow, 'Unpaid turnover must not be counted as granted');
|
|
|
|
|
+ $this->assertEquals(24, DB::table('wallets')->value('available_balance'));
|
|
|
|
|
+ $this->assertSame(1, DB::table('site_messages')->count());
|
|
|
|
|
+ $this->assertSame(1, DB::table('balance_logs')->count());
|
|
|
|
|
+ $this->assertFalse(RewardGrantService::grant('rebate', $id, 'operator-one', true));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public function test_rebate_granted_amount_migration_preserves_paid_history_and_partial_totals(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $paid = $this->rebate(['date' => '2026-09-01', 'status' => 1, 'amount' => 30.33, 'need_flow' => 60.66]);
|
|
|
|
|
+ $pending = $this->rebate(['date' => '2026-09-02', 'amount' => 99]);
|
|
|
|
|
+ $partial = $this->rebate(['date' => '2026-10-25', 'status' => 2, 'amount' => 50, 'granted_amount' => 30.33, 'granted_need_flow' => 60.66]);
|
|
|
|
|
+ $migration = require dirname(__DIR__, 2) . '/database/migrations/2026_11_08_120000_add_rebate_granted_amounts.php';
|
|
|
|
|
+ $migration->up();
|
|
|
|
|
+ $this->assertEquals(30.33, DB::table('rebates')->where('id', $paid)->value('granted_amount'));
|
|
|
|
|
+ $this->assertEquals(60.66, DB::table('rebates')->where('id', $paid)->value('granted_need_flow'));
|
|
|
|
|
+ $this->assertEquals(0, DB::table('rebates')->where('id', $pending)->value('granted_amount'));
|
|
|
|
|
+ $this->assertEquals(30.33, DB::table('rebates')->where('id', $partial)->value('granted_amount'));
|
|
|
|
|
+ $migration->down();
|
|
|
|
|
+ $this->assertSame(1, (int) DB::table('rebates')->where('id', $partial)->value('status'));
|
|
|
|
|
+ $this->assertFalse(Schema::hasColumn('rebates', 'granted_amount'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public function test_granted_amount_migration_backfills_paid_rows(): void
|
|
public function test_granted_amount_migration_backfills_paid_rows(): void
|
|
|
{
|
|
{
|
|
|
$paid = DB::table('backflow')->insertGetId(['member_id' => '1001', 'date' => '2026-08', 'status' => 1, 'amount' => '35', 'need_flow' => '70', 'granted_amount' => 0]);
|
|
$paid = DB::table('backflow')->insertGetId(['member_id' => '1001', 'date' => '2026-08', 'status' => 1, 'amount' => '35', 'need_flow' => '70', 'granted_amount' => 0]);
|