修复新人进房欢迎消息显示

This commit is contained in:
pllx
2026-04-27 14:05:11 +08:00
parent 8db1a252d7
commit 3d8e270df4
4 changed files with 56 additions and 15 deletions
+6 -7
View File
@@ -127,14 +127,9 @@ class ChatController extends Controller
// 必须在推送新消息之前执行,否则可能误删刚刚创建的欢迎播报
$this->chatState->removeOldWelcomeMessages($id, $user->username);
// 新人首次进入:赠送 6666 金币、播放满场烟花、发送全场欢迎通告
// 双重校验:优先以 has_received_new_gift 标记为准,若标记异常则用 created_at 时间兜底
// 新人首次进入:赠送 6666 金币、播放满场烟花、发送全场欢迎通告
$user->refresh();
$isNewbie = ! $user->has_received_new_gift;
// 兜底:如果用户创建时间在 2 分钟内,即使标记异常也按新人处理
if (! $isNewbie && $user->created_at && $user->created_at->diffInSeconds(now()) < 120) {
$isNewbie = true;
}
if ($isNewbie) {
// 通过统一积分服务发放新人礼包 6666 金币并记录流水
$this->currencyService->change(
@@ -153,6 +148,7 @@ class ChatController extends Controller
'font_color' => '#b91c1c',
'action' => '',
'welcome_user' => $user->username,
'welcome_kind' => 'newbie_bonus',
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($id, $newbieMsg);
@@ -178,11 +174,13 @@ class ChatController extends Controller
'id' => $this->chatState->nextMessageId($id),
'room_id' => $id,
'from_user' => 'AI小班长',
'to_user' => $user->username,
'to_user' => '大家',
'content' => $aiWelcomeContent,
'is_secret' => false,
'font_color' => '#16a34a',
'action' => '大声宣告',
'welcome_user' => $user->username,
'welcome_kind' => 'ai_newbie_welcome',
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($id, $aiWelcomeMsg);
@@ -204,6 +202,7 @@ class ChatController extends Controller
'font_color' => $color,
'action' => empty($vipPresencePayload) ? 'system_welcome' : 'vip_presence',
'welcome_user' => $user->username,
'welcome_kind' => 'entry_broadcast',
'sent_at' => now()->toDateTimeString(),
];
+3 -2
View File
@@ -220,8 +220,9 @@ class ChatStateService
foreach ($messages as $msgJson) {
$msg = json_decode($msgJson, true);
// 只要消息里带了 welcome_user 且等于当前用户,就抛弃这条旧的
if ($msg && isset($msg['welcome_user']) && $msg['welcome_user'] === $username) {
// 只清理普通进出播报,避免误删新人礼包公告和 AI 小班长新人欢迎。
$welcomeKind = $msg['welcome_kind'] ?? 'entry_broadcast';
if ($msg && isset($msg['welcome_user']) && $msg['welcome_user'] === $username && $welcomeKind === 'entry_broadcast') {
continue;
}
$filtered[] = $msgJson;
@@ -2642,13 +2642,20 @@
// 后端下发的带有 welcome_user 的也是系统欢迎/离开消息,加上属性标记
if (msg.welcome_user) {
const welcomeKind = msg.welcome_kind || 'entry_broadcast';
div.setAttribute('data-system-user', msg.welcome_user);
// 收到后端来的新欢迎消息时,把界面上该用户旧的都删掉
const welcomeSelector = `[data-system-user="${msg.welcome_user}"]`;
const oldWelcomes = container.querySelectorAll(welcomeSelector);
oldWelcomes.forEach(el => el.remove());
renderBatch?.publicFragment.querySelectorAll(welcomeSelector).forEach(el => el.remove());
renderBatch?.privateFragment.querySelectorAll(welcomeSelector).forEach(el => el.remove());
div.setAttribute('data-system-welcome-kind', welcomeKind);
// 收到后端来的同类欢迎消息时,只替换同类旧消息,避免进场播报误删新人礼包和 AI 欢迎。
const removeSameWelcome = (root) => {
root?.querySelectorAll('[data-system-user]').forEach(el => {
if (el.dataset.systemUser === msg.welcome_user && (el.dataset.systemWelcomeKind || 'entry_broadcast') === welcomeKind) {
el.remove();
}
});
};
removeSameWelcome(container);
removeSameWelcome(renderBatch?.publicFragment);
removeSameWelcome(renderBatch?.privateFragment);
}
// 路由规则(复刻原版):
+34
View File
@@ -721,6 +721,8 @@ class ChatControllerTest extends TestCase
$room = Room::create(['room_name' => 'annsafe']);
$user = $this->createUserWithPositionPermissions([
PositionPermissionRegistry::ROOM_ANNOUNCEMENT,
], [
'has_received_new_gift' => true,
]);
$this->actingAs($user)->get(route('chat.room', $room->id));
@@ -992,6 +994,38 @@ class ChatControllerTest extends TestCase
$this->assertStringContainsString($user->username, $presenceMessage['presence_text']);
}
/**
* 测试新人首次进房时首屏历史包含礼包公告、AI 欢迎和普通进场播报。
*/
public function test_newbie_first_join_keeps_bonus_ai_and_entry_welcome_messages(): void
{
$room = Room::create(['room_name' => 'newbie']);
$user = User::factory()->create([
'jjb' => 0,
'has_received_new_gift' => false,
]);
$response = $this->actingAs($user)->get(route('chat.room', $room->id));
$response->assertOk();
$history = collect($response->viewData('historyMessages'));
$newbieBonusMessage = $history->first(fn (array $message): bool => ($message['welcome_kind'] ?? '') === 'newbie_bonus');
$aiWelcomeMessage = $history->first(fn (array $message): bool => ($message['welcome_kind'] ?? '') === 'ai_newbie_welcome');
$entryMessage = $history->first(fn (array $message): bool => ($message['welcome_kind'] ?? '') === 'entry_broadcast');
$this->assertNotNull($newbieBonusMessage);
$this->assertSame('系统公告', $newbieBonusMessage['from_user']);
$this->assertStringContainsString('6666 金币新人大礼包', $newbieBonusMessage['content']);
$this->assertNotNull($aiWelcomeMessage);
$this->assertSame('AI小班长', $aiWelcomeMessage['from_user']);
$this->assertSame('大家', $aiWelcomeMessage['to_user']);
$this->assertNotNull($entryMessage);
$this->assertSame($user->username, $entryMessage['welcome_user']);
$this->assertTrue($user->fresh()->has_received_new_gift);
$this->assertSame(6666, (int) $user->fresh()->jjb);
}
/**
* 测试可以获取所有房间的在线人数状态。
*/