Files
chatroom/DEVELOPMENT.md

40 KiB
Raw Permalink Blame History

ChatRoom 开发计划与注意事项

技术栈Laravel 12 · PHP 8.4 · Laravel Reverb (WebSocket) · Redis · MySQL 8.0 · Laravel Horizon 参照原项目/Users/pllx/Web/chat/hp0709VBScript ASP 聊天室,仅作功能参考,不做数据迁移) 目标域名http://chatroom.testHerd 自动配置)| https://chat.ay.lc(生产环境)


一、环境版本要求

组件 版本
PHP 8.4.5+
Laravel Framework v12.x
Laravel Reverb latestWebSocket 服务器)
Laravel Horizon v5Redis 队列可视化管理)
PHPUnit v11测试框架
Node.js 20.x LTS
MySQL 8.0+
Redis 7.x

二、代码规范(强制执行)

2.1 Laravel Pint 格式化

# 提交代码前必须运行,修复格式问题
vendor/bin/pint --dirty

# 检查格式问题(不修复)
vendor/bin/pint --test

# 格式化整个项目
vendor/bin/pint

2.2 PHP 8.4 类型系统(必须遵守)

// ✅ 正确:构造函数属性提升 (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 中配置。

// 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,
            'chat.site_owner' => \App\Http\Middleware\SiteOwnerOnly::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

2.4 中文注释规范(每个文件必须)

<?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 HashKey 为 "room:{房间ID}:users"
        $this->redis->hset("room:{$roomId}:users", $username, json_encode($userInfo));
    }
}

三、首次启动(必须先执行)

cd /Users/pllx/Web/Herd/chatroom

# 安装 PHP 依赖(已完成)
composer install

# 创建数据库(已完成)
mysql -u root -proot -e "CREATE DATABASE IF NOT EXISTS chatroom CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# 运行数据库迁移 + 基础数据填充
php artisan migrate --seed

# 安装前端依赖并构建(已完成)
npm install
npm run build

