# ChatRoom 开发计划与注意事项 > **技术栈**:Laravel 12 · PHP 8.4 · Laravel Reverb (WebSocket) · Redis · MySQL 8.0 · Laravel Horizon > **原项目**:`/Users/pllx/Web/chat/hp0709`(VBScript ASP + MS Access 聊天室) > **目标域名**:`http://chatroom.test`(Herd 自动配置) --- ## 一、环境版本要求 | 组件 | 版本 | | --------------------- | -------------------------- | | **PHP** | 8.4.5+ | | **Laravel Framework** | v12.x | | **Laravel Reverb** | latest(WebSocket 服务器) | | **Laravel Horizon** | v5(Redis 队列可视化管理) | | **PHPUnit** | v11(测试框架) | | **Node.js** | 20.x LTS | | **MySQL** | 8.0+ | | **Redis** | 7.x | --- ## 二、代码规范(强制执行) ### 2.1 Laravel Pint 格式化 ```bash # 提交代码前必须运行,修复格式问题 vendor/bin/pint --dirty # 检查格式问题(不修复) vendor/bin/pint --test # 格式化整个项目 vendor/bin/pint ``` ### 2.2 PHP 8.4 类型系统(必须遵守) ```php // ✅ 正确:构造函数属性提升 (Constructor Property Promotion) class ChatController extends Controller { public function __construct( private readonly ChatStateService $chatState, private readonly MessageFilterService $filter, ) {} } // ❌ 错误:不允许无参空构造函数 class SomeClass { public function __construct() {} // 禁止! } // ✅ 正确:显式返回类型 + 参数类型提示 public function send(SendMessageRequest $request): JsonResponse { // ... } // ✅ 正确:使用 PHP 8.4 新特性 // 联合类型 public function findUser(int|string $id): User|null {} // readonly 属性 class MessageDto { public function __construct( public readonly string $content, public readonly string $fromUser, public readonly int $roomId, ) {} } ``` ### 2.3 Laravel 12 中间件配置(重要) > [!IMPORTANT] > Laravel 12 已废弃 `Kernel.php`,中间件在 `bootstrap/app.php` 中配置。 ```php // bootstrap/app.php return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', // API 路由 channels: __DIR__.'/../routes/channels.php', // WebSocket 频道 commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { // 注册聊天室登录验证中间件 $middleware->alias([ 'chat.auth' => \App\Http\Middleware\ChatAuthenticate::class, 'chat.level' => \App\Http\Middleware\LevelRequired::class, ]); // Session 中间件(Web 路由自动携带) $middleware->web(append: [ \App\Http\Middleware\HandleInertiaRequests::class, ]); }) ->withExceptions(function (Exceptions $exceptions): void { // })->create(); ``` ### 2.4 中文注释规范(每个文件必须) ```php redis->hset("room:{$roomId}:users", $username, json_encode($userInfo)); } } ``` --- ## 三、首次启动(必须先执行) ```bash cd /Users/pllx/Web/Herd/chatroom # 安装 Reverb WebSocket 服务器(已经完成) composer require laravel/reverb predis/predis # 安装 Horizon 队列管理(替代 queue:work,提供 Web 监控面板) composer require laravel/horizon # 发布配置文件 php artisan reverb:install php artisan horizon:install # 创建数据库(已经完成) mysql -u root -proot -e "CREATE DATABASE IF NOT EXISTS chatroom CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 运行数据库迁移(迁移前检查原有迁移文件是否有用) php artisan migrate # 安装前端依赖 npm install (已经完成) npm install laravel-echo pusher-js(已经完成) npm run dev ``` **开发时运行的服务**: ```bash php artisan reverb:start --debug # WebSocket 服务器 :8080 php artisan horizon # 队列 Worker(含 Web 监控 /horizon) npm run dev # Vite 热更新 # HTTP 由 Herd 自动提供 chatroom.test ``` --- ## 四、数据库迁移对照表 **原 Access 表** → **Laravel Migration** 对应关系: | 原 ASP 表 | Laravel 迁移文件 | Model 类 | 说明 | | ----------- | -------------------------------- | ----------------- | --------------------------------- | | `user` | `create_users_table` | `User` | 主用户表(默认迁移文件,需修改) | | `room` | `create_rooms_table` | `Room` | 聊天房间 | | _(内存)_ | `create_messages_table` | `Message` | 消息归档(原用 Application 内存) | | `sysparam` | `create_sys_params_table` | `SysParam` | 系统参数 | | `ip_lock` | `create_ip_locks_table` | `IpLock` | IP 封锁 | | `record` | `create_audit_logs_table` | `AuditLog` | 管理操作日志 | | `guestbook` | `create_guestbooks_table` | `Guestbook` | 留言板 | | `calls` | `create_friend_calls_table` | `FriendCall` | 好友呼叫 | | `friendrq` | `create_friend_requests_table` | `FriendRequest` | 好友申请 | | `action` | `create_actions_table` | `Action` | 表情/动作定义 | | `admincz` | `create_admin_logs_table` | `AdminLog` | 管理员操作统计 | | `gg` | `create_user_items_table` | `UserItem` | 道具(封口令等) | | `scrollad` | `create_scroll_ads_table` | `ScrollAd` | 滚动公告 | | `hy` / `lh` | `create_marriages_table` | `Marriage` | 婚姻关系 | | `ip` | `create_ip_logs_table` | `IpLog` | IP 登录日志 | | `room_des` | `create_room_descriptions_table` | `RoomDescription` | 房间描述模板 | **批量生成迁移命令**: ```bash php artisan make:migration create_rooms_table php artisan make:migration create_messages_table php artisan make:migration create_sys_params_table php artisan make:migration create_ip_locks_table php artisan make:migration create_audit_logs_table php artisan make:migration create_guestbooks_table php artisan make:migration create_friend_calls_table php artisan make:migration create_friend_requests_table php artisan make:migration create_actions_table php artisan make:migration create_admin_logs_table php artisan make:migration create_user_items_table php artisan make:migration create_scroll_ads_table php artisan make:migration create_marriages_table php artisan make:migration create_ip_logs_table php artisan make:migration create_room_descriptions_table ``` --- ## 五、推荐目录结构 ``` app/ ├── Events/ # WebSocket 广播事件(ShouldBroadcast) │ ├── MessageSent.php # 消息发送(替代 NEWSAY.ASP) │ ├── UserJoined.php # 用户进房(替代 INIT.ASP) │ ├── UserLeft.php # 用户离开(替代 LEAVE.ASP) │ ├── UserKicked.php # 踢人 │ ├── UserMuted.php # 封口 │ └── RoomTitleUpdated.php # 房间标题更新 │ ├── Services/ # 业务逻辑服务层(纯 PHP,不含 HTTP 逻辑) │ ├── ChatStateService.php # Redis 全局状态(替代 Application 对象) │ ├── MessageFilterService.php # 敏感词/HTML 过滤 │ ├── AuthService.php # 登录验证逻辑 │ └── UserLevelService.php # 等级权限判断(替代 getLevel()) │ ├── Http/ │ ├── Controllers/ │ │ ├── AuthController.php # DEFAULT.asp + CHECK.asp + CLOSE.ASP │ │ ├── RegisterController.php # Reg.asp + addnewuser.asp │ │ ├── ChatController.php # NEWSAY.ASP + INIT.ASP + LEAVE.ASP │ │ ├── RoomController.php # ROOM*.ASP 系列 │ │ ├── UserController.php # USERSET + DOUSER + KILLUSER │ │ ├── FriendController.php # addfriend + agreefriend 等 │ │ ├── GuestbookController.php # GUEST.ASP │ │ └── Admin/ │ │ ├── AdminController.php │ │ ├── UserManagerController.php │ │ └── SystemController.php # VIEWSYS.ASP │ │ │ ├── Middleware/ │ │ ├── ChatAuthenticate.php # 聊天室登录验证 │ │ └── LevelRequired.php # 等级权限中间件(用法:chat.level:5) │ │ │ └── Requests/ # Form Request 验证 │ ├── LoginRequest.php │ ├── SendMessageRequest.php │ └── CreateRoomRequest.php │ ├── Models/ │ ├── User.php │ ├── Room.php │ ├── Message.php │ ├── SysParam.php │ ├── IpLock.php │ └── ... │ └── Jobs/ └── SaveMessageJob.php # 异步将消息持久化到 MySQL bootstrap/ └── app.php # ⚠ Laravel 12:中间件/路由在此配置(无 Kernel.php) routes/ ├── web.php # 所有 HTTP 路由 ├── api.php # API 路由(Horizon 监控等) └── channels.php # WebSocket Presence Channel 鉴权 resources/ ├── views/ │ ├── index.blade.php # 登录页(DEFAULT.asp) │ ├── chat/ │ │ ├── frame.blade.php # 聊天主框架(CHAT.ASP) │ │ └── room.blade.php # 消息区 │ └── ... └── js/ ├── app.js └── chat.js # Laravel Echo 替代 newChat.js ``` --- ## 六、具体开发任务计划 ### ✅ 已完成 - [x] 创建 Laravel 12 项目 - [x] 安装 `laravel/reverb`、`predis/predis` - [x] 创建 MySQL 数据库 `chatroom` - [x] 配置 `.env`(MySQL root/root · Redis · Reverb · 时区 Asia/Shanghai) --- ### 🔲 第一阶段:数据库层(预计 1-2 天) **目标**:所有表创建完毕,并完成基础 Seeder。 - [ ] **修改默认 `users` 迁移**,对应 ASP `user` 表字段(username/passwd/sex/user_level/exp_num/friends/headface/等) - [ ] **创建 `rooms` 迁移**(room_name/owner/auto/des/title/permit_level/door_open) - [ ] **创建 `messages` 迁移**(room_id/from_user/to_user/content/is_secret/font_color/action/sent_at) - [ ] **创建 `sys_params` 迁移**(alias/guidetxt/body) - [ ] **创建 `ip_locks` 迁移**(ip/end_time/act_level) - [ ] **创建 `audit_logs` 迁移**(occ_time/occ_env/stype) - [ ] **创建 `guestbooks` 迁移**(who/towho/secret/ip/post_time/text_title/text_body) - [ ] **创建 `friend_calls` 迁移**(who/towho/callmess/calltime/read) - [ ] **创建 `friend_requests` 迁移**(who/towho/sub_time) - [ ] **创建 `actions` 迁移**(act_name/alias/toall/toself/toother) - [ ] **创建 `user_items` 迁移**(name/gg/times/dayy/lx — 对应道具/封口令等) - [ ] **创建 `scroll_ads` 迁移**(ad_title/ad_link/ad_new_flag) - [ ] **创建 `marriages` 迁移**(hyname/hyname1/hytime/hygb/hyjb) - [ ] **创建 `ip_logs` 迁移**(ip/sdate/uuname) - [ ] **创建所有 Model 文件**(含 `$fillable`、`$casts`、关联关系、中文 DocBlock) - [ ] **创建 `SysParamSeeder`**(写入系统默认参数:maxpeople/namelength/maxsayslength 等) - [ ] 运行 `php artisan migrate --seed`,验证建表成功 --- ### 🔲 第二阶段:Auth 认证(预计 1-2 天) **目标**:用户能够登录、注册、退出。 - [ ] **`LoginRequest`**(验证:username/password/captcha 验证码) - [ ] **`AuthController::index()`** — 登录页(含验证码生成,替代 `session("regjm")`) - [ ] **`AuthController::check()`** — 登录验证(含 IP 封锁检查 + 密码 MD5/bcrypt 双模式) - [ ] **`AuthController::logout()`** — 退出并清理 Redis 用户状态 - [ ] **`RegisterController::show()`** — 注册页 - [ ] **`RegisterController::store()`** — 注册逻辑(含 IP 注册频率限制) - [ ] **`ChatAuthenticate` 中间件** — 检查 Session 是否有效,无则跳转登录页 - [ ] **`LevelRequired` 中间件** — 检查用户等级,不足则拒绝(`chat.level:5`) - [ ] 在 **`bootstrap/app.php`** 注册以上中间件别名 - [ ] **登录 Blade 视图** `resources/views/index.blade.php`(仿原 DEFAULT.asp 样式) - [ ] 测试:注册 → 登录 → 退出完整流程 --- ### 🔲 第三阶段:Redis 状态层(预计 1 天) **目标**:`ChatStateService` 完整实现,替代原 Application 对象。 - [ ] **`ChatStateService`** 实现以下方法(全部带中文注释): - `userJoin(int $roomId, string $username, array $info): void` - `userLeave(int $roomId, string $username): void` - `getRoomUsers(int $roomId): array` - `pushMessage(int $roomId, array $message): void` - `getNewMessages(int $roomId, int $lastId): array` - `nextMessageId(int $roomId): int`(Redis 自增计数器) - `withLock(string $key, callable $callback): mixed`(分布式锁) - `getSysParam(string $alias): string`(读取系统参数,缓存1分钟) - [ ] **`MessageFilterService`** — 敏感词替换 + HTML 过滤(替代 `TrStr()` / `SHTM()` 函数) - [ ] **`UserLevelService`** — 从 Redis 读取当前用户等级 --- ### 🔲 第四阶段:WebSocket 广播(预计 1 天) **目标**:Reverb 正常运行,前端能收到实时消息。 - [ ] **`MessageSent` Event**(implement `ShouldBroadcast`)— 广播到 `room.{id}` Presence Channel - [ ] **`UserJoined` Event** — 用户进入广播 - [ ] **`UserLeft` Event** — 用户离开广播 - [ ] **`UserKicked` Event** — 踢人广播(私有频道,只发给被踢人) - [ ] **`UserMuted` Event** — 封口广播 - [ ] **`RoomTitleUpdated` Event** — 标题更新广播 - [ ] **`routes/channels.php`** — Presence Channel 鉴权(验证等级 + 返回用户信息) - [ ] **`resources/js/chat.js`** — Laravel Echo 接入(`Echo.join('room.X').here().joining().leaving().listen()`) - [ ] 运行 `php artisan reverb:start --debug`,测试 WebSocket 连通性 --- ### 🔲 第五阶段:聊天核心(预计 2-3 天) **目标**:进房、发言、离开完整流程可用。 - [ ] **`ChatController::init()`** — 进入房间(读取 Redis 用户列表 + 历史消息,替代 INIT.ASP) - [ ] **`ChatController::send()`** — 发言(过滤 → 推 Redis → `SaveMessageJob` → 广播 Event) - [ ] **`ChatController::leave()`** — 离开房间(清 Redis → 广播 `UserLeft`) - [ ] **`SaveMessageJob`** — 实现 `ShouldQueue`,异步写消息到 `messages` 表 - [ ] **聊天 Blade 视图** `resources/views/chat/frame.blade.php`(主框架,含 Vite 引入) - [ ] 测试:登录 → 进房 → 发言 → 另一浏览器实时收到消息 --- ### 🔲 第六阶段:房间管理(预计 2 天) - [ ] **`RoomController::list()`** — 房间列表(替代 ROOMLIST.ASP) - [ ] **`RoomController::create()`** / `store()` — 创建房间(替代 NEWROOM.ASP) - [ ] **`RoomController::edit()`** / `update()` — 修改设置(替代 ROOMSET.ASP) - [ ] **`RoomController::destroy()`** — 删除/解散房间(替代 CUTROOM.ASP) - [ ] **`RoomController::transfer()`** — 转让房主(替代 OVERROOM.ASP) - [ ] 对应 Blade 视图 --- ### 🔲 第七阶段:用户管理(预计 2 天) - [ ] **`UserController::info()`** — 用户信息(替代 USERinfo.ASP) - [ ] **`UserController::update()`** — 修改个人资料(替代 USERSET.ASP) - [ ] **`UserController::kick()`** — 踢人(替代 KILLUSER.ASP,广播 `UserKicked`) - [ ] **`UserController::mute()`** — 封口(道具 `user_items` 表操作) - [ ] **`UserController::changePassword()`** — 改密码(替代 chpasswd.asp) --- ### 🔲 第八阶段:管理后台(预计 3-5 天) - [ ] **`LevelRequired` 中间件** 保护 `/admin` 路由(需 level=15) - [ ] **`Admin\SystemController`** — 系统参数配置(替代 VIEWSYS.ASP) - [ ] **`Admin\UserManagerController`** — 用户管理列表(替代 `gl/` 目录) - [ ] **`Admin\SqlController`** — 后台 SQL 执行(替代 SQL.asp,⚠ 仅 SELECT) - [ ] **Horizon 面板** `/horizon`(队列监控,替代后台日志查看) - [ ] 对应 Blade 视图 --- ### 🔲 第九阶段:附加功能(按需) - [ ] 好友系统(FriendController) - [ ] 留言板(GuestbookController) - [ ] 排行榜(RankController) - [ ] 会员系统(`huiyuan/` 对应功能) - [ ] 滚动公告(ScrollAd 管理) --- ## 七、注意事项 ### 7.1 密码兼容策略 - 导入旧数据时,`password` 字段存原始 MD5 值(32位字符串) - 登录时双模式验证:`md5($input) === $storedPass` 成功后升级为 `bcrypt` - 新注册用户直接用 `bcrypt`(`Hash::make()`) - 约 3 个月后移除 MD5 兼容分支 ### 7.2 字符编码 - 原 Access 数据库为 **GBK 编码** - 所有 MySQL 表必须 `utf8mb4_unicode_ci` - 导入历史数据前转换: ```bash iconv -f GBK -t UTF-8 原文件.csv > 目标文件_utf8.csv ``` ### 7.3 REFRESH.ASP 已废弃 原系统的 6 秒 `` **完全由 Reverb WebSocket 实时推送取代**,无需任何轮询逻辑。 ### 7.4 Application 对象替代 | 原 ASP | Laravel 替代 | | ------------------------------- | --------------------------------------------- | | `Application("_user_list")` | `Redis::hgetall("room:{id}:users")` | | `Application("_says")` 环形缓冲 | `Redis::lrange("room:{id}:messages", 0, 199)` | | `Application("_room_list")` | `Redis::get("room:{id}:info")` + `rooms` 表 | | `Application.Lock/Unlock` | `Cache::lock("key", 10)->block(5, fn)` | ### 7.5 Flash 游戏(暂不处理) `game/`、`pig/`、`Gupiao/` 等目录内的 `.swf` Flash 文件现代浏览器已不支持,**本期不做转换**,后续单独用 HTML5/Canvas 重写。 --- ## 八、常用命令速查 ```bash # 创建 Model + Migration(-m 同时生成迁移) php artisan make:model Room -m # 创建 Controller(-r 生成 RESTful 方法) php artisan make:controller ChatController # 创建广播 Event php artisan make:event MessageSent # 创建队列 Job php artisan make:job SaveMessageJob # 创建 Middleware php artisan make:middleware ChatAuthenticate # 创建 Form Request php artisan make:request SendMessageRequest # 重置迁移(开发阶段) php artisan migrate:fresh --seed # 查看路由列表 php artisan route:list --columns=method,uri,name,action # 代码格式化(提交前必须运行) vendor/bin/pint --dirty # Horizon 队列监控(生产环境) php artisan horizon # 重启 Horizon(更新代码后) php artisan horizon:terminate # 清理所有缓存 php artisan optimize:clear ``` --- > 原 ASP 源码参考路径:`/Users/pllx/Web/chat/hp0709/` > 数据库 SQL 参考:`/Users/pllx/Web/chat/hp0709_php/database.sql`