Files
chatroom/app/Http/Controllers/ChatController.php
lkddi 50fc804402 feat: 实现挂机修仙、排行榜、大厅重构与全站留言板系统
- (Phase 8) 后台各维度管理与配置
- (Phase 9) 全自动静默挂机修仙升级
- (Phase 9) 四大维度风云排行榜页面
- (Phase 10) 全站留言板与悄悄话私信功能
- 运行 Pint 代码格式化
2026-02-26 13:35:38 +08:00

201 lines
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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']);
}
}