开发时运行的服务

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 主用户表(含 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 用户名黑名单
(新增) create_user_currency_logs_table UserCurrencyLog 积分流水(经验/金币/魅力变动记录)

五、推荐目录结构

app/
├── Events/                         # WebSocket 广播事件ShouldBroadcast
│   ├── MessageSent.php             # 消息发送(替代 NEWSAY.ASP
│   ├── UserJoined.php              # 用户进房(替代 INIT.ASP
│   ├── UserLeft.php                # 用户离开(替代 LEAVE.ASP
│   ├── UserKicked.php              # 踢人
│   ├── UserMuted.php               # 封口
│   ├── UserLevelUp.php             # 用户升级通知
│   ├── RoomTitleUpdated.php        # 房间标题更新
│   ├── ScreenCleared.php           # 管理员清屏
│   └── EffectBroadcast.php         # 特效广播(烟花/下雨等)
│
├── Services/                       # 业务逻辑服务层(纯 PHP不含 HTTP 逻辑)
│   ├── ChatStateService.php        # Redis 全局状态(替代 Application 对象)
│   ├── MessageFilterService.php    # 敏感词/HTML 过滤
│   ├── UserLevelService.php        # 等级权限判断(替代 getLevel()
│   ├── AiChatService.php           # AI 聊天服务多厂商适配OpenAI/DeepSeek 等)
│   ├── VipService.php              # VIP 开通/到期检查
│   ├── ShopService.php             # 商店购买 + 道具激活逻辑
│   └── UserCurrencyService.php     # ⭐ 积分统一操作服务(所有经验/金币/魅力变更必须经此)
│
├── Http/
│   ├── Controllers/
│   │   ├── AuthController.php      # DEFAULT.asp + CHECK.asp + CLOSE.ASP
│   │   ├── 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 # 聊天室内管理员快捷命令
│   │   └── Admin/
│   │       ├── DashboardController.php    # 后台首页总览
│   │       ├── SystemController.php       # VIEWSYS.ASP 系统参数配置
│   │       ├── UserManagerController.php  # 用户大盘管理
│   │       ├── RoomManagerController.php  # 房间大盘管理
│   │       ├── AutoactController.php      # 随机事件管理
│   │       ├── VipController.php          # VIP 等级 CRUD
│   │       ├── AiProviderController.php   # AI 厂商配置管理(含启用/禁用/设为默认)
│   │       ├── CurrencyStatsController.php # ⭐ 积分流水活动统计(每日来源产出分析)
│   │       └── SmtpController.php         # 邮件发信配置
│   │
│   ├── Middleware/
│   │   ├── ChatAuthenticate.php    # 聊天室登录验证(检查 Session
│   │   ├── LevelRequired.php       # 等级权限中间件chat.level:super
│   │   └── SiteOwnerOnly.php       # 超级站长专属chat.site_owner
│   │
│   └── Requests/                   # Form Request 验证
│       ├── LoginRequest.php
│       ├── SendMessageRequest.php
│       └── CreateRoomRequest.php
│
├── Models/
│   ├── 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
│   ├── UserCurrencyLog.php # ⭐ 积分流水记录
│   └── System/
│       └── PlatformSmtpAccount.php
│
├── Enums/
│   └── CurrencySource.php          # ⭐ 积分来源枚举auto_save/fishing_gain/admin_adjust 等)
│
└── Jobs/
    └── SaveMessageJob.php          # 异步将消息持久化到 MySQL

bootstrap/
└── app.php                         # ⚠ Laravel 12中间件/路由在此配置(无 Kernel.php

routes/
├── web.php                         # 所有 HTTP 路由
├── api.php                         # API 路由(验证码等)
└── channels.php                    # WebSocket Presence Channel 鉴权

六、具体开发任务计划

图例[x] 已完成 · [/] 进行中 · [ ] 待开发


第一阶段:数据库层

  • 修改默认 users 迁移username/jjb/exp/sex/headface/user_level/email 等)
  • 创建 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 迁移
  • 创建 audit_logs 迁移
  • 创建 guestbooks 迁移
  • 创建 friend_calls 迁移
  • 创建 friend_requests 迁移
  • 创建 actions 迁移
  • 创建 admin_logs 迁移
  • 创建 user_items 迁移(封口令道具)
  • 创建 scroll_ads 迁移
  • 创建 marriages 迁移
  • 创建 ip_logs 迁移
  • 创建 room_descriptions 迁移
  • 创建 autoacts 迁移(随机事件/机器人自动发言)
  • 创建 gifts 迁移礼物定义emoji/cost/charm
  • 创建 shop_items 迁移(商店商品:特效卡/改名卡)
  • 创建 user_purchases 迁移(购买记录)
  • 创建 vip_levels 迁移VIP 等级:倍率/进入模板/价格)
  • 创建 ai_provider_configs 迁移AI 服务商配置)
  • 创建 ai_usage_logs 迁移AI 用量日志)
  • 创建 username_blacklist 迁移(用户名黑名单)
  • 创建所有 Model 文件(含 $fillable$casts、关联关系、中文 DocBlock
  • 创建 DatabaseSeeder(默认房间「公共大厅」、系统参数)
  • 运行 php artisan migrate --seed 验证建表成功

第二阶段Auth 认证

  • AuthController::login() — 登录(含 IP 封锁检查 + 密码 bcrypt
  • AuthController::logout() — 退出并清理 Redis 用户状态
  • ChatAuthenticate 中间件 — 检查 Session 是否有效
  • LevelRequired 中间件 — 权限等级检查(chat.level:super
  • SiteOwnerOnly 中间件 — 超级站长专属(最高权限区块)
  • 登录 Blade 视图 resources/views/index.blade.php
  • 测试:登录 → 退出完整流程

第三阶段Redis 状态层

  • ChatStateService — 完整实现userJoin/userLeave/getRoomUsers/pushMessage/nextMsgId/withLock/getSysParam
  • MessageFilterService — 敏感词替换 + HTML 过滤
  • UserLevelService — 等级权限判断

第四阶段WebSocket 广播

  • MessageSent Event广播到 room.{id} Presence Channel
  • UserJoined Event — 用户进入广播
  • UserLeft Event — 用户离开广播
  • UserKicked Event — 踢人广播(私有频道)
  • UserMuted Event — 封口广播
  • UserLevelUp Event — 升级通知广播
  • RoomTitleUpdated Event — 标题更新广播
  • ScreenCleared Event — 管理员清屏广播
  • EffectBroadcast Event — 特效广播(烟花/下雨/闪电/彩带)
  • routes/channels.php — Presence Channel 鉴权
  • resources/js/chat.js — Laravel Echo 接入
  • Reverb 连通性测试通过

第五阶段:聊天核心

  • ChatController::init() — 进入房间(历史消息 + 在线用户)
  • ChatController::send() — 发言(过滤 → Redis → SaveMessageJob → 广播)
  • ChatController::heartbeat() — 挂机心跳限流每分钟6次
  • ChatController::leave() — 离开房间
  • ChatController::headfaceList() — 头像列表
  • ChatController::changeAvatar() — 修改头像
  • ChatController::setAnnouncement() — 设置房间公告
  • ChatController::sendFlower() — 送花/礼物(扣金币 + 增魅力)
  • SaveMessageJob — 异步消息持久化到 MySQL
  • 聊天主界面 Blade 视图 resources/views/chat/frame.blade.php
  • 测试:进房 → 发言 → 实时收到消息

第六阶段:房间管理

  • RoomController::index() — 大厅房间列表
  • RoomController::store() — 创建房间
  • RoomController::update() — 修改设置
  • RoomController::destroy() — 删除/解散房间
  • RoomController::transfer() — 转让房主
  • 对应 Blade 视图(大厅列表、引导页)

第七阶段:用户管理

  • UserController::show() — 用户名片/资料页
  • UserController::updateProfile() — 修改个人资料
  • UserController::changePassword() — 改密码
  • UserController::kick() — 踢人
  • UserController::mute() — 封口
  • UserController::ban() — 封号
  • UserController::banIp() — 封 IP
  • 邮箱验证码发送(Api\VerificationController

第八阶段:管理后台

  • Admin\DashboardController — 后台首页总览
  • Admin\SystemController — 系统参数配置
  • Admin\SmtpController — 邮件发信配置
  • Admin\UserManagerController — 用户大盘管理(编辑/删除)
  • Admin\RoomManagerController — 房间大盘管理
  • Admin\AutoactController — 随机事件 CRUD 管理
  • Admin\VipController — VIP 等级 CRUD 管理
  • Admin\AiProviderController — AI 厂商配置管理(含启用/禁用/设为默认)
  • Horizon 面板 /horizon(队列监控)
  • 对应 Blade 视图

第九阶段:外围功能

  • LeaderboardController — 风云排行榜(经验/金币/魅力榜)
  • GuestbookController — 留言板(发表/删除)

第十阶段:扩展玩法

  • 钓鱼小游戏 FishingController — 复刻原版 diaoyu/ 功能(投竿/收竿/随机收益)
  • AI 聊天机器人 ChatBotController + AiChatService多厂商OpenAI/DeepSeek/Gemini 等)
  • 礼物/送花系统Gift 模型 + ChatController::sendFlower()(金币消耗 + 魅力增量)
  • 商店系统 ShopController + ShopService(购买特效卡/改名卡,ShopItem + UserPurchase
  • VIP 会员系统 VipService + VipLevel(进入/离开专属模板、倍率加成)
  • 管理员快捷命令 AdminCommandController(警告/踢人/封口/冻结/清屏/全服广播/特效)
  • 特效系统 EffectBroadcast Event烟花/下雨/闪电/彩带,支持单次卡/周卡)

第十一阶段:积分流水审计系统

  • App\Enums\CurrencySource — 所有来源枚举(可扩展)
  • App\Models\UserCurrencyLog — 流水 Model含查询 Scope
  • App\Services\UserCurrencyService — 统一积分变更服务(所有经验/金币/魅力修改必须走此服务
  • 接入 AutoSaveExpauto_save 来源)
  • 接入 FishingControllerfishing_cost / fishing_gain 来源)
  • 接入 ChatController::init() 新人礼包newbie_bonus 来源)
  • 接入 Admin\UserManagerControlleradmin_adjust 来源)
  • LeaderboardController::todayIndex() — 今日风云榜独立页(/leaderboard/today
  • LeaderboardController::myLogs() — 用户个人流水日志(/my/currency-logs
  • Admin\CurrencyStatsController — 后台积分活动统计(/admin/currency-stats
  • 导航栏新增「📅 今日榜」按钮,后台侧边栏新增「📈 积分流水统计」入口

🔲 待完善事项

  • 用户名禁词管理(已完成)
    • username_blacklist 表新增 typetemp/permanent)和 reason 字段
    • temp = 改名后旧名30天保留permanent = 管理员永久禁用词
    • UsernameBlacklist::isBlocked() 精确匹配两种类型,isReserved() 保留兼容
    • 注册(AuthController)和改名(ShopService::useRenameCard)均经过检测
    • 后台「🚫 禁用用户名」页面(站长专用):
      • 单个添加 / 批量添加(多行/逗号/空格分隔,自动去重)
      • 关键词搜索 + 分页列表
      • 行内编辑原因备注、删除
    • 已预置:系统保留词、国家领导人名称、侮辱性词汇
  • 滚动公告管理(后台 CRUDScrollAd 模型已建,界面待完成)
  • 好友系统(已完成)
    • 好友面板独立 partialresources/views/chat/partials/friend-panel.blade.php
    • 工具栏「好友」按钮,点击弹出浮窗
    • 「我关注的好友」列表:显示互相状态(💚互相/👤单向)、添加时间、删除按钮
    • 「对方已加我」列表:显示对方添加时间、 回加按钮
    • 搜索栏:输入用户名 Enter/点击直接添加好友
    • 所有操作调用与双击用户卡片相同的后端接口
    • FriendController::index() 返回 friends(含 sub_time)和 pending 两个列表
  • 婚姻系统Marriage 模型已建,界面待完成)
  • 单元测试(核心 Service 层测试覆盖率)
  • Flash 游戏替代game/pig/ 等 .swf 文件,考虑 HTML5/Canvas 重写)

