---
trigger: always_on
---

> **技术栈**：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));
    }
}
```

### 2.5 迁移文件注意事项

同时新建多个迁移文件时，要注意 是否有关联主键问题，主键所在表要先创建，所以迁移文件名称 要比被调用表文件名的靠前，否则执行迁移时会报错；
