diff --git a/app/Http/Controllers/RideController.php b/app/Http/Controllers/RideController.php index 529629a..2741601 100644 --- a/app/Http/Controllers/RideController.php +++ b/app/Http/Controllers/RideController.php @@ -8,9 +8,12 @@ namespace App\Http\Controllers; +use App\Events\MessageSent; use App\Http\Requests\BuyRideRequest; +use App\Jobs\SaveMessageJob; use App\Models\Ride; use App\Models\Room; +use App\Models\User; use App\Services\ChatStateService; use App\Services\RideService; use Illuminate\Http\JsonResponse; @@ -67,6 +70,8 @@ class RideController extends Controller return response()->json(['status' => 'error', 'message' => $result['message']], 400); } + $this->pushRidePurchaseNotice($user, $item, $roomId); + return response()->json([ 'status' => 'success', 'message' => $result['message'], @@ -75,4 +80,36 @@ class RideController extends Controller 'jjb' => $user->fresh()->jjb, ]); } + + /** + * 向当前房间广播座驾购买成功通知,方便其他用户快速打开座驾页面。 + */ + private function pushRidePurchaseNotice(User $user, Ride $item, int $roomId): void + { + $button = ''; + $content = sprintf( + '🚀 【座驾】 %s 购买了 %s,有效期 %d 天,排面已安排!%s', + e($user->username), + e($item->name), + (int) $item->duration_days, + $button, + ); + + $message = [ + 'id' => $this->chatState->nextMessageId($roomId), + 'room_id' => $roomId, + 'from_user' => '系统传音', + 'to_user' => '大家', + 'content' => $content, + 'is_secret' => false, + 'font_color' => '#0f766e', + 'action' => 'ride_purchase', + 'sent_at' => now()->toDateTimeString(), + ]; + + // 购买通知需要写入房间消息缓存、实时广播并落库,刷新后仍可追溯。 + $this->chatState->pushMessage($roomId, $message); + broadcast(new MessageSent($roomId, $message)); + SaveMessageJob::dispatch($message); + } } diff --git a/plugins/chatroom-ride-development/skills/chatroom-ride-development/SKILL.md b/plugins/chatroom-ride-development/skills/chatroom-ride-development/SKILL.md index de90eb7..d1a8646 100644 --- a/plugins/chatroom-ride-development/skills/chatroom-ride-development/SKILL.md +++ b/plugins/chatroom-ride-development/skills/chatroom-ride-development/SKILL.md @@ -61,6 +61,11 @@ description: "开发 /Users/pllx/Web/Herd/chatroom 的聊天室座驾。适用 - 第一行用户身份信息显示 `用户 <用户名> · 部门 <部门> · 职务 <图标 职务> · 会员 <图标 会员>`。 - 第二行标题只显示 `乘坐【<座驾名称>】闪亮登场`,不要重复用户名。 - `effect_user_info` 供动画 HUD 第一行使用,`effect_title` 供动画 HUD 第二行使用,`identity_text` 只供文字播报使用。 +- 座驾购买成功必须向当前房间广播文字通知: + - 通知使用 `系统传音`,`action = ride_purchase`。 + - 文案包含 `【座驾】`,前端会按百家乐同款紧凑卡片样式渲染。 + - 通知末尾必须带 `购买座驾` 按钮,点击调用 `openRideModal()`,方便其他人快速购买。 + - 购买通知必须写入 `ChatStateService`、广播 `MessageSent`,并通过 `SaveMessageJob` 落库。 - 前端改动后如果浏览器仍显示旧动画标题,必须运行 `npm run build` 或确认 Vite dev server 已刷新,避免加载旧的 `public/build` 资源。 ## 新增座驾步骤 diff --git a/resources/js/chat-room/message-renderer.js b/resources/js/chat-room/message-renderer.js index 461e933..52ba5ae 100644 --- a/resources/js/chat-room/message-renderer.js +++ b/resources/js/chat-room/message-renderer.js @@ -201,6 +201,18 @@ function resolveGameNotificationCardMeta(msg) { }; } + if (normalizedContent.includes("【座驾】")) { + return { + label: "座驾", + icon: "🚀", + accent: "#0f766e", + background: "linear-gradient(135deg,#ecfeff,#f0fdfa)", + border: "rgba(15,118,110,.16)", + text: "#115e59", + chipBg: "#ccfbf1", + }; + } + if ( normalizedContent.includes("【赛马】") || normalizedContent.startsWith("🐎 开赛:") @@ -336,6 +348,14 @@ function extractSystemGameCardSummary(content, meta) { .trim(); } + if (meta.label === "座驾") { + return normalizedContent + .replace(/^[🚀]+\s*/u, "") + .replace(/^【座驾】/u, "") + .replace(/\s+/gu, " ") + .trim(); + } + if (meta.label === "赛马") { return normalizedContent .replace(/^[🐎🏇🏆]+\s*/u, "") diff --git a/tests/Feature/RideControllerTest.php b/tests/Feature/RideControllerTest.php index 675c822..5e239a2 100644 --- a/tests/Feature/RideControllerTest.php +++ b/tests/Feature/RideControllerTest.php @@ -122,6 +122,17 @@ class RideControllerTest extends TestCase 'remark' => '购买聊天室座驾:测试座驾', 'room_id' => $room->id, ]); + + $messages = app(\App\Services\ChatStateService::class)->getNewMessages($room->id, 0); + $purchaseNotice = collect($messages)->first(fn (array $message): bool => ($message['action'] ?? '') === 'ride_purchase'); + + $this->assertNotNull($purchaseNotice); + $this->assertSame('系统传音', $purchaseNotice['from_user']); + $this->assertStringContainsString('【座驾】', $purchaseNotice['content']); + $this->assertStringContainsString($user->username, $purchaseNotice['content']); + $this->assertStringContainsString('测试座驾', $purchaseNotice['content']); + $this->assertStringContainsString('openRideModal()', $purchaseNotice['content']); + $this->assertStringContainsString('购买座驾', $purchaseNotice['content']); } /**