七、注意事项

7.1 密码策略

  • 所有用户密码使用 bcryptHash::make()
  • 登录时使用 Hash::check() 验证

7.2 实时推送机制

聊天室所有状态变更均通过 Reverb WebSocket 广播,无需轮询刷新。

Note

历史上基于 <meta http-equiv=refresh> 的 6秒刷新方案已被完全取代任何涉及聊天室状态更新的功能都应通过 Event → Broadcasting 实现,禁止在实现中引入轮询逻辑

7.3 Redis 状态层设计

数据 Redis Key 格式 说明
在线用户列表 room:{id}:users Hash 值为用户信息 JSON
消息环形缓冲 room:{id}:messages List 最多保留 200 条
房间基本信息 room:{id}:info String JSON 快照
并发锁 Cache::lock("key", 10) 使用 ->block(5, fn) 实现

7.4 Flash 游戏(暂不处理)

game/pig/Gupiao/ 等目录内的 .swf Flash 文件现代浏览器已不支持,本期不做转换

7.5 AI 机器人多厂商支持

AiChatService 支持以下 Provider通过 ai_provider_configs 后台配置切换:

Provider 推荐模型 说明
openai gpt-4o-mini OpenAI 官方 API
deepseek deepseek-chat 国产低成本,兼容 OpenAI 接口
gemini gemini-1.5-flash Google Gemini

