chatState->userJoin($id, $user->username, [ 'level' => $user->user_level, 'sex' => $user->sex, 'headface' => $user->headface, ]); // 2. 广播 UserJoined 事件,通知房间内的其他人 broadcast(new UserJoined($id, $user->username, [ 'level' => $user->user_level, 'sex' => $user->sex, 'headface' => $user->headface, ]))->toOthers(); // 3. 获取历史消息用于初次渲染 // TODO: 可在前端通过请求另外的接口拉取历史记录,或者直接在这里 attach // 渲染主聊天框架视图 return view('chat.frame', [ 'room' => $room, 'user' => $user, ]); } /** * 发送消息 (等同于原版 NEWSAY.ASP) * * @param int $id 房间ID */ public function send(SendMessageRequest $request, int $id): JsonResponse { $data = $request->validated(); $user = Auth::user(); // 1. 过滤净化消息体 $pureContent = $this->filter->filter($data['content'] ?? ''); if (empty($pureContent)) { return response()->json(['status' => 'error', 'message' => '消息内容不能为空或不合法。'], 422); } // 2. 封装消息对象 $messageData = [ 'id' => $this->chatState->nextMessageId($id), // 分布式安全自增序号 'room_id' => $id, 'from_user' => $user->username, 'to_user' => $data['to_user'] ?? '大家', 'content' => $pureContent, 'is_secret' => $data['is_secret'] ?? false, 'font_color' => $data['font_color'] ?? '', 'action' => $data['action'] ?? '', 'sent_at' => now()->toDateTimeString(), ]; // 3. 压入 Redis 缓存列表 (防炸内存,只保留最近 N 条) $this->chatState->pushMessage($id, $messageData); // 4. 立刻向 WebSocket 发射广播,前端达到 0 延迟渲染 broadcast(new MessageSent($id, $messageData)); // 5. 丢进异步列队,慢慢持久化到 MySQL,保护数据库连接池 SaveMessageJob::dispatch($messageData); return response()->json(['status' => 'success']); } /** * 自动挂机存点心跳与经验升级 (新增) * 替代原版定时 iframe 刷新的 save.asp。 * * @param int $id 房间ID */ public function heartbeat(Request $request, int $id): JsonResponse { $user = Auth::user(); if (! $user) { return response()->json(['status' => 'error'], 401); } // 1. 每次心跳 +1 点经验 $user->exp_num += 1; // 2. 检查等级计算:设定简单粗暴的平滑算式:需要经验=等级*等级*10 // 例如:0级->0点;1级->10点;2级->40点;3级->90点;10级->1000点 $currentLevel = $user->user_level; $requiredExpForNextLevel = ($currentLevel) * ($currentLevel) * 10; $leveledUp = false; if ($user->exp_num >= $requiredExpForNextLevel) { $user->user_level += 1; $leveledUp = true; } $user->save(); // 存点入库 // 3. 将新的等级反馈给当前用户的在线名单上 // 确保刚刚升级后别人查看到的也是最准确等级 $this->chatState->userJoin($id, $user->username, [ 'level' => $user->user_level, 'sex' => $user->sex, 'headface' => $user->headface, ]); // 4. 如果突破境界,向全房系统喊话广播! if ($leveledUp) { // 生成炫酷广播消息发向该频道 $sysMsg = [ 'id' => $this->chatState->nextMessageId($id), 'room_id' => $id, 'from_user' => '系统传音', 'to_user' => '大家', 'content' => "🌟 天道酬勤!恭喜侠客【{$user->username}】挂机苦修,境界突破至 LV.{$user->user_level}!", 'is_secret' => false, 'font_color' => '#d97706', // 琥珀橙色 'action' => '大声宣告', 'sent_at' => now()->toDateTimeString(), ]; $this->chatState->pushMessage($id, $sysMsg); broadcast(new MessageSent($id, $sysMsg)); // 落库 SaveMessageJob::dispatch($sysMsg); } return response()->json([ 'status' => 'success', 'data' => [ 'exp_num' => $user->exp_num, 'user_level' => $user->user_level, 'leveled_up' => $leveledUp, ], ]); } /** * 离开房间 (等同于原版 LEAVE.ASP) * * @param int $id 房间ID */ public function leave(Request $request, int $id): JsonResponse { $user = Auth::user(); if (! $user) { return response()->json(['status' => 'error'], 401); } // 1. 从 Redis 删除该用户 $this->chatState->userLeave($id, $user->username); // 2. 广播通知他人 broadcast(new UserLeft($id, $user->username))->toOthers(); return response()->json(['status' => 'success']); } }