diff --git a/app/Http/Controllers/BaccaratController.php b/app/Http/Controllers/BaccaratController.php index 42af81a..b8b894c 100644 --- a/app/Http/Controllers/BaccaratController.php +++ b/app/Http/Controllers/BaccaratController.php @@ -20,6 +20,7 @@ use App\Models\BaccaratBet; use App\Models\BaccaratRound; use App\Models\GameConfig; use App\Services\BaccaratLossCoverService; +use App\Services\GameBetBroadcastService; use App\Services\GameRoomScopeService; use App\Services\UserCurrencyService; use Illuminate\Http\JsonResponse; @@ -35,6 +36,7 @@ class BaccaratController extends Controller private readonly UserCurrencyService $currency, private readonly BaccaratLossCoverService $lossCoverService, private readonly GameRoomScopeService $roomScopeService, + private readonly GameBetBroadcastService $betBroadcastService, ) {} /** @@ -192,27 +194,7 @@ class BaccaratController extends Controller 'big' => '大', 'small' => '小', default => '豹子' }; - // 发送系统传音到聊天室,公示该用户的押注信息 - $chatState = app(\App\Services\ChatStateService::class); - $formattedAmount = number_format($data['amount']); - $roomId = $round->room_id ?? 1; - - // 格式:🌟 🎲 娜姐 押注了 119 金币(大)!✨ - $content = "🎲 【百家乐】【{$user->username}】 押注了 {$formattedAmount} 金币({$betLabel})!✨"; - $msg = [ - 'id' => $chatState->nextMessageId($roomId), - 'room_id' => $roomId, - 'from_user' => '系统传音', - 'to_user' => '大家', - 'content' => $content, - 'is_secret' => false, - 'font_color' => '#d97706', - 'action' => '', - 'sent_at' => now()->toDateTimeString(), - ]; - $chatState->pushMessage($roomId, $msg); - event(new \App\Events\MessageSent($roomId, $msg)); - \App\Jobs\SaveMessageJob::dispatch($msg); + $this->betBroadcastService->baccarat((int) ($round->room_id ?? 1), $user->username, (int) $data['amount'], $betLabel); return response()->json([ 'ok' => true, diff --git a/app/Http/Controllers/HorseRaceController.php b/app/Http/Controllers/HorseRaceController.php index 8fbfdd0..4081204 100644 --- a/app/Http/Controllers/HorseRaceController.php +++ b/app/Http/Controllers/HorseRaceController.php @@ -17,12 +17,10 @@ namespace App\Http\Controllers; use App\Enums\CurrencySource; -use App\Events\MessageSent; -use App\Jobs\SaveMessageJob; use App\Models\GameConfig; use App\Models\HorseBet; use App\Models\HorseRace; -use App\Services\ChatStateService; +use App\Services\GameBetBroadcastService; use App\Services\GameRoomScopeService; use App\Services\UserCurrencyService; use Illuminate\Http\JsonResponse; @@ -40,6 +38,7 @@ class HorseRaceController extends Controller public function __construct( private readonly UserCurrencyService $currency, private readonly GameRoomScopeService $roomScopeService, + private readonly GameBetBroadcastService $betBroadcastService, ) {} /** @@ -221,23 +220,7 @@ class HorseRaceController extends Controller 'status' => 'pending', ]); - $chatState = app(ChatStateService::class); - $formattedAmount = number_format($data['amount']); - $content = "🐎 【赛马】【{$user->username}】 押注了 {$formattedAmount} 金币({$horseName})!✨"; - $msg = [ - 'id' => $chatState->nextMessageId((int) $race->room_id), - 'room_id' => (int) $race->room_id, - 'from_user' => '系统传音', - 'to_user' => '大家', - 'content' => $content, - 'is_secret' => false, - 'font_color' => '#d97706', - 'action' => '', - 'sent_at' => now()->toDateTimeString(), - ]; - $chatState->pushMessage((int) $race->room_id, $msg); - event(new MessageSent((int) $race->room_id, $msg)); - SaveMessageJob::dispatch($msg); + $this->betBroadcastService->horseRace((int) $race->room_id, $user->username, (int) $data['amount'], $horseName); return response()->json([ 'ok' => true, diff --git a/app/Services/GameBetBroadcastService.php b/app/Services/GameBetBroadcastService.php new file mode 100644 index 0000000..a710347 --- /dev/null +++ b/app/Services/GameBetBroadcastService.php @@ -0,0 +1,116 @@ +pushBetMessage( + roomId: $roomId, + content: "🎲 【百家乐】【{$username}】 押注了 {$formattedAmount} 金币({$betLabel})!✨", + fontColor: '#d97706', + toastTitle: '🎲 有人下注百家乐', + toastMessage: "{$username} 押注 {$formattedAmount} 金币({$betLabel})", + toastIcon: '🎲', + toastColor: '#d97706', + ); + } + + /** + * 广播赛马下注成功通知。 + */ + public function horseRace(int $roomId, string $username, int $amount, string $horseName): void + { + $formattedAmount = number_format($amount); + + $this->pushBetMessage( + roomId: $roomId, + content: "🐎 【赛马】【{$username}】 押注了 {$formattedAmount} 金币({$horseName})!✨", + fontColor: '#d97706', + toastTitle: '🐎 有人下注赛马', + toastMessage: "{$username} 押注 {$formattedAmount} 金币({$horseName})", + toastIcon: '🐎', + toastColor: '#d97706', + ); + } + + /** + * 广播双色球购票成功通知。 + */ + public function lottery(int $roomId, string $username, string $issueNo, string $numbersLabel, int $ticketCount): void + { + $moreText = $ticketCount > 1 ? "等 {$ticketCount} 注号码" : ''; + + $this->pushBetMessage( + roomId: $roomId, + content: "🎟️ 【{$username}】购买 {$issueNo} 期 {$numbersLabel} {$moreText}", + fontColor: '#dc2626', + toastTitle: '🎟️ 有人购买双色球', + toastMessage: "{$username} 购买 {$issueNo} 期 {$numbersLabel} {$moreText}", + toastIcon: '🎟️', + toastColor: '#dc2626', + action: '大声宣告', + ); + } + + /** + * 推送带右下角通知载荷的公屏游戏下注消息。 + */ + private function pushBetMessage( + int $roomId, + string $content, + string $fontColor, + string $toastTitle, + string $toastMessage, + string $toastIcon, + string $toastColor, + string $action = '', + ): void { + $message = [ + 'id' => $this->chatState->nextMessageId($roomId), + 'room_id' => $roomId, + 'from_user' => '系统传音', + 'to_user' => '大家', + 'content' => $content, + 'is_secret' => false, + 'font_color' => $fontColor, + 'action' => $action, + 'sent_at' => now()->toDateTimeString(), + 'toast_notification' => [ + 'title' => $toastTitle, + 'message' => $toastMessage, + 'icon' => $toastIcon, + 'color' => $toastColor, + 'duration' => 8000, + ], + ]; + + // 下注通知必须进房间 Presence 频道,确保当前房间所有在线人员都能看到右下角提示。 + $this->chatState->pushMessage($roomId, $message); + event(new MessageSent($roomId, $message)); + SaveMessageJob::dispatch($message); + } +} diff --git a/app/Services/LotteryService.php b/app/Services/LotteryService.php index 075877d..085280f 100644 --- a/app/Services/LotteryService.php +++ b/app/Services/LotteryService.php @@ -31,6 +31,7 @@ class LotteryService private readonly UserCurrencyService $currency, private readonly ChatStateService $chatState, private readonly GameRoomScopeService $roomScopeService, + private readonly GameBetBroadcastService $betBroadcastService, ) {} // ─── 购票 ───────────────────────────────────────────────────────── @@ -139,8 +140,7 @@ class LotteryService // 用户成功购买后,发送系统传音广播(大家都能看到他买了彩票) $firstTicket = $tickets[0]; $numsStr = $firstTicket->numbersLabel(); - $moreStr = $buyCount > 1 ? "等 {$buyCount} 注号码" : ''; - $this->pushSystemMessage("🎟️ 【{$user->username}】购买 {$issue->issue_no} 期 {$numsStr} {$moreStr}", (int) $issue->room_id); + $this->betBroadcastService->lottery((int) $issue->room_id, $user->username, $issue->issue_no, $numsStr, $buyCount); return $tickets; } diff --git a/resources/views/chat/partials/games/red-packet-panel.blade.php b/resources/views/chat/partials/games/red-packet-panel.blade.php index 1dec140..5e540cf 100644 --- a/resources/views/chat/partials/games/red-packet-panel.blade.php +++ b/resources/views/chat/partials/games/red-packet-panel.blade.php @@ -260,12 +260,6 @@
- {{--
- {{-- 领取名单 --}} - --}} diff --git a/tests/Feature/BaccaratControllerTest.php b/tests/Feature/BaccaratControllerTest.php index 22a2f1b..c283bfb 100644 --- a/tests/Feature/BaccaratControllerTest.php +++ b/tests/Feature/BaccaratControllerTest.php @@ -27,6 +27,9 @@ class BaccaratControllerTest extends TestCase { use RefreshDatabase; + /** + * 方法功能:初始化百家乐默认配置。 + */ protected function setUp(): void { parent::setUp(); @@ -46,7 +49,10 @@ class BaccaratControllerTest extends TestCase ); } - public function test_can_get_current_round() + /** + * 方法功能:验证可以获取当前百家乐局次。 + */ + public function test_can_get_current_round(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 3456]); @@ -73,7 +79,10 @@ class BaccaratControllerTest extends TestCase $this->assertSame(3456, $response->json('jjb')); } - public function test_can_bet() + /** + * 方法功能:验证可以下注并给房间在线用户广播右下角通知。 + */ + public function test_can_bet(): void { Event::fake(); @@ -112,9 +121,24 @@ class BaccaratControllerTest extends TestCase ]); Event::assertDispatched(\App\Events\BaccaratPoolUpdated::class); + + $messages = Redis::lrange('room:1:messages', 0, -1); + $publicMessage = collect($messages) + ->map(fn (string $item) => json_decode($item, true)) + ->first(fn (array $item) => ($item['to_user'] ?? null) === '大家' + && ($item['toast_notification']['title'] ?? null) === '🎲 有人下注百家乐' + && str_contains((string) ($item['toast_notification']['message'] ?? ''), $user->username)); + + $this->assertNotNull($publicMessage); + $this->assertFalse((bool) ($publicMessage['is_secret'] ?? true)); + $this->assertStringContainsString($user->username, (string) ($publicMessage['toast_notification']['message'] ?? '')); + $this->assertSame('🎲', $publicMessage['toast_notification']['icon'] ?? null); } - public function test_cannot_bet_out_of_range() + /** + * 方法功能:验证超出配置范围的下注会被拦截。 + */ + public function test_cannot_bet_out_of_range(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 200]); @@ -143,7 +167,10 @@ class BaccaratControllerTest extends TestCase $response->assertJson(['ok' => false]); } - public function test_cannot_bet_twice_in_same_round() + /** + * 方法功能:验证同一用户同一局不能重复下注。 + */ + public function test_cannot_bet_twice_in_same_round(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 200]); @@ -180,7 +207,10 @@ class BaccaratControllerTest extends TestCase $response->assertJson(['ok' => false]); } - public function test_can_get_history() + /** + * 方法功能:验证可以获取最近已结算百家乐历史。 + */ + public function test_can_get_history(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(); diff --git a/tests/Feature/HorseRaceControllerTest.php b/tests/Feature/HorseRaceControllerTest.php index 7071f46..7668af1 100644 --- a/tests/Feature/HorseRaceControllerTest.php +++ b/tests/Feature/HorseRaceControllerTest.php @@ -60,7 +60,7 @@ class HorseRaceControllerTest extends TestCase /** * 方法功能:验证可获取当前进行中的场次。 */ - public function test_can_get_current_race() + public function test_can_get_current_race(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 4567]); @@ -214,7 +214,7 @@ class HorseRaceControllerTest extends TestCase /** * 方法功能:验证用户可以成功下注。 */ - public function test_can_bet() + public function test_can_bet(): void { Event::fake(); @@ -249,12 +249,24 @@ class HorseRaceControllerTest extends TestCase 'horse_id' => 1, 'amount' => 100, ]); + + $messages = Redis::lrange('room:1:messages', 0, -1); + $publicMessage = collect($messages) + ->map(fn (string $item) => json_decode($item, true)) + ->first(fn (array $item) => ($item['to_user'] ?? null) === '大家' + && ($item['toast_notification']['title'] ?? null) === '🐎 有人下注赛马' + && str_contains((string) ($item['toast_notification']['message'] ?? ''), $user->username)); + + $this->assertNotNull($publicMessage); + $this->assertFalse((bool) ($publicMessage['is_secret'] ?? true)); + $this->assertStringContainsString($user->username, (string) ($publicMessage['toast_notification']['message'] ?? '')); + $this->assertSame('🐎', $publicMessage['toast_notification']['icon'] ?? null); } /** * 方法功能:验证超出配置范围的下注会被拦截。 */ - public function test_cannot_bet_out_of_range() + public function test_cannot_bet_out_of_range(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 500]); @@ -284,7 +296,7 @@ class HorseRaceControllerTest extends TestCase /** * 方法功能:验证同一用户同一场只能下注一次。 */ - public function test_cannot_bet_twice_in_same_race() + public function test_cannot_bet_twice_in_same_race(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 500]); @@ -322,7 +334,7 @@ class HorseRaceControllerTest extends TestCase /** * 方法功能:验证可读取最近的赛马历史记录。 */ - public function test_can_get_history() + public function test_can_get_history(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(); diff --git a/tests/Feature/LotteryControllerTest.php b/tests/Feature/LotteryControllerTest.php index 3abd09f..972a9c5 100644 --- a/tests/Feature/LotteryControllerTest.php +++ b/tests/Feature/LotteryControllerTest.php @@ -1,5 +1,11 @@ create(); @@ -60,7 +76,10 @@ class LotteryControllerTest extends TestCase $this->assertEquals($issue->id, $response->json('issue.id')); } - public function test_can_quick_pick() + /** + * 方法功能:验证可以生成机选号码。 + */ + public function test_can_quick_pick(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(); @@ -72,7 +91,10 @@ class LotteryControllerTest extends TestCase $this->assertCount(2, $response->json('numbers')); } - public function test_cannot_buy_without_enough_gold() + /** + * 方法功能:验证金币不足时无法购票。 + */ + public function test_cannot_buy_without_enough_gold(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 50]); @@ -107,7 +129,10 @@ class LotteryControllerTest extends TestCase $response->assertStatus(422); // Exception thrown with 422 } - public function test_can_buy_with_enough_gold() + /** + * 方法功能:验证金币充足时可以购票并广播右下角通知。 + */ + public function test_can_buy_with_enough_gold(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(['jjb' => 200]); @@ -143,9 +168,24 @@ class LotteryControllerTest extends TestCase $response->assertJson(['status' => 'success']); $this->assertEquals(100, $user->fresh()->jjb); // cost is 100 per ticket + + $messages = Redis::lrange('room:1:messages', 0, -1); + $publicMessage = collect($messages) + ->map(fn (string $item) => json_decode($item, true)) + ->first(fn (array $item) => ($item['to_user'] ?? null) === '大家' + && ($item['toast_notification']['title'] ?? null) === '🎟️ 有人购买双色球' + && str_contains((string) ($item['toast_notification']['message'] ?? ''), $user->username)); + + $this->assertNotNull($publicMessage); + $this->assertFalse((bool) ($publicMessage['is_secret'] ?? true)); + $this->assertStringContainsString($user->username, (string) ($publicMessage['toast_notification']['message'] ?? '')); + $this->assertSame('🎟️', $publicMessage['toast_notification']['icon'] ?? null); } - public function test_can_get_history() + /** + * 方法功能:验证可以查询历史期次。 + */ + public function test_can_get_history(): void { /** @var \App\Models\User $user */ $user = User::factory()->create(); @@ -173,7 +213,10 @@ class LotteryControllerTest extends TestCase $this->assertCount(1, $response->json('issues')); } - public function test_can_get_my_tickets() + /** + * 方法功能:验证可以查询我的购票记录。 + */ + public function test_can_get_my_tickets(): void { /** @var \App\Models\User $user */ $user = User::factory()->create();