Files
chatroom/app/Http/Controllers/ChatController.php

201 lines
6.3 KiB
PHP
Raw Normal View History

<?php
/**
* 文件功能:聊天室核心控制器
* 接管原版 INIT.ASP, NEWSAY.ASP, LEAVE.ASP 的所有职责
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers;
use App\Events\MessageSent;
use App\Events\UserJoined;
use App\Events\UserLeft;
use App\Http\Requests\SendMessageRequest;
use App\Jobs\SaveMessageJob;
use App\Models\Room;
use App\Services\ChatStateService;
use App\Services\MessageFilterService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class ChatController extends Controller
{
public function __construct(
private readonly ChatStateService $chatState,
private readonly MessageFilterService $filter,
) {}
/**
* 进入房间初始化 (等同于原版 INIT.ASP)
*
* @param int $id 房间ID
* @return View|JsonResponse
*/
public function init(int $id)
{
$room = Room::findOrFail($id);
$user = Auth::user();
// 1. 将当前用户加入到 Redis 房间在线列表
$this->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']);
}
}