7.6 商店商品类型说明

type slug 示例 说明
instant once_fireworks 单次特效卡(即时生效)
duration week_fireworks 周卡7天内持续生效
rename rename 改名卡(改变显示名称)

7.7 积分流水系统 — 开发者使用指南

Important

铁律:所有涉及修改用户 exp_num(经验)、jjb(金币)、meili(魅力)的操作, 必须通过 UserCurrencyService::change() 执行,禁止直接操作 $user->increment()User::update()

8.1 注入服务

use App\Enums\CurrencySource;
use App\Services\UserCurrencyService;

public function __construct(
    private readonly UserCurrencyService $currencyService,
) {}

8.2 单次变更

// 给用户增加经验(正数=增加,负数=扣除)
$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 批量变更(自动存点场景)

// 对多个用户批量发放,内部逐条原子执行
$this->currencyService->batchChange(
    users: $onlineUsers,     // Collection<User>
    currency: 'exp',
    amount: 1,
    source: CurrencySource::AUTO_SAVE,
    remark: '定时存点',
);

8.4 添加新的积分来源(扩展 Enum

App\Enums\CurrencySource 枚举中新增一个常量即可,无需修改数据库

// 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

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

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

使用示例

// ① 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 组件已通过代理方法封装:

// 在 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(紫色)

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 说明

window.chatToast.show({
    title: "标题文字", // 必填
    message: "正文内容", // 必填,支持 HTML
    icon: "🪙", // 可选,左侧 Emoji默认 💬
    color: "#f59e0b", // 可选,强调色,默认 #336699
    duration: 6000, // 可选自动消失毫秒0 = 不自动消失,默认 6000
    action: {
        // 可选,操作按钮
        label: "操作文字",
        onClick: () => {
            /* ... */
        },
    },
});

通过消息字段自动触发(后端控制)

后端 broadcast 的消息中包含 toast_notification 字段,且接收方是当前用户时,前端脚本会自动弹出 Toast

// 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

Note

chatBanner 是居中弹出的沉浸式大卡片通知组件,适用于好友通知、任命公告、系统广播等重要事件。与 chatDialog 不同,它不阻断操作流程,支持自动消失和自定义按钮。

文件位置

resources/views/chat/partials/scripts.blade.php   ← chatBanner 定义window.chatBanner
app/Events/BannerNotification.php                 ← 广播事件(后端推送)
app/Http/Controllers/Admin/BannerBroadcastController.php  ← 管理员推送接口

前端 API

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 自动关闭时间ms0 = 不自动关闭 5000
buttons Button[] 按钮列表(见下) 无(无按钮时不可点击)

buttons 数组元素:

{
  label:   string,                          // 按钮文字
  color:   string,                          // 背景色CSS 颜色值)
  onClick: (btn: HTMLElement, close: () => void) => void,  // 点击回调
}

使用示例

// ① 简单通知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 事件向单个用户整个房间广播大卡片:

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。
  • 内容净化bodysubtitle 字段经过 strip_tags 白名单处理(允许 <b><strong><em><span><br>),防止 XSS。
  • 按钮 action 白名单:仅允许 close | add_friend | remove_friend | link,禁止任意 JS 注入。

八、常用命令速查

# 创建 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

参照原项目/Users/pllx/Web/chat/hp0709/VBScript ASP 源码,仅作功能参考和备查,不做数据迁移)