diff --git a/app/Services/GameBetBroadcastService.php b/app/Services/GameBetBroadcastService.php
index 4e23bdd..c839988 100644
--- a/app/Services/GameBetBroadcastService.php
+++ b/app/Services/GameBetBroadcastService.php
@@ -94,6 +94,8 @@ class GameBetBroadcastService
toastMessage: "{$username} 抢到 {$formattedAmount} {$typeLabel}礼包",
toastIcon: '🧧',
toastColor: $toastColor,
+ toastActorUsername: $username,
+ skipToastForActor: true,
);
}
@@ -109,7 +111,23 @@ class GameBetBroadcastService
string $toastIcon,
string $toastColor,
string $action = '',
+ ?string $toastActorUsername = null,
+ bool $skipToastForActor = false,
): void {
+ $toastNotification = [
+ 'title' => $toastTitle,
+ 'message' => $toastMessage,
+ 'icon' => $toastIcon,
+ 'color' => $toastColor,
+ 'duration' => 8000,
+ ];
+
+ if ($toastActorUsername !== null) {
+ // 记录触发人用于前端去重,避免本人同时看到本地到账提示和公屏领取提示。
+ $toastNotification['actor_username'] = $toastActorUsername;
+ $toastNotification['skip_for_actor'] = $skipToastForActor;
+ }
+
$message = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
@@ -120,13 +138,7 @@ class GameBetBroadcastService
'font_color' => $fontColor,
'action' => $action,
'sent_at' => now()->toDateTimeString(),
- 'toast_notification' => [
- 'title' => $toastTitle,
- 'message' => $toastMessage,
- 'icon' => $toastIcon,
- 'color' => $toastColor,
- 'duration' => 8000,
- ],
+ 'toast_notification' => $toastNotification,
];
// 下注通知必须进房间 Presence 频道,确保当前房间所有在线人员都能看到右下角提示。
diff --git a/resources/js/chat-room/chat-events.js b/resources/js/chat-room/chat-events.js
index 5dab2f3..90d449b 100644
--- a/resources/js/chat-room/chat-events.js
+++ b/resources/js/chat-room/chat-events.js
@@ -70,6 +70,20 @@ function runWhenDomReady(callback) {
callback();
}
+/**
+ * 判断 Toast 通知是否需要对当前用户隐藏。
+ *
+ * @param {Record} toastNotification 右下角通知载荷
+ * @returns {boolean}
+ */
+function shouldSkipToastForCurrentUser(toastNotification) {
+ if (!toastNotification?.skip_for_actor) {
+ return false;
+ }
+
+ return String(toastNotification.actor_username || "") === String(window.chatContext?.username || "");
+}
+
// ── 禁言逻辑 ──
function handleMutedEvent(e) {
const state = getState();
@@ -417,6 +431,10 @@ export function bindChatEvents() {
// 若消息携带 toast_notification 字段且当前用户是接收者或为公屏广播或为欢迎动作,弹右下角小卡片
if (msg.toast_notification && (msg.to_user === window.chatContext?.username || msg.to_user === '大家' || msg.action === '欢迎')) {
const t = msg.toast_notification;
+ if (shouldSkipToastForCurrentUser(t)) {
+ return;
+ }
+
window.chatToast?.show({
title: t.title || "通知",
message: t.message || "",
diff --git a/tests/Feature/RedPacketControllerTest.php b/tests/Feature/RedPacketControllerTest.php
index 6952644..44c8d66 100644
--- a/tests/Feature/RedPacketControllerTest.php
+++ b/tests/Feature/RedPacketControllerTest.php
@@ -280,6 +280,8 @@ class RedPacketControllerTest extends TestCase
$this->assertFalse((bool) ($publicMessage['is_secret'] ?? true));
$this->assertStringContainsString('金币礼包', (string) ($publicMessage['toast_notification']['message'] ?? ''));
$this->assertSame('🧧', $publicMessage['toast_notification']['icon'] ?? null);
+ $this->assertSame($user->username, $publicMessage['toast_notification']['actor_username'] ?? null);
+ $this->assertTrue((bool) ($publicMessage['toast_notification']['skip_for_actor'] ?? false));
}
/**