2026-02-26 12:02:00 +08:00
|
|
|
|
---
|
|
|
|
|
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-03-03 19:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
### 2.5 迁移文件注意事项
|
|
|
|
|
|
|
|
|
|
|
|
同时新建多个迁移文件时,要注意 是否有关联主键问题,主键所在表要先创建,所以迁移文件名称 要比被调用表文件名的靠前,否则执行迁移时会报错;
|