# 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
<?php

/**
 * 文件功能：[本文件的业务职责描述]
 *
 * 对应原 ASP 文件：[原文件名.asp]
 *
 * @package  App\[命名空间]
 * @author   ChatRoom Laravel
 * @version  1.0.0
 */

namespace App\Services;

class ChatStateService
{
    /**
     * 用户进入聊天房间，将其信息写入 Redis。
     *
     * 替代原 ASP 的 Application("_user_list") 字符串拼接操作。
     *
     * @param  int    $roomId    房间 ID
     * @param  string $username  用户名
     * @param  array  $userInfo  用户信息（等级、头像、性别等）
     */
    public function userJoin(int $roomId, string $username, array $userInfo): void
    {
        // 将用户信息序列化后存入 Redis Hash，Key 为 "room:{房间ID}:users"
        $this->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 秒 `<meta http-equiv=refresh>` **完全由 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`
