2026-02-26 12:02:00 +08:00
|
|
|
|
# ChatRoom 开发计划与注意事项
|
|
|
|
|
|
|
|
|
|
|
|
> **技术栈**:Laravel 12 · PHP 8.4 · Laravel Reverb (WebSocket) · Redis · MySQL 8.0 · Laravel Horizon
|
2026-02-28 14:14:20 +08:00
|
|
|
|
> **参照原项目**:`/Users/pllx/Web/chat/hp0709`(VBScript ASP 聊天室,仅作功能参考,不做数据迁移)
|
2026-02-28 12:49:26 +08:00
|
|
|
|
> **目标域名**:`http://chatroom.test`(Herd 自动配置)| `https://chat.ay.lc`(生产环境)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、环境版本要求
|
|
|
|
|
|
|
|
|
|
|
|
| 组件 | 版本 |
|
|
|
|
|
|
| --------------------- | -------------------------- |
|
|
|
|
|
|
| **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([
|
2026-02-28 12:49:26 +08:00
|
|
|
|
'chat.auth' => \App\Http\Middleware\ChatAuthenticate::class,
|
|
|
|
|
|
'chat.level' => \App\Http\Middleware\LevelRequired::class,
|
|
|
|
|
|
'chat.site_owner' => \App\Http\Middleware\SiteOwnerOnly::class,
|
2026-02-26 12:02:00 +08:00
|
|
|
|
]);
|
|
|
|
|
|
})
|
|
|
|
|
|
->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
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
# 安装 PHP 依赖(已完成)
|
|
|
|
|
|
composer install
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
# 创建数据库(已完成)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
mysql -u root -proot -e "CREATE DATABASE IF NOT EXISTS chatroom CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
# 运行数据库迁移 + 基础数据填充
|
|
|
|
|
|
php artisan migrate --seed
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
# 安装前端依赖并构建(已完成)
|
|
|
|
|
|
npm install
|
|
|
|
|
|
npm run build
|
2026-02-26 12:02:00 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**开发时运行的服务**:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
php artisan reverb:start --debug # WebSocket 服务器 :8080
|
|
|
|
|
|
php artisan horizon # 队列 Worker(含 Web 监控 /horizon)
|
2026-02-28 12:49:26 +08:00
|
|
|
|
npm run dev # Vite 热更新(开发阶段)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
# HTTP 由 Herd 自动提供 chatroom.test
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、数据库迁移对照表
|
|
|
|
|
|
|
|
|
|
|
|
**原 Access 表** → **Laravel Migration** 对应关系:
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
| 原 ASP 表 | Laravel 迁移文件 | Model 类 | 说明 |
|
|
|
|
|
|
| ----------- | ---------------------------------- | ------------------- | ---------------------------------- |
|
|
|
|
|
|
| `user` | `create_users_table` | `User` | 主用户表(含 jjb 金币 / exp 经验) |
|
|
|
|
|
|
| `room` | `create_rooms_table` | `Room` | 聊天房间 |
|
|
|
|
|
|
| _(内存)_ | `create_messages_table` | `Message` | 消息归档(实时用 Redis) |
|
|
|
|
|
|
| `sysparam` | `create_sys_params_table` | `Sysparam` | 系统参数(alias/guidetxt/body) |
|
|
|
|
|
|
| `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` | 房间描述模板 |
|
|
|
|
|
|
| `autoact` | `create_autoacts_table` | `Autoact` | 机器人随机事件 |
|
|
|
|
|
|
| _(新增)_ | `create_gifts_table` | `Gift` | 礼物/鲜花定义(名称/图标/金币) |
|
|
|
|
|
|
| _(新增)_ | `create_shop_items_table` | `ShopItem` | 商店商品(特效卡/改名卡等) |
|
|
|
|
|
|
| _(新增)_ | `create_user_purchases_table` | `UserPurchase` | 用户购买记录 |
|
|
|
|
|
|
| _(新增)_ | `create_vip_levels_table` | `VipLevel` | VIP 会员等级(倍率/进入模板) |
|
|
|
|
|
|
| _(新增)_ | `create_ai_provider_configs_table` | `AiProviderConfig` | AI 服务商配置(多厂商支持) |
|
|
|
|
|
|
| _(新增)_ | `create_ai_usage_logs_table` | `AiUsageLog` | AI 使用日志(Token 用量记录) |
|
|
|
|
|
|
| _(新增)_ | `create_username_blacklist_table` | `UsernameBlacklist` | 用户名黑名单 |
|
2026-02-28 14:06:55 +08:00
|
|
|
|
| _(新增)_ | `create_user_currency_logs_table` | `UserCurrencyLog` | 积分流水(经验/金币/魅力变动记录) |
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、推荐目录结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
app/
|
|
|
|
|
|
├── Events/ # WebSocket 广播事件(ShouldBroadcast)
|
|
|
|
|
|
│ ├── MessageSent.php # 消息发送(替代 NEWSAY.ASP)
|
|
|
|
|
|
│ ├── UserJoined.php # 用户进房(替代 INIT.ASP)
|
|
|
|
|
|
│ ├── UserLeft.php # 用户离开(替代 LEAVE.ASP)
|
|
|
|
|
|
│ ├── UserKicked.php # 踢人
|
|
|
|
|
|
│ ├── UserMuted.php # 封口
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ ├── UserLevelUp.php # 用户升级通知
|
|
|
|
|
|
│ ├── RoomTitleUpdated.php # 房间标题更新
|
|
|
|
|
|
│ ├── ScreenCleared.php # 管理员清屏
|
|
|
|
|
|
│ └── EffectBroadcast.php # 特效广播(烟花/下雨等)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
│
|
|
|
|
|
|
├── Services/ # 业务逻辑服务层(纯 PHP,不含 HTTP 逻辑)
|
|
|
|
|
|
│ ├── ChatStateService.php # Redis 全局状态(替代 Application 对象)
|
|
|
|
|
|
│ ├── MessageFilterService.php # 敏感词/HTML 过滤
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ ├── UserLevelService.php # 等级权限判断(替代 getLevel())
|
|
|
|
|
|
│ ├── AiChatService.php # AI 聊天服务(多厂商适配:OpenAI/DeepSeek 等)
|
|
|
|
|
|
│ ├── VipService.php # VIP 开通/到期检查
|
2026-02-28 14:06:55 +08:00
|
|
|
|
│ ├── ShopService.php # 商店购买 + 道具激活逻辑
|
|
|
|
|
|
│ └── UserCurrencyService.php # ⭐ 积分统一操作服务(所有经验/金币/魅力变更必须经此)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
│
|
|
|
|
|
|
├── Http/
|
|
|
|
|
|
│ ├── Controllers/
|
|
|
|
|
|
│ │ ├── AuthController.php # DEFAULT.asp + CHECK.asp + CLOSE.ASP
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ │ ├── ChatController.php # NEWSAY.ASP + INIT.ASP + LEAVE.ASP + 心跳
|
|
|
|
|
|
│ │ ├── RoomController.php # ROOM*.ASP 系列(大厅/创建/编辑/删除/转让)
|
|
|
|
|
|
│ │ ├── UserController.php # USERSET + DOUSER + KILLUSER + 封禁/解封
|
|
|
|
|
|
│ │ ├── GuestbookController.php # GUEST.ASP(留言板)
|
|
|
|
|
|
│ │ ├── LeaderboardController.php # TOP10.ASP(排行榜)
|
|
|
|
|
|
│ │ ├── ShopController.php # 商店购买 + 改名
|
|
|
|
|
|
│ │ ├── ChatBotController.php # AI 聊天机器人接口
|
|
|
|
|
|
│ │ ├── FishingController.php # 钓鱼小游戏(复刻原版 diaoyu/ 功能)
|
|
|
|
|
|
│ │ ├── AdminCommandController.php # 聊天室内管理员快捷命令
|
2026-02-26 12:02:00 +08:00
|
|
|
|
│ │ └── Admin/
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ │ ├── DashboardController.php # 后台首页总览
|
|
|
|
|
|
│ │ ├── SystemController.php # VIEWSYS.ASP 系统参数配置
|
|
|
|
|
|
│ │ ├── UserManagerController.php # 用户大盘管理
|
|
|
|
|
|
│ │ ├── RoomManagerController.php # 房间大盘管理
|
|
|
|
|
|
│ │ ├── AutoactController.php # 随机事件管理
|
|
|
|
|
|
│ │ ├── VipController.php # VIP 等级 CRUD
|
2026-02-28 14:06:55 +08:00
|
|
|
|
│ │ ├── AiProviderController.php # AI 厂商配置管理(含启用/禁用/设为默认)
|
|
|
|
|
|
│ │ ├── CurrencyStatsController.php # ⭐ 积分流水活动统计(每日来源产出分析)
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ │ └── SmtpController.php # 邮件发信配置
|
2026-02-26 12:02:00 +08:00
|
|
|
|
│ │
|
|
|
|
|
|
│ ├── Middleware/
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ │ ├── ChatAuthenticate.php # 聊天室登录验证(检查 Session)
|
|
|
|
|
|
│ │ ├── LevelRequired.php # 等级权限中间件(chat.level:super)
|
|
|
|
|
|
│ │ └── SiteOwnerOnly.php # 超级站长专属(chat.site_owner)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
│ │
|
|
|
|
|
|
│ └── Requests/ # Form Request 验证
|
|
|
|
|
|
│ ├── LoginRequest.php
|
|
|
|
|
|
│ ├── SendMessageRequest.php
|
|
|
|
|
|
│ └── CreateRoomRequest.php
|
|
|
|
|
|
│
|
|
|
|
|
|
├── Models/
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ ├── User.php ├── Room.php ├── Message.php
|
|
|
|
|
|
│ ├── Sysparam.php ├── IpLock.php ├── IpLog.php
|
|
|
|
|
|
│ ├── AuditLog.php ├── AdminLog.php ├── Guestbook.php
|
|
|
|
|
|
│ ├── FriendCall.php ├── FriendRequest.php ├── Marriage.php
|
|
|
|
|
|
│ ├── Action.php ├── Autoact.php ├── ScrollAd.php
|
|
|
|
|
|
│ ├── RoomDescription.php ├── UserItem.php ├── Gift.php
|
|
|
|
|
|
│ ├── ShopItem.php ├── UserPurchase.php ├── VipLevel.php
|
|
|
|
|
|
│ ├── AiProviderConfig.php ├── AiUsageLog.php ├── UsernameBlacklist.php
|
2026-02-28 14:06:55 +08:00
|
|
|
|
│ ├── UserCurrencyLog.php # ⭐ 积分流水记录
|
2026-02-28 12:49:26 +08:00
|
|
|
|
│ └── System/
|
|
|
|
|
|
│ └── PlatformSmtpAccount.php
|
2026-02-26 12:02:00 +08:00
|
|
|
|
│
|
2026-02-28 14:06:55 +08:00
|
|
|
|
├── Enums/
|
|
|
|
|
|
│ └── CurrencySource.php # ⭐ 积分来源枚举(auto_save/fishing_gain/admin_adjust 等)
|
|
|
|
|
|
│
|
2026-02-26 12:02:00 +08:00
|
|
|
|
└── Jobs/
|
|
|
|
|
|
└── SaveMessageJob.php # 异步将消息持久化到 MySQL
|
|
|
|
|
|
|
|
|
|
|
|
bootstrap/
|
|
|
|
|
|
└── app.php # ⚠ Laravel 12:中间件/路由在此配置(无 Kernel.php)
|
|
|
|
|
|
|
|
|
|
|
|
routes/
|
|
|
|
|
|
├── web.php # 所有 HTTP 路由
|
2026-02-28 12:49:26 +08:00
|
|
|
|
├── api.php # API 路由(验证码等)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
└── channels.php # WebSocket Presence Channel 鉴权
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、具体开发任务计划
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
> **图例**:`[x]` 已完成 · `[/]` 进行中 · `[ ]` 待开发
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第一阶段:数据库层
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 修改默认 `users` 迁移(username/jjb/exp/sex/headface/user_level/email 等)
|
|
|
|
|
|
- [x] 创建 `rooms` 迁移(room_name/owner/auto/des/title/permit_level/door_open)
|
|
|
|
|
|
- [x] 创建 `messages` 迁移(room_id/from_user/to_user/content/is_secret/font_color/action/sent_at)
|
|
|
|
|
|
- [x] 创建 `sys_params` 迁移(alias/guidetxt/body)
|
|
|
|
|
|
- [x] 创建 `ip_locks` 迁移
|
|
|
|
|
|
- [x] 创建 `audit_logs` 迁移
|
|
|
|
|
|
- [x] 创建 `guestbooks` 迁移
|
|
|
|
|
|
- [x] 创建 `friend_calls` 迁移
|
|
|
|
|
|
- [x] 创建 `friend_requests` 迁移
|
|
|
|
|
|
- [x] 创建 `actions` 迁移
|
|
|
|
|
|
- [x] 创建 `admin_logs` 迁移
|
|
|
|
|
|
- [x] 创建 `user_items` 迁移(封口令道具)
|
|
|
|
|
|
- [x] 创建 `scroll_ads` 迁移
|
|
|
|
|
|
- [x] 创建 `marriages` 迁移
|
|
|
|
|
|
- [x] 创建 `ip_logs` 迁移
|
|
|
|
|
|
- [x] 创建 `room_descriptions` 迁移
|
|
|
|
|
|
- [x] 创建 `autoacts` 迁移(随机事件/机器人自动发言)
|
|
|
|
|
|
- [x] 创建 `gifts` 迁移(礼物定义:emoji/cost/charm)
|
|
|
|
|
|
- [x] 创建 `shop_items` 迁移(商店商品:特效卡/改名卡)
|
|
|
|
|
|
- [x] 创建 `user_purchases` 迁移(购买记录)
|
|
|
|
|
|
- [x] 创建 `vip_levels` 迁移(VIP 等级:倍率/进入模板/价格)
|
|
|
|
|
|
- [x] 创建 `ai_provider_configs` 迁移(AI 服务商配置)
|
|
|
|
|
|
- [x] 创建 `ai_usage_logs` 迁移(AI 用量日志)
|
|
|
|
|
|
- [x] 创建 `username_blacklist` 迁移(用户名黑名单)
|
|
|
|
|
|
- [x] 创建所有 Model 文件(含 `$fillable`、`$casts`、关联关系、中文 DocBlock)
|
|
|
|
|
|
- [x] 创建 `DatabaseSeeder`(默认房间「公共大厅」、系统参数)
|
|
|
|
|
|
- [x] 运行 `php artisan migrate --seed` 验证建表成功
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第二阶段:Auth 认证
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] `AuthController::login()` — 登录(含 IP 封锁检查 + 密码 bcrypt)
|
|
|
|
|
|
- [x] `AuthController::logout()` — 退出并清理 Redis 用户状态
|
|
|
|
|
|
- [x] `ChatAuthenticate` 中间件 — 检查 Session 是否有效
|
|
|
|
|
|
- [x] `LevelRequired` 中间件 — 权限等级检查(`chat.level:super`)
|
|
|
|
|
|
- [x] `SiteOwnerOnly` 中间件 — 超级站长专属(最高权限区块)
|
|
|
|
|
|
- [x] 登录 Blade 视图 `resources/views/index.blade.php`
|
|
|
|
|
|
- [x] 测试:登录 → 退出完整流程
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第三阶段:Redis 状态层
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] `ChatStateService` — 完整实现(userJoin/userLeave/getRoomUsers/pushMessage/nextMsgId/withLock/getSysParam)
|
|
|
|
|
|
- [x] `MessageFilterService` — 敏感词替换 + HTML 过滤
|
|
|
|
|
|
- [x] `UserLevelService` — 等级权限判断
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第四阶段:WebSocket 广播
|
|
|
|
|
|
|
|
|
|
|
|
- [x] `MessageSent` Event(广播到 `room.{id}` Presence Channel)
|
|
|
|
|
|
- [x] `UserJoined` Event — 用户进入广播
|
|
|
|
|
|
- [x] `UserLeft` Event — 用户离开广播
|
|
|
|
|
|
- [x] `UserKicked` Event — 踢人广播(私有频道)
|
|
|
|
|
|
- [x] `UserMuted` Event — 封口广播
|
|
|
|
|
|
- [x] `UserLevelUp` Event — 升级通知广播
|
|
|
|
|
|
- [x] `RoomTitleUpdated` Event — 标题更新广播
|
|
|
|
|
|
- [x] `ScreenCleared` Event — 管理员清屏广播
|
|
|
|
|
|
- [x] `EffectBroadcast` Event — 特效广播(烟花/下雨/闪电/彩带)
|
|
|
|
|
|
- [x] `routes/channels.php` — Presence Channel 鉴权
|
|
|
|
|
|
- [x] `resources/js/chat.js` — Laravel Echo 接入
|
|
|
|
|
|
- [x] Reverb 连通性测试通过
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### ✅ 第五阶段:聊天核心
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] `ChatController::init()` — 进入房间(历史消息 + 在线用户)
|
|
|
|
|
|
- [x] `ChatController::send()` — 发言(过滤 → Redis → SaveMessageJob → 广播)
|
|
|
|
|
|
- [x] `ChatController::heartbeat()` — 挂机心跳(限流:每分钟6次)
|
|
|
|
|
|
- [x] `ChatController::leave()` — 离开房间
|
|
|
|
|
|
- [x] `ChatController::headfaceList()` — 头像列表
|
|
|
|
|
|
- [x] `ChatController::changeAvatar()` — 修改头像
|
|
|
|
|
|
- [x] `ChatController::setAnnouncement()` — 设置房间公告
|
|
|
|
|
|
- [x] `ChatController::sendFlower()` — 送花/礼物(扣金币 + 增魅力)
|
|
|
|
|
|
- [x] `SaveMessageJob` — 异步消息持久化到 MySQL
|
|
|
|
|
|
- [x] 聊天主界面 Blade 视图 `resources/views/chat/frame.blade.php`
|
|
|
|
|
|
- [x] 测试:进房 → 发言 → 实时收到消息
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第六阶段:房间管理
|
|
|
|
|
|
|
|
|
|
|
|
- [x] `RoomController::index()` — 大厅房间列表
|
|
|
|
|
|
- [x] `RoomController::store()` — 创建房间
|
|
|
|
|
|
- [x] `RoomController::update()` — 修改设置
|
|
|
|
|
|
- [x] `RoomController::destroy()` — 删除/解散房间
|
|
|
|
|
|
- [x] `RoomController::transfer()` — 转让房主
|
|
|
|
|
|
- [x] 对应 Blade 视图(大厅列表、引导页)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第七阶段:用户管理
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] `UserController::show()` — 用户名片/资料页
|
|
|
|
|
|
- [x] `UserController::updateProfile()` — 修改个人资料
|
|
|
|
|
|
- [x] `UserController::changePassword()` — 改密码
|
|
|
|
|
|
- [x] `UserController::kick()` — 踢人
|
|
|
|
|
|
- [x] `UserController::mute()` — 封口
|
|
|
|
|
|
- [x] `UserController::ban()` — 封号
|
|
|
|
|
|
- [x] `UserController::banIp()` — 封 IP
|
|
|
|
|
|
- [x] 邮箱验证码发送(`Api\VerificationController`)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第八阶段:管理后台
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] `Admin\DashboardController` — 后台首页总览
|
|
|
|
|
|
- [x] `Admin\SystemController` — 系统参数配置
|
|
|
|
|
|
- [x] `Admin\SmtpController` — 邮件发信配置
|
|
|
|
|
|
- [x] `Admin\UserManagerController` — 用户大盘管理(编辑/删除)
|
|
|
|
|
|
- [x] `Admin\RoomManagerController` — 房间大盘管理
|
|
|
|
|
|
- [x] `Admin\AutoactController` — 随机事件 CRUD 管理
|
|
|
|
|
|
- [x] `Admin\VipController` — VIP 等级 CRUD 管理
|
|
|
|
|
|
- [x] `Admin\AiProviderController` — AI 厂商配置管理(含启用/禁用/设为默认)
|
|
|
|
|
|
- [x] Horizon 面板 `/horizon`(队列监控)
|
|
|
|
|
|
- [x] 对应 Blade 视图
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第九阶段:外围功能
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] `LeaderboardController` — 风云排行榜(经验/金币/魅力榜)
|
|
|
|
|
|
- [x] `GuestbookController` — 留言板(发表/删除)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### ✅ 第十阶段:扩展玩法
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [x] **钓鱼小游戏** `FishingController` — 复刻原版 `diaoyu/` 功能(投竿/收竿/随机收益)
|
|
|
|
|
|
- [x] **AI 聊天机器人** `ChatBotController` + `AiChatService`(多厂商:OpenAI/DeepSeek/Gemini 等)
|
|
|
|
|
|
- [x] **礼物/送花系统** — `Gift` 模型 + `ChatController::sendFlower()`(金币消耗 + 魅力增量)
|
|
|
|
|
|
- [x] **商店系统** `ShopController` + `ShopService`(购买特效卡/改名卡,`ShopItem` + `UserPurchase`)
|
|
|
|
|
|
- [x] **VIP 会员系统** `VipService` + `VipLevel`(进入/离开专属模板、倍率加成)
|
|
|
|
|
|
- [x] **管理员快捷命令** `AdminCommandController`(警告/踢人/封口/冻结/清屏/全服广播/特效)
|
|
|
|
|
|
- [x] **特效系统** `EffectBroadcast` Event(烟花/下雨/闪电/彩带,支持单次卡/周卡)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 14:06:55 +08:00
|
|
|
|
### ✅ 第十一阶段:积分流水审计系统
|
|
|
|
|
|
|
|
|
|
|
|
- [x] `App\Enums\CurrencySource` — 所有来源枚举(可扩展)
|
|
|
|
|
|
- [x] `App\Models\UserCurrencyLog` — 流水 Model(含查询 Scope)
|
|
|
|
|
|
- [x] `App\Services\UserCurrencyService` — 统一积分变更服务(**所有经验/金币/魅力修改必须走此服务**)
|
|
|
|
|
|
- [x] 接入 `AutoSaveExp`(auto_save 来源)
|
|
|
|
|
|
- [x] 接入 `FishingController`(fishing_cost / fishing_gain 来源)
|
|
|
|
|
|
- [x] 接入 `ChatController::init()` 新人礼包(newbie_bonus 来源)
|
|
|
|
|
|
- [x] 接入 `Admin\UserManagerController`(admin_adjust 来源)
|
|
|
|
|
|
- [x] `LeaderboardController::todayIndex()` — 今日风云榜独立页(`/leaderboard/today`)
|
|
|
|
|
|
- [x] `LeaderboardController::myLogs()` — 用户个人流水日志(`/my/currency-logs`)
|
|
|
|
|
|
- [x] `Admin\CurrencyStatsController` — 后台积分活动统计(`/admin/currency-stats`)
|
|
|
|
|
|
- [x] 导航栏新增「📅 今日榜」按钮,后台侧边栏新增「📈 积分流水统计」入口
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
### 🔲 待完善事项
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
- [ ] **用户名黑名单管理**(后台 CRUD,`UsernameBlacklist` 模型已建,路由/界面待完成)
|
|
|
|
|
|
- [ ] **滚动公告管理**(后台 CRUD,`ScrollAd` 模型已建,界面待完成)
|
|
|
|
|
|
- [ ] **好友系统**(`FriendCall` / `FriendRequest` 模型已建,Controller 待完成)
|
|
|
|
|
|
- [ ] **婚姻系统**(`Marriage` 模型已建,界面待完成)
|
|
|
|
|
|
- [ ] **单元测试**(核心 Service 层测试覆盖率)
|
|
|
|
|
|
- [ ] **Flash 游戏替代**(`game/`、`pig/` 等 .swf 文件,考虑 HTML5/Canvas 重写)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 七、注意事项
|
|
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.1 密码策略
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
- 所有用户密码使用 `bcrypt`(`Hash::make()`)
|
|
|
|
|
|
- 登录时使用 `Hash::check()` 验证
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.2 实时推送机制
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
**聊天室所有状态变更均通过 Reverb WebSocket 广播**,无需轮询刷新。
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
> [!NOTE]
|
|
|
|
|
|
> 历史上基于 `<meta http-equiv=refresh>` 的 6秒刷新方案已被完全取代,任何涉及聊天室状态更新的功能都应通过 Event → Broadcasting 实现,**禁止在实现中引入轮询逻辑**。
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.3 Redis 状态层设计
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
| 数据 | Redis Key 格式 | 说明 |
|
|
|
|
|
|
| ------------ | ------------------------- | -------------------------- |
|
|
|
|
|
|
| 在线用户列表 | `room:{id}:users` Hash | 值为用户信息 JSON |
|
|
|
|
|
|
| 消息环形缓冲 | `room:{id}:messages` List | 最多保留 200 条 |
|
|
|
|
|
|
| 房间基本信息 | `room:{id}:info` String | JSON 快照 |
|
|
|
|
|
|
| 并发锁 | `Cache::lock("key", 10)` | 使用 `->block(5, fn)` 实现 |
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.4 Flash 游戏(暂不处理)
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
2026-02-28 12:49:26 +08:00
|
|
|
|
`game/`、`pig/`、`Gupiao/` 等目录内的 `.swf` Flash 文件现代浏览器已不支持,**本期不做转换**。
|
|
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.5 AI 机器人多厂商支持
|
2026-02-28 12:49:26 +08:00
|
|
|
|
|
|
|
|
|
|
`AiChatService` 支持以下 Provider,通过 `ai_provider_configs` 后台配置切换:
|
|
|
|
|
|
|
|
|
|
|
|
| Provider | 推荐模型 | 说明 |
|
|
|
|
|
|
| ---------- | ------------------ | ---------------------------- |
|
|
|
|
|
|
| `openai` | `gpt-4o-mini` | OpenAI 官方 API |
|
|
|
|
|
|
| `deepseek` | `deepseek-chat` | 国产低成本,兼容 OpenAI 接口 |
|
|
|
|
|
|
| `gemini` | `gemini-1.5-flash` | Google Gemini |
|
|
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.6 商店商品类型说明
|
2026-02-28 12:49:26 +08:00
|
|
|
|
|
|
|
|
|
|
| type | slug 示例 | 说明 |
|
|
|
|
|
|
| ---------- | ---------------- | ---------------------- |
|
|
|
|
|
|
| `instant` | `once_fireworks` | 单次特效卡(即时生效) |
|
|
|
|
|
|
| `duration` | `week_fireworks` | 周卡(7天内持续生效) |
|
|
|
|
|
|
| `rename` | `rename` | 改名卡(改变显示名称) |
|
2026-02-26 12:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-28 14:13:00 +08:00
|
|
|
|
### 7.7 积分流水系统 — 开发者使用指南 ⭐
|
2026-02-28 14:06:55 +08:00
|
|
|
|
|
|
|
|
|
|
> [!IMPORTANT]
|
|
|
|
|
|
> **铁律**:所有涉及修改用户 `exp_num`(经验)、`jjb`(金币)、`meili`(魅力)的操作,
|
|
|
|
|
|
> **必须通过 `UserCurrencyService::change()` 执行**,禁止直接操作 `$user->increment()` 或 `User::update()`。
|
|
|
|
|
|
|
|
|
|
|
|
#### 8.1 注入服务
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
use App\Enums\CurrencySource;
|
|
|
|
|
|
use App\Services\UserCurrencyService;
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
|
|
private readonly UserCurrencyService $currencyService,
|
|
|
|
|
|
) {}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 8.2 单次变更
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
// 给用户增加经验(正数=增加,负数=扣除)
|
|
|
|
|
|
$this->currencyService->change(
|
|
|
|
|
|
user: $user,
|
|
|
|
|
|
currency: 'exp', // 'exp' | 'gold' | 'charm'
|
|
|
|
|
|
amount: 10, // 正数=增加,负数=消耗(不会低于0)
|
|
|
|
|
|
source: CurrencySource::AUTO_SAVE, // 来源(必须用 Enum,不得写裸字符串)
|
|
|
|
|
|
remark: '挂机获得经验', // 可选备注
|
|
|
|
|
|
roomId: 1, // 可选房间ID
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 扣金币(钓鱼消耗)
|
|
|
|
|
|
$this->currencyService->change($user, 'gold', -5, CurrencySource::FISHING_COST, '抛竿消耗');
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 8.3 批量变更(自动存点场景)
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
// 对多个用户批量发放,内部逐条原子执行
|
|
|
|
|
|
$this->currencyService->batchChange(
|
|
|
|
|
|
users: $onlineUsers, // Collection<User>
|
|
|
|
|
|
currency: 'exp',
|
|
|
|
|
|
amount: 1,
|
|
|
|
|
|
source: CurrencySource::AUTO_SAVE,
|
|
|
|
|
|
remark: '定时存点',
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 8.4 添加新的积分来源(扩展 Enum)
|
|
|
|
|
|
|
|
|
|
|
|
在 `App\Enums\CurrencySource` 枚举中新增一个常量即可,**无需修改数据库**:
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
// app/Enums/CurrencySource.php
|
|
|
|
|
|
enum CurrencySource: string
|
|
|
|
|
|
{
|
|
|
|
|
|
case AUTO_SAVE = 'auto_save';
|
|
|
|
|
|
case FISHING_COST = 'fishing_cost';
|
|
|
|
|
|
case FISHING_GAIN = 'fishing_gain';
|
|
|
|
|
|
case NEWBIE_BONUS = 'newbie_bonus';
|
|
|
|
|
|
case ADMIN_ADJUST = 'admin_adjust';
|
|
|
|
|
|
// ↓ 新增活动时在此追加一行即可
|
|
|
|
|
|
case DAILY_SIGN_IN = 'daily_sign_in'; // 示例:每日签到
|
|
|
|
|
|
|
|
|
|
|
|
/** 返回可读名称(用于后台统计展示) */
|
|
|
|
|
|
public function label(): string
|
|
|
|
|
|
{
|
|
|
|
|
|
return match($this) {
|
|
|
|
|
|
self::AUTO_SAVE => '⏰ 自动存点',
|
|
|
|
|
|
self::FISHING_COST => '🎣 钓鱼消耗',
|
|
|
|
|
|
self::FISHING_GAIN => '🐟 钓鱼收获',
|
|
|
|
|
|
self::NEWBIE_BONUS => '🎁 新人礼包',
|
|
|
|
|
|
self::ADMIN_ADJUST => '🛠️ 管理员调整',
|
|
|
|
|
|
self::DAILY_SIGN_IN => '📅 每日签到', // 对应新增
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 8.5 流水查询 Scope
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
use App\Models\UserCurrencyLog;
|
|
|
|
|
|
|
|
|
|
|
|
// 查询某用户最近7天的经验记录
|
|
|
|
|
|
$logs = UserCurrencyLog::where('user_id', $user->id)
|
|
|
|
|
|
->currency('exp')
|
|
|
|
|
|
->whereDate('created_at', '>=', now()->subDays(7))
|
|
|
|
|
|
->orderByDesc('created_at')
|
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
|
|
// 今日金币排行(今日获得量前20名)
|
|
|
|
|
|
$ranking = UserCurrencyLog::whereDate('created_at', today())
|
|
|
|
|
|
->where('currency', 'gold')
|
|
|
|
|
|
->where('amount', '>', 0) // 只算增加,不算消耗
|
|
|
|
|
|
->selectRaw('user_id, username, SUM(amount) as total')
|
|
|
|
|
|
->groupBy('user_id', 'username')
|
|
|
|
|
|
->orderByDesc('total')
|
|
|
|
|
|
->limit(20)
|
|
|
|
|
|
->get();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 8.6 相关页面路由速查
|
|
|
|
|
|
|
|
|
|
|
|
| 页面 | 路由名 | URI |
|
|
|
|
|
|
| ------------ | ---------------------------- | --------------------------------------- |
|
|
|
|
|
|
| 个人积分日志 | `currency.my-logs` | `/my/currency-logs?currency=exp&days=7` |
|
|
|
|
|
|
| 今日风云榜 | `leaderboard.today` | `/leaderboard/today` |
|
|
|
|
|
|
| 累计风云榜 | `leaderboard.index` | `/leaderboard` |
|
|
|
|
|
|
| 后台积分统计 | `admin.currency-stats.index` | `/admin/currency-stats?date=2026-02-28` |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-01 00:36:54 +08:00
|
|
|
|
### 7.8 全局弹窗系统 `window.chatDialog` ⭐
|
|
|
|
|
|
|
|
|
|
|
|
> [!IMPORTANT]
|
|
|
|
|
|
> 聊天室内**禁止使用**浏览器原生的 `alert()` / `confirm()` / `prompt()`。
|
|
|
|
|
|
> 原因:原生弹窗在 Chrome 中会与 `beforeunload` / `pagehide` 事件产生冲突,导致弹窗闪烁消失。
|
|
|
|
|
|
> **所有需要提示或确认的场景,必须使用 `window.chatDialog`。**
|
|
|
|
|
|
|
|
|
|
|
|
#### 文件位置
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
resources/views/chat/partials/global-dialog.blade.php ← 弹窗 HTML + JS
|
|
|
|
|
|
resources/views/chat/frame.blade.php ← 已 @include,全页面可用
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### API 说明
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 模式 | 返回值 | 说明 |
|
|
|
|
|
|
| ----------------------------------------- | ----------------- | ------------------ | ---------------- |
|
|
|
|
|
|
| `chatDialog.alert(msg, title?, color?)` | 仅「确定」按钮 | `Promise<void>` | 替代 `alert()` |
|
|
|
|
|
|
| `chatDialog.confirm(msg, title?, color?)` | 「取消」+「确定」 | `Promise<boolean>` | 替代 `confirm()` |
|
|
|
|
|
|
|
|
|
|
|
|
参数说明:
|
|
|
|
|
|
|
|
|
|
|
|
- `msg`:弹窗正文内容(字符串)
|
|
|
|
|
|
- `title`:标题栏文字,默认 `'提示'` / `'请确认'`
|
|
|
|
|
|
- `color`:标题栏背景色(CSS 颜色值),默认蓝色 `#336699` / 红色 `#cc4444`
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用示例
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// ① alert 模式(提示成功 — 绿色)
|
|
|
|
|
|
await window.chatDialog.alert("操作成功!", "提示", "#16a34a");
|
|
|
|
|
|
|
|
|
|
|
|
// ② alert 模式(提示失败 — 红色)
|
|
|
|
|
|
await window.chatDialog.alert("网络异常,请稍后重试", "错误", "#cc4444");
|
|
|
|
|
|
|
|
|
|
|
|
// ③ confirm 模式(执行危险操作前确认)
|
|
|
|
|
|
const yes = await window.chatDialog.confirm(
|
|
|
|
|
|
"确定要撤销该用户的职务吗?撤销后将不再拥有相关权限。",
|
|
|
|
|
|
"撤销职务", // 标题默认红色
|
|
|
|
|
|
);
|
|
|
|
|
|
if (yes) {
|
|
|
|
|
|
// 用户点了「确定」
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ④ confirm 模式 — 自定义颜色
|
|
|
|
|
|
const leave = await window.chatDialog.confirm(
|
|
|
|
|
|
"确定要离开聊天室吗?",
|
|
|
|
|
|
"离开聊天室",
|
|
|
|
|
|
"#cc4444",
|
|
|
|
|
|
);
|
|
|
|
|
|
if (leave) {
|
|
|
|
|
|
leaveRoom();
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 在 Alpine.js 组件内使用
|
|
|
|
|
|
|
|
|
|
|
|
`userCardComponent` 组件已通过代理方法封装:
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// 在 Alpine.js 组件方法中,可以直接用 this.$alert / this.$confirm
|
|
|
|
|
|
await this.$alert("任命成功!", "任命成功 ✨", "#16a34a");
|
|
|
|
|
|
|
|
|
|
|
|
const ok = await this.$confirm("确定撤销职务?", "撤销确认");
|
|
|
|
|
|
if (ok) {
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这两个方法内部等价于 `window.chatDialog.alert()` / `window.chatDialog.confirm()`。
|
|
|
|
|
|
|
|
|
|
|
|
#### 颜色速查
|
|
|
|
|
|
|
|
|
|
|
|
| 场景 | 推荐颜色 |
|
|
|
|
|
|
| --------------- | ----------------------- |
|
|
|
|
|
|
| 成功 / 正向操作 | `#16a34a`(绿色) |
|
|
|
|
|
|
| 错误 / 警告 | `#cc4444`(红色) |
|
|
|
|
|
|
| 一般提示 | `#336699`(蓝色,默认) |
|
|
|
|
|
|
| 撤销 / 中性操作 | `#6b7280`(灰色) |
|
|
|
|
|
|
| 管理 / 特权操作 | `#7c3aed`(紫色) |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-03-01 12:15:18 +08:00
|
|
|
|
### 7.9 全局右下角 Toast 通知卡片 `window.chatToast` ⭐
|
|
|
|
|
|
|
|
|
|
|
|
> [!NOTE]
|
|
|
|
|
|
> `chatToast` 是固定在**右下角**的轻量通知卡片,适用于实时事件通知(奖励到账、好友动态等)。
|
|
|
|
|
|
> 与 `chatDialog` 不同,它不阻断操作流程,自动消失,可堆叠多条。
|
|
|
|
|
|
|
|
|
|
|
|
#### 文件位置
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
resources/views/chat/partials/toast-notification.blade.php ← Toast 组件 HTML + JS
|
|
|
|
|
|
resources/views/chat/frame.blade.php ← 已 @include,全页面可用
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### API 说明
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
window.chatToast.show({
|
|
|
|
|
|
title: "标题文字", // 必填
|
|
|
|
|
|
message: "正文内容", // 必填,支持 HTML
|
|
|
|
|
|
icon: "🪙", // 可选,左侧 Emoji,默认 💬
|
|
|
|
|
|
color: "#f59e0b", // 可选,强调色,默认 #336699
|
|
|
|
|
|
duration: 6000, // 可选,自动消失毫秒,0 = 不自动消失,默认 6000
|
|
|
|
|
|
action: {
|
|
|
|
|
|
// 可选,操作按钮
|
|
|
|
|
|
label: "操作文字",
|
|
|
|
|
|
onClick: () => {
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 通过消息字段自动触发(后端控制)
|
|
|
|
|
|
|
|
|
|
|
|
后端 broadcast 的消息中包含 `toast_notification` 字段,且接收方是当前用户时,前端脚本会自动弹出 Toast:
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
// AdminCommandController::reward() 示例
|
|
|
|
|
|
$msg['toast_notification'] = [
|
|
|
|
|
|
'title' => '🪙 奖励金币到账',
|
|
|
|
|
|
'message' => "<b>{$admin->username}</b> 向你发放了 <b>{$amount}</b> 枚金币!",
|
|
|
|
|
|
'icon' => '🪙',
|
|
|
|
|
|
'color' => '#f59e0b',
|
|
|
|
|
|
'duration' => 8000,
|
|
|
|
|
|
];
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用场景
|
|
|
|
|
|
|
|
|
|
|
|
| 场景 | 颜色 | 图标 |
|
|
|
|
|
|
| ---------------- | --------------- | ---- |
|
|
|
|
|
|
| 奖励金币到账 | `#f59e0b`(橙) | 🪙 |
|
|
|
|
|
|
| 好友动态通知 | `#6b7280`(灰) | 👥 |
|
|
|
|
|
|
| 礼物收到 | `#e11d48`(玫) | 🎁 |
|
|
|
|
|
|
| 系统提示(普通) | `#336699`(蓝) | 💬 |
|
|
|
|
|
|
| 等级晋升 | `#7c3aed`(紫) | 🌟 |
|
|
|
|
|
|
|
|
|
|
|
|
#### 原 `showFriendToast` 迁移说明
|
|
|
|
|
|
|
|
|
|
|
|
旧函数 `showFriendToast()` 已被 `window.chatToast.show()` 替代,好友删除通知已改用新 API。
|
|
|
|
|
|
新增功能只需调用 `window.chatToast.show()`,**勿新增** `showFriendToast` 调用。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 7.10 全局大卡片通知 `window.chatBanner` ⭐
|
重构:聊天室所有 alert() 改为 window.chatDialog.alert()
scripts.blade.php 全部 21 处原生 alert() 替换:
- 成功类 → chatDialog.alert(..., '提示', '#16a34a')
- 失败/错误类 → chatDialog.alert(..., '操作失败', '#cc4444')
- 网络异常类 → chatDialog.alert(..., '网络异常', '#cc4444')
- 连接断开/踢出 → chatDialog.alert(..., '连接警告', '#b45309')
- 一般提示 → chatDialog.alert(..., '提示', '#336699')
DEVELOPMENT.md 新增 §7.9 window.chatBanner 使用文档
2026-03-01 01:32:20 +08:00
|
|
|
|
|
|
|
|
|
|
> [!NOTE]
|
|
|
|
|
|
> `chatBanner` 是居中弹出的沉浸式大卡片通知组件,适用于好友通知、任命公告、系统广播等重要事件。与 `chatDialog` 不同,它**不阻断操作流程**,支持自动消失和自定义按钮。
|
|
|
|
|
|
|
|
|
|
|
|
#### 文件位置
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
resources/views/chat/partials/scripts.blade.php ← chatBanner 定义(window.chatBanner)
|
|
|
|
|
|
app/Events/BannerNotification.php ← 广播事件(后端推送)
|
|
|
|
|
|
app/Http/Controllers/Admin/BannerBroadcastController.php ← 管理员推送接口
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 前端 API
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
window.chatBanner.show(options); // 显示大卡片
|
|
|
|
|
|
window.chatBanner.close(id); // 关闭指定 banner
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### `show(options)` 参数说明
|
|
|
|
|
|
|
|
|
|
|
|
| 参数 | 类型 | 说明 | 默认值 |
|
|
|
|
|
|
| ------------ | ---------- | ------------------------------------------- | --------------------------------- |
|
|
|
|
|
|
| `id` | `string` | 可选,banner DOM ID,同 ID 自动覆盖防重叠 | `'chat-banner-default'` |
|
|
|
|
|
|
| `icon` | `string` | 顶部 Emoji 图标 | 无 |
|
|
|
|
|
|
| `title` | `string` | 小标题(显示为 `══ 标题 ══`) | 无 |
|
|
|
|
|
|
| `name` | `string` | 大名字/主角行(自动 HTML 转义) | 无 |
|
|
|
|
|
|
| `body` | `string` | 主内容,支持有限 HTML(`<b><span><br>` 等) | 无 |
|
|
|
|
|
|
| `sub` | `string` | 副说明(小字,支持有限 HTML) | 无 |
|
|
|
|
|
|
| `gradient` | `string[]` | 渐变色数组,如 `['#4f46e5', '#db2777']` | `['#4f46e5','#7c3aed','#db2777']` |
|
|
|
|
|
|
| `titleColor` | `string` | 小标题字体颜色 | `'#fde68a'` |
|
|
|
|
|
|
| `autoClose` | `number` | 自动关闭时间(ms),`0` = 不自动关闭 | `5000` |
|
|
|
|
|
|
| `buttons` | `Button[]` | 按钮列表(见下) | 无(无按钮时不可点击) |
|
|
|
|
|
|
|
|
|
|
|
|
`buttons` 数组元素:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
{
|
|
|
|
|
|
label: string, // 按钮文字
|
|
|
|
|
|
color: string, // 背景色(CSS 颜色值)
|
|
|
|
|
|
onClick: (btn: HTMLElement, close: () => void) => void, // 点击回调
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用示例
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// ① 简单通知(5秒自动消失)
|
|
|
|
|
|
window.chatBanner.show({
|
|
|
|
|
|
icon: "🎉💚🎉",
|
|
|
|
|
|
title: "好友通知",
|
|
|
|
|
|
name: "lkddi1",
|
|
|
|
|
|
body: "将你加为好友了!",
|
|
|
|
|
|
sub: '<strong style="color:#a7f3d0;">你们现在互为好友 🎊</strong>',
|
|
|
|
|
|
gradient: ["#065f46", "#059669", "#10b981"],
|
|
|
|
|
|
titleColor: "#a7f3d0",
|
|
|
|
|
|
autoClose: 5000,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ② 带操作按钮(不自动关闭)
|
|
|
|
|
|
window.chatBanner.show({
|
|
|
|
|
|
id: "friend-add-banner",
|
|
|
|
|
|
icon: "💚📩",
|
|
|
|
|
|
title: "好友申请",
|
|
|
|
|
|
name: "lkddi1",
|
|
|
|
|
|
body: "将你加为好友了!",
|
|
|
|
|
|
sub: "但你还没有回加对方为好友",
|
|
|
|
|
|
gradient: ["#1e3a5f", "#1d4ed8", "#0891b2"],
|
|
|
|
|
|
titleColor: "#bae6fd",
|
|
|
|
|
|
autoClose: 0, // 不自动关闭,等待用户操作
|
|
|
|
|
|
buttons: [
|
|
|
|
|
|
{
|
|
|
|
|
|
label: "➕ 回加好友",
|
|
|
|
|
|
color: "#10b981",
|
|
|
|
|
|
onClick: (btn, close) => {
|
|
|
|
|
|
btn.textContent = "处理中…";
|
|
|
|
|
|
// ... 调用 API ...
|
|
|
|
|
|
close(); // 完成后手动关闭
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: "稍后再说",
|
|
|
|
|
|
color: "rgba(255,255,255,0.15)",
|
|
|
|
|
|
onClick: (btn, close) => close(),
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ③ 手动关闭指定 banner
|
|
|
|
|
|
window.chatBanner.close("friend-add-banner");
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 后端推送(管理员)
|
|
|
|
|
|
|
|
|
|
|
|
通过 `BannerNotification` 事件向**单个用户**或**整个房间**广播大卡片:
|
|
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
|
use App\Events\BannerNotification;
|
|
|
|
|
|
|
|
|
|
|
|
// 推给单个用户(私有频道 user.{username})
|
|
|
|
|
|
broadcast(new BannerNotification(
|
|
|
|
|
|
target: 'user',
|
|
|
|
|
|
targetId: 'lkddi',
|
|
|
|
|
|
options: [
|
|
|
|
|
|
'icon' => '📢',
|
|
|
|
|
|
'title' => '系统通知',
|
|
|
|
|
|
'body' => '你有一条新消息',
|
|
|
|
|
|
'gradient' => ['#4f46e5', '#7c3aed'],
|
|
|
|
|
|
'autoClose' => 6000,
|
|
|
|
|
|
]
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
// 推给整个房间(Presence 频道 room.{id})
|
|
|
|
|
|
broadcast(new BannerNotification(
|
|
|
|
|
|
target: 'room',
|
|
|
|
|
|
targetId: 1,
|
|
|
|
|
|
options: [
|
|
|
|
|
|
'icon' => '🎊',
|
|
|
|
|
|
'title' => '活动公告',
|
|
|
|
|
|
'body' => '双倍积分活动已开始!',
|
|
|
|
|
|
'gradient' => ['#065f46', '#059669'],
|
|
|
|
|
|
'autoClose' => 8000,
|
|
|
|
|
|
]
|
|
|
|
|
|
));
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
也可通过管理员 HTTP 接口(仅超级管理员):
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /admin/banner/broadcast
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
"target": "room",
|
|
|
|
|
|
"target_id": 1,
|
|
|
|
|
|
"options": {
|
|
|
|
|
|
"icon": "📢",
|
|
|
|
|
|
"title": "公告",
|
|
|
|
|
|
"body": "服务器将于 10 分钟后重启",
|
|
|
|
|
|
"gradient": ["#374151", "#4b5563"],
|
|
|
|
|
|
"autoClose": 10000
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 渐变配色速查
|
|
|
|
|
|
|
|
|
|
|
|
| 场景 | 渐变数组 |
|
|
|
|
|
|
| ----------- | --------------------------------------------- |
|
|
|
|
|
|
| 好友 / 成功 | `['#065f46', '#059669', '#10b981']`(绿色) |
|
|
|
|
|
|
| 申请 / 信息 | `['#1e3a5f', '#1d4ed8', '#0891b2']`(蓝色) |
|
|
|
|
|
|
| 任命 / 荣耀 | `['#4f46e5', '#7c3aed', '#db2777']`(紫粉) |
|
|
|
|
|
|
| 撤销 / 中性 | `['#374151', '#4b5563', '#6b7280']`(灰色) |
|
|
|
|
|
|
| 警告 / 紧急 | `['#7f1d1d', '#991b1b', '#dc2626']`(红色) |
|
|
|
|
|
|
| 系统 / 特权 | `['#1e1b4b', '#312e81', '#4338ca']`(深蓝紫) |
|
|
|
|
|
|
|
|
|
|
|
|
#### 安全说明
|
|
|
|
|
|
|
|
|
|
|
|
> [!IMPORTANT]
|
|
|
|
|
|
>
|
|
|
|
|
|
> - **前端控制台调用** `window.chatBanner.show()` 只影响**用户自己的浏览器**,无法推送给他人。
|
|
|
|
|
|
> - **HTTP 推送接口** `POST /admin/banner/broadcast` 受三层中间件保护(`chat.auth` + `chat.has_position` + `chat.level:super`),普通用户调用返回 403。
|
|
|
|
|
|
> - **内容净化**:`body`、`sub`、`title` 字段经过 `strip_tags` 白名单处理(允许 `<b><strong><em><span><br>`),防止 XSS。
|
|
|
|
|
|
> - **按钮 action 白名单**:仅允许 `close | add_friend | remove_friend | link`,禁止任意 JS 注入。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-02-26 12:02:00 +08:00
|
|
|
|
## 八、常用命令速查
|
|
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
|
```
|
2026-02-28 14:14:20 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
> **参照原项目**:`/Users/pllx/Web/chat/hp0709/`(VBScript ASP 源码,仅作功能参考和备查,不做数据迁移)
|