From 709e0d4975d536def942ac0ea942e9e54c17e8ea Mon Sep 17 00:00:00 2001 From: lkddi Date: Fri, 27 Feb 2026 14:14:35 +0800 Subject: [PATCH] =?UTF-8?q?Feat:=20=E5=AE=9E=E7=8E=B0=E5=85=A8=E5=B1=8F?= =?UTF-8?q?=E7=89=B9=E6=95=88=E7=B3=BB=E7=BB=9F=EF=BC=88=E7=83=9F=E8=8A=B1?= =?UTF-8?q?/=E4=B8=8B=E9=9B=A8/=E9=9B=B7=E7=94=B5=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=E4=B8=80=E9=94=AE=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E5=85=A8=E6=88=BF=E9=97=B4=E5=B9=BF=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Events/EffectBroadcast.php | 68 +++++++++ .../Controllers/AdminCommandController.php | 32 ++++ .../0001_01_01_000000_create_users_table.php | 106 ------------- .../0001_01_01_000001_create_cache_table.php | 35 ----- .../0001_01_01_000002_create_jobs_table.php | 57 ------- .../2026_02_26_040510_create_rooms_table.php | 42 ------ ...6_02_26_040521_create_audit_logs_table.php | 30 ---- ...6_02_26_040521_create_guestbooks_table.php | 37 ----- ...026_02_26_040521_create_ip_locks_table.php | 30 ---- ...026_02_26_040521_create_messages_table.php | 35 ----- ...6_02_26_040521_create_sys_params_table.php | 30 ---- ...2026_02_26_040522_create_actions_table.php | 32 ---- ...6_02_26_040522_create_admin_logs_table.php | 48 ------ ...02_26_040522_create_friend_calls_table.php | 32 ---- ...26_040522_create_friend_requests_table.php | 30 ---- ...2026_02_26_040523_create_ip_logs_table.php | 30 ---- ...26_02_26_040523_create_marriages_table.php | 33 ---- ..._040523_create_room_descriptions_table.php | 29 ---- ...6_02_26_040523_create_scroll_ads_table.php | 30 ---- ...6_02_26_040523_create_user_items_table.php | 32 ---- ...26_091905_add_visit_num_to_rooms_table.php | 34 ----- ...026_02_26_092923_create_sysparam_table.php | 71 --------- ...2026_02_26_094113_create_autoact_table.php | 73 --------- ...095610_add_announcement_to_rooms_table.php | 33 ---- ...hange_s_color_to_string_in_users_table.php | 33 ---- ...26_124547_add_missing_sysparam_records.php | 81 ---------- ...02_26_124729_update_level_system_to_99.php | 95 ------------ ...26_125933_add_fishing_sysparam_records.php | 64 -------- ...6_02_26_132014_create_vip_levels_table.php | 129 ---------------- ...132015_add_vip_level_id_to_users_table.php | 38 ----- .../2026_02_26_132600_create_ai_tables.php | 70 --------- ...26_143645_add_warn_freeze_level_params.php | 49 ------ ...26_144806_add_question_answer_to_users.php | 51 ------- ...2_26_151000_lowercase_usersf_extension.php | 33 ---- ...003842_seed_leaderboard_limit_sysparam.php | 42 ------ ...004100_seed_jjb_per_heartbeat_sysparam.php | 50 ------- .../2026_02_27_005300_create_gifts_table.php | 57 ------- .../2026_02_27_011200_seed_charm_sysparam.php | 50 ------- public/js/effects/effect-manager.js | 91 +++++++++++ public/js/effects/fireworks.js | 141 ++++++++++++++++++ public/js/effects/lightning.js | 126 ++++++++++++++++ public/js/effects/rain.js | 108 ++++++++++++++ resources/js/chat.js | 5 + resources/views/chat/frame.blade.php | 7 + .../views/chat/partials/input-bar.blade.php | 10 ++ .../views/chat/partials/scripts.blade.php | 32 ++++ routes/web.php | 1 + 47 files changed, 621 insertions(+), 1751 deletions(-) create mode 100644 app/Events/EffectBroadcast.php delete mode 100644 database/migrations/0001_01_01_000000_create_users_table.php delete mode 100644 database/migrations/0001_01_01_000001_create_cache_table.php delete mode 100644 database/migrations/0001_01_01_000002_create_jobs_table.php delete mode 100644 database/migrations/2026_02_26_040510_create_rooms_table.php delete mode 100644 database/migrations/2026_02_26_040521_create_audit_logs_table.php delete mode 100644 database/migrations/2026_02_26_040521_create_guestbooks_table.php delete mode 100644 database/migrations/2026_02_26_040521_create_ip_locks_table.php delete mode 100644 database/migrations/2026_02_26_040521_create_messages_table.php delete mode 100644 database/migrations/2026_02_26_040521_create_sys_params_table.php delete mode 100644 database/migrations/2026_02_26_040522_create_actions_table.php delete mode 100644 database/migrations/2026_02_26_040522_create_admin_logs_table.php delete mode 100644 database/migrations/2026_02_26_040522_create_friend_calls_table.php delete mode 100644 database/migrations/2026_02_26_040522_create_friend_requests_table.php delete mode 100644 database/migrations/2026_02_26_040523_create_ip_logs_table.php delete mode 100644 database/migrations/2026_02_26_040523_create_marriages_table.php delete mode 100644 database/migrations/2026_02_26_040523_create_room_descriptions_table.php delete mode 100644 database/migrations/2026_02_26_040523_create_scroll_ads_table.php delete mode 100644 database/migrations/2026_02_26_040523_create_user_items_table.php delete mode 100644 database/migrations/2026_02_26_091905_add_visit_num_to_rooms_table.php delete mode 100644 database/migrations/2026_02_26_092923_create_sysparam_table.php delete mode 100644 database/migrations/2026_02_26_094113_create_autoact_table.php delete mode 100644 database/migrations/2026_02_26_095610_add_announcement_to_rooms_table.php delete mode 100644 database/migrations/2026_02_26_123935_change_s_color_to_string_in_users_table.php delete mode 100644 database/migrations/2026_02_26_124547_add_missing_sysparam_records.php delete mode 100644 database/migrations/2026_02_26_124729_update_level_system_to_99.php delete mode 100644 database/migrations/2026_02_26_125933_add_fishing_sysparam_records.php delete mode 100644 database/migrations/2026_02_26_132014_create_vip_levels_table.php delete mode 100644 database/migrations/2026_02_26_132015_add_vip_level_id_to_users_table.php delete mode 100644 database/migrations/2026_02_26_132600_create_ai_tables.php delete mode 100644 database/migrations/2026_02_26_143645_add_warn_freeze_level_params.php delete mode 100644 database/migrations/2026_02_26_144806_add_question_answer_to_users.php delete mode 100644 database/migrations/2026_02_26_151000_lowercase_usersf_extension.php delete mode 100644 database/migrations/2026_02_27_003842_seed_leaderboard_limit_sysparam.php delete mode 100644 database/migrations/2026_02_27_004100_seed_jjb_per_heartbeat_sysparam.php delete mode 100644 database/migrations/2026_02_27_005300_create_gifts_table.php delete mode 100644 database/migrations/2026_02_27_011200_seed_charm_sysparam.php create mode 100644 public/js/effects/effect-manager.js create mode 100644 public/js/effects/fireworks.js create mode 100644 public/js/effects/lightning.js create mode 100644 public/js/effects/rain.js diff --git a/app/Events/EffectBroadcast.php b/app/Events/EffectBroadcast.php new file mode 100644 index 0000000..567aede --- /dev/null +++ b/app/Events/EffectBroadcast.php @@ -0,0 +1,68 @@ + + */ + public function broadcastOn(): array + { + return [ + new PresenceChannel('room.' . $this->roomId), + ]; + } + + /** + * 广播数据:特效类型和操作者 + * + * @return array + */ + public function broadcastWith(): array + { + return [ + 'type' => $this->type, + 'operator' => $this->operator, + ]; + } +} diff --git a/app/Http/Controllers/AdminCommandController.php b/app/Http/Controllers/AdminCommandController.php index a69329b..c9285d1 100644 --- a/app/Http/Controllers/AdminCommandController.php +++ b/app/Http/Controllers/AdminCommandController.php @@ -373,6 +373,38 @@ class AdminCommandController extends Controller return response()->json(['status' => 'success', 'message' => '已执行全员清屏']); } + /** + * 管理员触发全屏特效(烟花/下雨/雷电) + * + * 向房间内所有用户广播 EffectBroadcast 事件,前端收到后播放对应 Canvas 动画。 + * 仅 superlevel 等级管理员可触发。 + * + * @param Request $request 请求对象,需包含 room_id, type + * @return JsonResponse 操作结果 + */ + public function effect(Request $request): JsonResponse + { + $request->validate([ + 'room_id' => 'required|integer', + 'type' => 'required|in:fireworks,rain,lightning', + ]); + + $admin = Auth::user(); + $roomId = $request->input('room_id'); + $type = $request->input('type'); + $superLevel = (int) Sysparam::getValue('superlevel', '100'); + + // 仅 superlevel 等级可触发特效 + if ($admin->user_level < $superLevel) { + return response()->json(['status' => 'error', 'message' => '仅站长可触发特效'], 403); + } + + // 广播特效事件给房间内所有在线用户 + broadcast(new \App\Events\EffectBroadcast($roomId, $type, $admin->username)); + + return response()->json(['status' => 'success', 'message' => "已触发特效:{$type}"]); + } + /** * 权限检查:管理员是否可对目标用户执行指定操作 * diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php deleted file mode 100644 index 97eeea8..0000000 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ /dev/null @@ -1,106 +0,0 @@ -id(); - $table->string('username', 50)->unique()->comment('用户名'); - $table->string('password')->comment('密码 (MD5 or Bcrypt)'); - $table->string('email', 250)->nullable()->comment('邮箱'); - $table->tinyInteger('sex')->default(0)->comment('性别 (0保密 1男 2女)'); - $table->tinyInteger('user_level')->default(1)->comment('用户等级'); - $table->dateTime('log_time')->nullable()->comment('登录时间'); - $table->integer('visit_num')->default(0)->comment('访问次数'); - $table->dateTime('in_time')->nullable()->comment('进房时间'); - $table->tinyInteger('out_info')->default(0)->comment('退出信息'); - $table->dateTime('out_time')->nullable()->comment('退出时间'); - $table->integer('exp_num')->default(0)->index()->comment('经验值'); - $table->tinyInteger('f_size')->nullable()->comment('字体大小'); - $table->tinyInteger('l_height')->nullable()->comment('行高'); - $table->tinyInteger('n_color')->nullable()->comment('名称颜色'); - $table->integer('s_color')->nullable()->comment('发言颜色'); - $table->string('remand', 250)->nullable()->comment('密码提示问题'); - $table->string('answer', 250)->nullable()->comment('密码提示答案'); - $table->string('bgcolor', 50)->nullable()->comment('背景颜色'); - $table->string('temppass', 20)->nullable()->comment('临时密码'); - $table->string('oicq', 30)->nullable()->comment('QQ号'); - $table->tinyInteger('saved')->nullable()->comment('是否保存'); - $table->string('first_ip', 50)->nullable()->comment('首次IP'); - $table->string('last_ip', 50)->nullable()->comment('最后IP'); - $table->string('aihaos', 250)->nullable()->comment('爱好'); - $table->string('friends', 250)->nullable()->comment('好友列表'); - $table->integer('headface')->nullable()->comment('头像'); - $table->integer('room_id')->default(0)->index()->comment('所在房间'); - $table->tinyInteger('auto_update')->nullable()->comment('自动刷新'); - $table->string('ppass', 50)->nullable()->comment('二级密码'); - $table->integer('jjb')->default(0)->comment('交友币/金币'); - $table->string('love', 50)->nullable()->comment('伴侣'); - $table->string('gzdw', 50)->nullable()->comment('工作单位'); - $table->integer('photo')->nullable()->comment('照片 (对应原表中文列名照片)'); - $table->integer('hj')->default(0)->comment('呼叫状态'); - $table->string('djname', 50)->nullable()->comment('等级名称'); - $table->string('usersf', 50)->nullable()->comment('用户身份'); - $table->integer('yh')->nullable()->comment('隐身状态'); - $table->text('userpassword')->nullable()->comment('备用密码'); - $table->string('huiyuan', 50)->nullable()->comment('会员组别'); - $table->dateTime('hy_time')->nullable()->comment('会员到期时间'); - $table->string('tuijian', 50)->nullable()->comment('推荐人'); - $table->string('tuijian_ip', 50)->nullable()->comment('推荐IP'); - $table->string('xiaohai', 50)->nullable()->comment('小孩'); - $table->string('qingren', 50)->nullable()->comment('情人'); - $table->string('zhufang', 50)->nullable()->comment('住房'); - $table->string('zuoqiimg', 50)->nullable()->comment('坐骑图片'); - $table->string('zuoqi', 50)->nullable()->comment('坐骑名称'); - $table->integer('guanli')->nullable()->comment('管理员标记'); - $table->integer('meili')->nullable()->comment('魅力值'); - $table->integer('teshu')->nullable()->comment('特殊权限'); - $table->dateTime('xr_time')->nullable()->comment('仙人时间'); - $table->dateTime('yx_time')->nullable()->comment('英雄时间'); - $table->integer('q1')->nullable(); - $table->integer('q2')->nullable(); - $table->integer('q3')->nullable(); - $table->string('hua', 255)->nullable()->comment('鲜花'); - $table->dateTime('sj')->nullable()->comment('注册/更新时间'); - $table->string('pig', 255)->nullable()->comment('宠物'); - $table->text('qianming')->nullable()->comment('个性签名'); - $table->dateTime('q3_time')->nullable(); - - $table->rememberToken(); - $table->timestamps(); - }); - - Schema::create('password_reset_tokens', function (Blueprint $table) { - $table->string('email')->primary(); - $table->string('token'); - $table->timestamp('created_at')->nullable(); - }); - - Schema::create('sessions', function (Blueprint $table) { - $table->string('id')->primary(); - $table->foreignId('user_id')->nullable()->index(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->longText('payload'); - $table->integer('last_activity')->index(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('users'); - Schema::dropIfExists('password_reset_tokens'); - Schema::dropIfExists('sessions'); - } -}; diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php deleted file mode 100644 index ed758bd..0000000 --- a/database/migrations/0001_01_01_000001_create_cache_table.php +++ /dev/null @@ -1,35 +0,0 @@ -string('key')->primary(); - $table->mediumText('value'); - $table->integer('expiration')->index(); - }); - - Schema::create('cache_locks', function (Blueprint $table) { - $table->string('key')->primary(); - $table->string('owner'); - $table->integer('expiration')->index(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('cache'); - Schema::dropIfExists('cache_locks'); - } -}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php deleted file mode 100644 index 425e705..0000000 --- a/database/migrations/0001_01_01_000002_create_jobs_table.php +++ /dev/null @@ -1,57 +0,0 @@ -id(); - $table->string('queue')->index(); - $table->longText('payload'); - $table->unsignedTinyInteger('attempts'); - $table->unsignedInteger('reserved_at')->nullable(); - $table->unsignedInteger('available_at'); - $table->unsignedInteger('created_at'); - }); - - Schema::create('job_batches', function (Blueprint $table) { - $table->string('id')->primary(); - $table->string('name'); - $table->integer('total_jobs'); - $table->integer('pending_jobs'); - $table->integer('failed_jobs'); - $table->longText('failed_job_ids'); - $table->mediumText('options')->nullable(); - $table->integer('cancelled_at')->nullable(); - $table->integer('created_at'); - $table->integer('finished_at')->nullable(); - }); - - Schema::create('failed_jobs', function (Blueprint $table) { - $table->id(); - $table->string('uuid')->unique(); - $table->text('connection'); - $table->text('queue'); - $table->longText('payload'); - $table->longText('exception'); - $table->timestamp('failed_at')->useCurrent(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('jobs'); - Schema::dropIfExists('job_batches'); - Schema::dropIfExists('failed_jobs'); - } -}; diff --git a/database/migrations/2026_02_26_040510_create_rooms_table.php b/database/migrations/2026_02_26_040510_create_rooms_table.php deleted file mode 100644 index f8b96e6..0000000 --- a/database/migrations/2026_02_26_040510_create_rooms_table.php +++ /dev/null @@ -1,42 +0,0 @@ -id(); - $table->string('room_name', 10)->unique()->comment('房间标识/名称'); - $table->string('room_auto', 10)->nullable()->comment('房间别名/自动属性'); - $table->string('room_owner', 10)->nullable()->comment('房主'); - $table->string('room_des', 250)->nullable()->comment('房间描述'); - $table->string('room_top', 100)->nullable()->comment('置顶信息'); - $table->string('room_title', 250)->nullable()->comment('房间标题'); - $table->tinyInteger('room_keep')->default(0)->comment('是否保留'); - $table->dateTime('room_time')->nullable()->comment('建立/最后时间'); - $table->tinyInteger('room_tt')->default(0)->comment('相关开关'); - $table->tinyInteger('room_html')->default(0)->comment('是否允许HTML'); - $table->integer('room_exp')->default(0)->comment('所需经验'); - $table->dateTime('build_time')->nullable()->comment('建立时间'); - $table->tinyInteger('permit_level')->default(1)->comment('允许进入的最低等级'); - $table->tinyInteger('door_open')->default(1)->comment('大门是否开启 (1开 0关)'); - $table->integer('ooooo')->nullable()->comment('未知/扩展属性o5'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('rooms'); - } -}; diff --git a/database/migrations/2026_02_26_040521_create_audit_logs_table.php b/database/migrations/2026_02_26_040521_create_audit_logs_table.php deleted file mode 100644 index 6014241..0000000 --- a/database/migrations/2026_02_26_040521_create_audit_logs_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->dateTime('occ_time')->nullable()->index()->comment('发生时间'); - $table->text('occ_env')->nullable()->comment('发生环境/详情'); - $table->tinyInteger('stype')->default(0)->comment('类别标识'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('audit_logs'); - } -}; diff --git a/database/migrations/2026_02_26_040521_create_guestbooks_table.php b/database/migrations/2026_02_26_040521_create_guestbooks_table.php deleted file mode 100644 index 45435d0..0000000 --- a/database/migrations/2026_02_26_040521_create_guestbooks_table.php +++ /dev/null @@ -1,37 +0,0 @@ -id(); - $table->string('who', 50)->index()->comment('留言人'); - $table->string('towho', 50)->nullable()->index()->comment('给谁留言'); - $table->tinyInteger('secret')->default(0)->comment('是否悄悄话 (1是 0否)'); - $table->string('ip', 50)->nullable()->comment('留言者IP'); - $table->string('email', 250)->nullable()->comment('留言者邮箱'); - $table->string('web', 250)->nullable()->comment('留言者网站'); - $table->string('addr', 250)->nullable()->comment('留言者地址'); - $table->dateTime('post_time')->nullable()->index()->comment('留言时间'); - $table->string('text_title', 250)->nullable()->comment('留言标题'); - $table->text('text_body')->nullable()->comment('留言内容'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('guestbooks'); - } -}; diff --git a/database/migrations/2026_02_26_040521_create_ip_locks_table.php b/database/migrations/2026_02_26_040521_create_ip_locks_table.php deleted file mode 100644 index 2aa3677..0000000 --- a/database/migrations/2026_02_26_040521_create_ip_locks_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->string('ip', 20)->unique()->comment('封锁的IP'); - $table->dateTime('end_time')->nullable()->comment('封禁结束时间'); - $table->tinyInteger('act_level')->unsigned()->default(1)->comment('封禁级别'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('ip_locks'); - } -}; diff --git a/database/migrations/2026_02_26_040521_create_messages_table.php b/database/migrations/2026_02_26_040521_create_messages_table.php deleted file mode 100644 index afecdea..0000000 --- a/database/migrations/2026_02_26_040521_create_messages_table.php +++ /dev/null @@ -1,35 +0,0 @@ -id(); - $table->integer('room_id')->index()->comment('房间ID'); - $table->string('from_user', 50)->index()->comment('发送者'); - $table->string('to_user', 50)->nullable()->index()->comment('接收者'); - $table->text('content')->comment('消息内容'); - $table->tinyInteger('is_secret')->default(0)->comment('是否私聊'); - $table->string('font_color', 50)->nullable()->comment('字体颜色'); - $table->string('action', 50)->nullable()->comment('动作'); - $table->dateTime('sent_at')->useCurrent()->index()->comment('发送时间'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('messages'); - } -}; diff --git a/database/migrations/2026_02_26_040521_create_sys_params_table.php b/database/migrations/2026_02_26_040521_create_sys_params_table.php deleted file mode 100644 index db33cbf..0000000 --- a/database/migrations/2026_02_26_040521_create_sys_params_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->string('alias', 50)->unique()->comment('参数别名'); - $table->text('guidetxt')->nullable()->comment('参数说明'); - $table->text('body')->nullable()->comment('参数值'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('sys_params'); - } -}; diff --git a/database/migrations/2026_02_26_040522_create_actions_table.php b/database/migrations/2026_02_26_040522_create_actions_table.php deleted file mode 100644 index 9dc2a5e..0000000 --- a/database/migrations/2026_02_26_040522_create_actions_table.php +++ /dev/null @@ -1,32 +0,0 @@ -id(); - $table->string('act_name', 50)->unique()->comment('动作名称'); - $table->string('alias', 50)->nullable()->comment('动作别名'); - $table->string('toall', 200)->nullable()->comment('对所有人表现'); - $table->string('toself', 200)->nullable()->comment('对自己的表现'); - $table->string('toother', 200)->nullable()->comment('对其他人的表现'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('actions'); - } -}; diff --git a/database/migrations/2026_02_26_040522_create_admin_logs_table.php b/database/migrations/2026_02_26_040522_create_admin_logs_table.php deleted file mode 100644 index 3db8006..0000000 --- a/database/migrations/2026_02_26_040522_create_admin_logs_table.php +++ /dev/null @@ -1,48 +0,0 @@ -id(); - $table->string('username', 50)->index()->comment('管理员账号'); - $table->integer('uu_level')->nullable()->comment('管理员等级'); - $table->dateTime('in_time')->nullable()->comment('进入时间'); - $table->integer('out_time')->nullable()->comment('离开时间'); - $table->text('caozuo')->nullable()->comment('操作详情'); - $table->integer('fs_sl')->default(0)->comment('封杀数量'); - $table->text('fs_name')->nullable()->comment('封杀名单'); - $table->integer('tr_sl')->default(0)->comment('踢人数量'); - $table->text('tr_name')->nullable()->comment('踢人名单'); - $table->integer('jg_sl')->default(0)->comment('警告数量'); - $table->text('jg_name')->nullable()->comment('警告名单'); - $table->integer('dj_sl')->default(0)->comment('冻结数量'); - $table->text('dj_name')->nullable()->comment('冻结名单'); - $table->integer('yjdj_sl')->default(0)->comment('永久冻结数量'); - $table->text('yjdj_name')->nullable()->comment('永久冻结名单'); - $table->integer('fip_sl')->default(0)->comment('封IP数量'); - $table->text('fip_name')->nullable()->comment('封IP名单'); - $table->integer('fjj_sl')->default(0)->comment('发奖金数量'); - $table->integer('fjy_sl')->default(0)->comment('发经验数量'); - $table->integer('flh_sl')->default(0)->comment('发老虎数量'); - $table->dateTime('jl_time')->nullable()->index()->comment('记录时间'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('admin_logs'); - } -}; diff --git a/database/migrations/2026_02_26_040522_create_friend_calls_table.php b/database/migrations/2026_02_26_040522_create_friend_calls_table.php deleted file mode 100644 index 260495d..0000000 --- a/database/migrations/2026_02_26_040522_create_friend_calls_table.php +++ /dev/null @@ -1,32 +0,0 @@ -id(); - $table->string('who', 50)->index()->comment('呼叫人'); - $table->string('towho', 50)->index()->comment('被呼叫人'); - $table->text('callmess')->nullable()->comment('呼叫信息'); - $table->dateTime('calltime')->nullable()->index()->comment('呼叫时间'); - $table->tinyInteger('read')->default(0)->comment('是否已读 (1是 0否)'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('friend_calls'); - } -}; diff --git a/database/migrations/2026_02_26_040522_create_friend_requests_table.php b/database/migrations/2026_02_26_040522_create_friend_requests_table.php deleted file mode 100644 index e40a1ec..0000000 --- a/database/migrations/2026_02_26_040522_create_friend_requests_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->string('who', 50)->index()->comment('申请人'); - $table->string('towho', 50)->index()->comment('被申请人'); - $table->dateTime('sub_time')->nullable()->index()->comment('申请时间'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('friend_requests'); - } -}; diff --git a/database/migrations/2026_02_26_040523_create_ip_logs_table.php b/database/migrations/2026_02_26_040523_create_ip_logs_table.php deleted file mode 100644 index 1bf7b9d..0000000 --- a/database/migrations/2026_02_26_040523_create_ip_logs_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->string('ip', 50)->index()->comment('登录IP'); - $table->dateTime('sdate')->nullable()->index()->comment('登录时间'); - $table->string('uuname', 50)->index()->comment('登录用户名'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('ip_logs'); - } -}; diff --git a/database/migrations/2026_02_26_040523_create_marriages_table.php b/database/migrations/2026_02_26_040523_create_marriages_table.php deleted file mode 100644 index 28d92e9..0000000 --- a/database/migrations/2026_02_26_040523_create_marriages_table.php +++ /dev/null @@ -1,33 +0,0 @@ -id(); - $table->string('hyname', 50)->index()->comment('夫'); - $table->string('hyname1', 50)->index()->comment('妻'); - $table->dateTime('hytime')->nullable()->comment('结婚时间'); - $table->string('hygb', 50)->nullable()->comment('婚姻状态/广播'); - $table->string('hyjb', 50)->nullable()->comment('婚姻级别'); - $table->integer('i')->nullable()->comment('亲密度/属性'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('marriages'); - } -}; diff --git a/database/migrations/2026_02_26_040523_create_room_descriptions_table.php b/database/migrations/2026_02_26_040523_create_room_descriptions_table.php deleted file mode 100644 index 7cf8477..0000000 --- a/database/migrations/2026_02_26_040523_create_room_descriptions_table.php +++ /dev/null @@ -1,29 +0,0 @@ -id(); - $table->string('alias', 10)->unique()->comment('房间别名关联'); - $table->string('describ', 254)->nullable()->comment('房间详细描述文本'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('room_descriptions'); - } -}; diff --git a/database/migrations/2026_02_26_040523_create_scroll_ads_table.php b/database/migrations/2026_02_26_040523_create_scroll_ads_table.php deleted file mode 100644 index f4cf6b5..0000000 --- a/database/migrations/2026_02_26_040523_create_scroll_ads_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->string('ad_title', 250)->comment('广告/公告标题'); - $table->string('ad_link', 250)->nullable()->comment('链接地址'); - $table->tinyInteger('ad_new_flag')->default(0)->comment('新窗口打开标识 (1是 0否)'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('scroll_ads'); - } -}; diff --git a/database/migrations/2026_02_26_040523_create_user_items_table.php b/database/migrations/2026_02_26_040523_create_user_items_table.php deleted file mode 100644 index f738d4f..0000000 --- a/database/migrations/2026_02_26_040523_create_user_items_table.php +++ /dev/null @@ -1,32 +0,0 @@ -id(); - $table->string('name', 50)->index()->comment('使用者'); - $table->dateTime('times')->nullable()->index()->comment('获得时间'); - $table->text('gg')->nullable()->comment('道具类型/说明'); - $table->integer('dayy')->nullable()->comment('道具天数/数量'); - $table->string('lx', 50)->nullable()->comment('道具种类'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('user_items'); - } -}; diff --git a/database/migrations/2026_02_26_091905_add_visit_num_to_rooms_table.php b/database/migrations/2026_02_26_091905_add_visit_num_to_rooms_table.php deleted file mode 100644 index f95164f..0000000 --- a/database/migrations/2026_02_26_091905_add_visit_num_to_rooms_table.php +++ /dev/null @@ -1,34 +0,0 @@ -unsignedBigInteger('visit_num')->default(0)->after('ooooo') - ->comment('房间人气(总访问人次)'); - }); - } - - /** - * 回滚:移除 visit_num 字段 - */ - public function down(): void - { - Schema::table('rooms', function (Blueprint $table) { - $table->dropColumn('visit_num'); - }); - } -}; diff --git a/database/migrations/2026_02_26_092923_create_sysparam_table.php b/database/migrations/2026_02_26_092923_create_sysparam_table.php deleted file mode 100644 index c798b2b..0000000 --- a/database/migrations/2026_02_26_092923_create_sysparam_table.php +++ /dev/null @@ -1,71 +0,0 @@ -id(); - $table->string('alias', 100)->unique()->comment('参数别名(唯一标识)'); - $table->text('body')->nullable()->comment('参数值'); - $table->string('guidetxt', 500)->nullable()->comment('参数说明(后台显示)'); - $table->timestamps(); - }); - - // 插入默认的等级经验配置(管理员可在后台修改) - // 格式:逗号分隔,每项为"等级所需的累计经验值" - // 例如:等级1需要10经验,等级2需要50,等级3需要150... - DB::table('sysparam')->insert([ - [ - 'alias' => 'levelexp', - 'body' => '10,50,150,400,800,1500,3000,5000,8000,12000,18000,25000,35000,50000,80000', - 'guidetxt' => '等级经验阈值配置(逗号分隔)。第N个数字表示升到N级所需的累计经验值。例如:10,50,150 表示1级需10经验,2级需50经验,3级需150经验。', - 'created_at' => now(), - 'updated_at' => now(), - ], - [ - 'alias' => 'exp_per_heartbeat', - 'body' => '1', - 'guidetxt' => '每次心跳(约60秒)增加的经验值', - 'created_at' => now(), - 'updated_at' => now(), - ], - [ - 'alias' => 'superlevel', - 'body' => '16', - 'guidetxt' => '管理员级别(= 最高等级 + 1,拥有最高权限的等级阈值)', - 'created_at' => now(), - 'updated_at' => now(), - ], - [ - 'alias' => 'maxlevel', - 'body' => '15', - 'guidetxt' => '用户最高可达等级', - 'created_at' => now(), - 'updated_at' => now(), - ], - ]); - } - - /** - * 回滚:删除 sysparam 表 - */ - public function down(): void - { - Schema::dropIfExists('sysparam'); - } -}; diff --git a/database/migrations/2026_02_26_094113_create_autoact_table.php b/database/migrations/2026_02_26_094113_create_autoact_table.php deleted file mode 100644 index 78df96e..0000000 --- a/database/migrations/2026_02_26_094113_create_autoact_table.php +++ /dev/null @@ -1,73 +0,0 @@ -id(); - $table->string('text_body', 500)->comment('事件文本内容(支持变量替换)'); - $table->string('event_type', 20)->default('neutral')->comment('事件类型:good=好运, bad=坏运, neutral=中性'); - $table->integer('exp_change')->default(0)->comment('经验变化值(正=奖励, 负=惩罚)'); - $table->integer('jjb_change')->default(0)->comment('金币变化值(正=奖励, 负=惩罚)'); - $table->boolean('enabled')->default(true)->comment('是否启用'); - $table->timestamps(); - }); - - // 插入默认随机事件 - $events = [ - // 好运事件(奖励经验/金币) - ['text_body' => '🎉 恭喜【{username}】路遇仙人指点,获得 100 经验值!', 'event_type' => 'good', 'exp_change' => 100, 'jjb_change' => 0], - ['text_body' => '💰 【{username}】在路边捡到一袋金币,获得 500 金币!', 'event_type' => 'good', 'exp_change' => 0, 'jjb_change' => 500], - ['text_body' => '🌟 天降祥瑞!【{username}】获得 200 经验 + 200 金币!', 'event_type' => 'good', 'exp_change' => 200, 'jjb_change' => 200], - ['text_body' => '🎊 【{username}】参加武林大会获胜,奖励 300 经验!', 'event_type' => 'good', 'exp_change' => 300, 'jjb_change' => 0], - ['text_body' => '🏆 【{username}】完成了一个神秘任务,获得 150 金币!', 'event_type' => 'good', 'exp_change' => 0, 'jjb_change' => 150], - ['text_body' => '✨ 【{username}】领悟了一招绝学,经验暴增 500!', 'event_type' => 'good', 'exp_change' => 500, 'jjb_change' => 0], - ['text_body' => '🎁 系统随机赠送【{username}】100 金币,请查收!', 'event_type' => 'good', 'exp_change' => 0, 'jjb_change' => 100], - - // 坏运事件(扣除经验/金币) - ['text_body' => '💀 【{username}】不小心踩到陷阱,损失 50 经验!', 'event_type' => 'bad', 'exp_change' => -50, 'jjb_change' => 0], - ['text_body' => '😱 【{username}】遭遇山贼打劫,被抢走 100 金币!', 'event_type' => 'bad', 'exp_change' => 0, 'jjb_change' => -100], - ['text_body' => '🌧️ 【{username}】在雨中迷路,消耗 80 经验值。', 'event_type' => 'bad', 'exp_change' => -80, 'jjb_change' => 0], - ['text_body' => '🐍 【{username}】被毒蛇咬了一口,损失 30 经验和 50 金币!', 'event_type' => 'bad', 'exp_change' => -30, 'jjb_change' => -50], - - // 中性事件(纯文字,不奖惩) - ['text_body' => '🔮 星海小博士:【{username}】今天运势不错,继续加油!', 'event_type' => 'neutral', 'exp_change' => 0, 'jjb_change' => 0], - ['text_body' => '📜 星海小博士:有人说"坚持就是胜利",【{username}】觉得呢?', 'event_type' => 'neutral', 'exp_change' => 0, 'jjb_change' => 0], - ['text_body' => '🌈 星海小博士:【{username}】今天心情如何?记得多喝水哦!', 'event_type' => 'neutral', 'exp_change' => 0, 'jjb_change' => 0], - ['text_body' => '🎵 星海小博士:送给【{username}】一首歌,祝你天天开心!', 'event_type' => 'neutral', 'exp_change' => 0, 'jjb_change' => 0], - ]; - - $now = now(); - foreach ($events as &$event) { - $event['enabled'] = true; - $event['created_at'] = $now; - $event['updated_at'] = $now; - } - - DB::table('autoact')->insert($events); - } - - /** - * 回滚:删除 autoact 表 - */ - public function down(): void - { - Schema::dropIfExists('autoact'); - } -}; diff --git a/database/migrations/2026_02_26_095610_add_announcement_to_rooms_table.php b/database/migrations/2026_02_26_095610_add_announcement_to_rooms_table.php deleted file mode 100644 index b2bcbc4..0000000 --- a/database/migrations/2026_02_26_095610_add_announcement_to_rooms_table.php +++ /dev/null @@ -1,33 +0,0 @@ -string('announcement', 500)->nullable()->after('room_des')->comment('房间公告/祝福语(滚动显示)'); - }); - } - - /** - * 回滚 - */ - public function down(): void - { - Schema::table('rooms', function (Blueprint $table) { - $table->dropColumn('announcement'); - }); - } -}; diff --git a/database/migrations/2026_02_26_123935_change_s_color_to_string_in_users_table.php b/database/migrations/2026_02_26_123935_change_s_color_to_string_in_users_table.php deleted file mode 100644 index bba9a14..0000000 --- a/database/migrations/2026_02_26_123935_change_s_color_to_string_in_users_table.php +++ /dev/null @@ -1,33 +0,0 @@ -string('s_color', 10)->nullable()->comment('发言颜色(hex,如 #ff0000)')->change(); - }); - } - - /** - * 回滚:s_color 从 varchar 改回 integer - */ - public function down(): void - { - Schema::table('users', function (Blueprint $table) { - $table->integer('s_color')->nullable()->comment('发言颜色')->change(); - }); - } -}; diff --git a/database/migrations/2026_02_26_124547_add_missing_sysparam_records.php b/database/migrations/2026_02_26_124547_add_missing_sysparam_records.php deleted file mode 100644 index 5e197dc..0000000 --- a/database/migrations/2026_02_26_124547_add_missing_sysparam_records.php +++ /dev/null @@ -1,81 +0,0 @@ -insertOrIgnore([ - [ - 'alias' => 'level_kick', - 'body' => '10', - 'guidetxt' => '踢人所需等级(管理员可在聊天室踢出用户的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'level_mute', - 'body' => '8', - 'guidetxt' => '禁言所需等级(管理员可在聊天室禁言用户的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'level_ban', - 'body' => '12', - 'guidetxt' => '封号所需等级(管理员可封禁用户账号的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'level_banip', - 'body' => '14', - 'guidetxt' => '封IP所需等级(管理员可封禁用户IP地址的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'level_announcement', - 'body' => '10', - 'guidetxt' => '设置公告所需等级(可修改房间公告/祝福语的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'auto_event_chance', - 'body' => '10', - 'guidetxt' => '随机事件触发概率(百分比,1-100,每次心跳时按此概率触发随机事件)', - 'created_at' => $now, - 'updated_at' => $now, - ], - ]); - } - - /** - * 回滚:删除补充的参数记录 - */ - public function down(): void - { - DB::table('sysparam')->whereIn('alias', [ - 'level_kick', - 'level_mute', - 'level_ban', - 'level_banip', - 'level_announcement', - 'auto_event_chance', - ])->delete(); - } -}; diff --git a/database/migrations/2026_02_26_124729_update_level_system_to_99.php b/database/migrations/2026_02_26_124729_update_level_system_to_99.php deleted file mode 100644 index 0ca3dde..0000000 --- a/database/migrations/2026_02_26_124729_update_level_system_to_99.php +++ /dev/null @@ -1,95 +0,0 @@ -where('alias', 'maxlevel') - ->update(['body' => '99', 'guidetxt' => '用户最高可达等级', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'superlevel') - ->update(['body' => '100', 'guidetxt' => '管理员级别(= 最高等级 + 1,拥有最高权限的等级阈值)', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'levelexp') - ->update(['body' => $levelExpStr, 'updated_at' => $now]); - - // 按比例调整管理权限等级(原体系 /15,新体系 /99) - // 禁言:8/15 ≈ 53% → 50级 - DB::table('sysparam')->where('alias', 'level_mute') - ->update(['body' => '50', 'updated_at' => $now]); - - // 踢人:10/15 ≈ 67% → 60级 - DB::table('sysparam')->where('alias', 'level_kick') - ->update(['body' => '60', 'updated_at' => $now]); - - // 设公告:10/15 ≈ 67% → 60级 - DB::table('sysparam')->where('alias', 'level_announcement') - ->update(['body' => '60', 'updated_at' => $now]); - - // 封号:12/15 = 80% → 80级 - DB::table('sysparam')->where('alias', 'level_ban') - ->update(['body' => '80', 'updated_at' => $now]); - - // 封IP:14/15 ≈ 93% → 90级 - DB::table('sysparam')->where('alias', 'level_banip') - ->update(['body' => '90', 'updated_at' => $now]); - } - - /** - * 回滚:恢复到原始 15 级体系 - */ - public function down(): void - { - $now = now(); - - DB::table('sysparam')->where('alias', 'maxlevel') - ->update(['body' => '15', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'superlevel') - ->update(['body' => '16', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'levelexp') - ->update(['body' => '10,50,150,400,800,1500,3000,5000,8000,12000,18000,25000,35000,50000,80000', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'level_mute') - ->update(['body' => '8', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'level_kick') - ->update(['body' => '10', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'level_announcement') - ->update(['body' => '10', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'level_ban') - ->update(['body' => '12', 'updated_at' => $now]); - - DB::table('sysparam')->where('alias', 'level_banip') - ->update(['body' => '14', 'updated_at' => $now]); - } -}; diff --git a/database/migrations/2026_02_26_125933_add_fishing_sysparam_records.php b/database/migrations/2026_02_26_125933_add_fishing_sysparam_records.php deleted file mode 100644 index 2e594e0..0000000 --- a/database/migrations/2026_02_26_125933_add_fishing_sysparam_records.php +++ /dev/null @@ -1,64 +0,0 @@ -insertOrIgnore([ - [ - 'alias' => 'fishing_cost', - 'body' => '5', - 'guidetxt' => '每次钓鱼花费金币数量', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'fishing_cooldown', - 'body' => '300', - 'guidetxt' => '钓鱼冷却时间(秒,默认300秒=5分钟)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'fishing_wait_min', - 'body' => '8', - 'guidetxt' => '钓鱼最短等待上钩时间(秒)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'fishing_wait_max', - 'body' => '15', - 'guidetxt' => '钓鱼最长等待上钩时间(秒)', - 'created_at' => $now, - 'updated_at' => $now, - ], - ]); - } - - /** - * 回滚:删除钓鱼参数 - */ - public function down(): void - { - DB::table('sysparam')->whereIn('alias', [ - 'fishing_cost', - 'fishing_cooldown', - 'fishing_wait_min', - 'fishing_wait_max', - ])->delete(); - } -}; diff --git a/database/migrations/2026_02_26_132014_create_vip_levels_table.php b/database/migrations/2026_02_26_132014_create_vip_levels_table.php deleted file mode 100644 index f9f64f4..0000000 --- a/database/migrations/2026_02_26_132014_create_vip_levels_table.php +++ /dev/null @@ -1,129 +0,0 @@ -id(); - $table->string('name', 50)->comment('会员名称(如:白银会员)'); - $table->string('icon', 20)->default('⭐')->comment('等级图标/emoji'); - $table->string('color', 10)->default('#f59e0b')->comment('等级颜色(hex)'); - $table->decimal('exp_multiplier', 3, 1)->default(1.0)->comment('经验倍率'); - $table->decimal('jjb_multiplier', 3, 1)->default(1.0)->comment('金币倍率'); - $table->text('join_templates')->nullable()->comment('进入聊天室专属欢迎语 JSON 数组'); - $table->text('leave_templates')->nullable()->comment('离开聊天室专属提示语 JSON 数组'); - $table->tinyInteger('sort_order')->default(0)->comment('排序(越大越高级)'); - $table->integer('price')->default(0)->comment('赞助金额(元,供展示参考)'); - $table->integer('duration_days')->default(30)->comment('有效天数(0=永久)'); - $table->timestamps(); - }); - - // 默认种子数据(后台可随时修改/删除/新增) - $now = now(); - DB::table('vip_levels')->insert([ - [ - 'name' => '白银会员', - 'icon' => '🥈', - 'color' => '#94a3b8', - 'exp_multiplier' => 1.5, - 'jjb_multiplier' => 1.2, - 'join_templates' => json_encode([ - '白银贵宾{username}乘坐银色马车缓缓驶入,气质不凡!', - '白银贵宾{username}手持银色令牌,大步流星走了进来!', - ], JSON_UNESCAPED_UNICODE), - 'leave_templates' => json_encode([ - '白银贵宾{username}挥了挥手,潇洒离去', - '白银贵宾{username}骑着银色骏马飘然而去', - ], JSON_UNESCAPED_UNICODE), - 'sort_order' => 1, - 'price' => 10, - 'duration_days' => 30, - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'name' => '黄金会员', - 'icon' => '🥇', - 'color' => '#f59e0b', - 'exp_multiplier' => 2.0, - 'jjb_multiplier' => 1.5, - 'join_templates' => json_encode([ - '黄金贵宾{username}踩着金光闪闪的地毯,王者归来!', - '黄金贵宾{username}开着金色豪车呼啸而至,全场瞩目!', - ], JSON_UNESCAPED_UNICODE), - 'leave_templates' => json_encode([ - '黄金贵宾{username}乘金色祥云腾空而去,霸气侧漏!', - '黄金贵宾{username}微微拱手,金光一闪消失不见', - ], JSON_UNESCAPED_UNICODE), - 'sort_order' => 2, - 'price' => 30, - 'duration_days' => 30, - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'name' => '钻石会员', - 'icon' => '💎', - 'color' => '#3b82f6', - 'exp_multiplier' => 3.0, - 'jjb_multiplier' => 2.0, - 'join_templates' => json_encode([ - '钻石贵宾{username}踏着星辰大海从天而降,万众敬仰!', - '钻石贵宾{username}驾驭钻石巨龙破空而来,威震四方!', - ], JSON_UNESCAPED_UNICODE), - 'leave_templates' => json_encode([ - '钻石贵宾{username}化作一道星光冲天而去,令人叹服!', - '钻石贵宾{username}乘坐钻石飞船消失在银河尽头', - ], JSON_UNESCAPED_UNICODE), - 'sort_order' => 3, - 'price' => 50, - 'duration_days' => 30, - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'name' => '至尊会员', - 'icon' => '👑', - 'color' => '#a855f7', - 'exp_multiplier' => 5.0, - 'jjb_multiplier' => 3.0, - 'join_templates' => json_encode([ - '👑至尊{username}御驾亲临!九天雷鸣,万物俯首!众卿接驾!', - '👑至尊{username}脚踏七彩祥云,身披紫金龙袍,驾临此地!天地为之变色!', - ], JSON_UNESCAPED_UNICODE), - 'leave_templates' => json_encode([ - '👑至尊{username}龙袍一甩,紫气东来,瞬间消失在九天之上!', - '👑至尊{username}御驾回宫,百鸟齐鸣相送!', - ], JSON_UNESCAPED_UNICODE), - 'sort_order' => 4, - 'price' => 100, - 'duration_days' => 30, - 'created_at' => $now, - 'updated_at' => $now, - ], - ]); - } - - /** - * 回滚:删除 vip_levels 表 - */ - public function down(): void - { - Schema::dropIfExists('vip_levels'); - } -}; diff --git a/database/migrations/2026_02_26_132015_add_vip_level_id_to_users_table.php b/database/migrations/2026_02_26_132015_add_vip_level_id_to_users_table.php deleted file mode 100644 index 383d69f..0000000 --- a/database/migrations/2026_02_26_132015_add_vip_level_id_to_users_table.php +++ /dev/null @@ -1,38 +0,0 @@ -unsignedBigInteger('vip_level_id')->nullable()->after('huiyuan')->comment('会员等级ID'); - } - $table->foreign('vip_level_id')->references('id')->on('vip_levels')->nullOnDelete(); - }); - } - - /** - * 回滚:删除 vip_level_id 字段 - */ - public function down(): void - { - Schema::table('users', function (Blueprint $table) { - $table->dropForeign(['vip_level_id']); - $table->dropColumn('vip_level_id'); - }); - } -}; diff --git a/database/migrations/2026_02_26_132600_create_ai_tables.php b/database/migrations/2026_02_26_132600_create_ai_tables.php deleted file mode 100644 index b917c85..0000000 --- a/database/migrations/2026_02_26_132600_create_ai_tables.php +++ /dev/null @@ -1,70 +0,0 @@ -id(); - $table->string('provider', 50)->index()->comment('厂商标识(如 deepseek, qwen, openai)'); - $table->string('name', 100)->comment('厂商显示名称'); - $table->text('api_key')->comment('API Key(加密存储)'); - $table->string('api_endpoint', 255)->comment('API 端点地址'); - $table->string('model', 100)->comment('使用的模型名称'); - $table->decimal('temperature', 3, 2)->default(0.30)->comment('温度参数'); - $table->integer('max_tokens')->default(2048)->comment('最大 Token 数'); - $table->boolean('is_enabled')->default(true)->index()->comment('是否启用'); - $table->boolean('is_default')->default(false)->comment('是否为默认厂商'); - $table->integer('sort_order')->default(0)->comment('排序(故障转移时按此顺序)'); - $table->timestamps(); - }); - - // AI 使用日志表 - Schema::create('ai_usage_logs', function (Blueprint $table) { - $table->id(); - $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete()->comment('使用者'); - $table->string('provider', 50)->comment('AI 厂商标识'); - $table->string('model', 100)->comment('使用的模型'); - $table->string('action', 50)->default('chatbot')->comment('操作类型'); - $table->integer('prompt_tokens')->default(0)->comment('输入 Token 数'); - $table->integer('completion_tokens')->default(0)->comment('输出 Token 数'); - $table->integer('total_tokens')->default(0)->comment('总 Token 数'); - $table->decimal('cost', 10, 6)->default(0)->comment('费用'); - $table->integer('response_time_ms')->default(0)->comment('响应时间(毫秒)'); - $table->boolean('success')->default(true)->comment('是否成功'); - $table->text('error_message')->nullable()->comment('错误信息'); - $table->timestamps(); - - // 索引 - $table->index(['provider', 'created_at']); - }); - } - - /** - * 回滚迁移:删除 AI 相关的两张数据表 - */ - public function down(): void - { - Schema::dropIfExists('ai_usage_logs'); - Schema::dropIfExists('ai_provider_configs'); - } -}; diff --git a/database/migrations/2026_02_26_143645_add_warn_freeze_level_params.php b/database/migrations/2026_02_26_143645_add_warn_freeze_level_params.php deleted file mode 100644 index f7ef054..0000000 --- a/database/migrations/2026_02_26_143645_add_warn_freeze_level_params.php +++ /dev/null @@ -1,49 +0,0 @@ -insertOrIgnore([ - [ - 'alias' => 'level_warn', - 'body' => '5', - 'guidetxt' => '警告所需等级(管理员可在聊天室警告用户的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - [ - 'alias' => 'level_freeze', - 'body' => '14', - 'guidetxt' => '冻结账号所需等级(管理员可冻结用户账号的最低等级)', - 'created_at' => $now, - 'updated_at' => $now, - ], - ]); - } - - /** - * 回滚:删除补充的参数记录 - */ - public function down(): void - { - DB::table('sysparam')->whereIn('alias', [ - 'level_warn', - 'level_freeze', - ])->delete(); - } -}; diff --git a/database/migrations/2026_02_26_144806_add_question_answer_to_users.php b/database/migrations/2026_02_26_144806_add_question_answer_to_users.php deleted file mode 100644 index 973cfe0..0000000 --- a/database/migrations/2026_02_26_144806_add_question_answer_to_users.php +++ /dev/null @@ -1,51 +0,0 @@ -string('sign', 255)->nullable()->after('sex')->comment('个性签名'); - } - if (! Schema::hasColumn('users', 'question')) { - $table->string('question', 100)->nullable()->after('email')->comment('密保问题'); - } - if (! Schema::hasColumn('users', 'answer')) { - $table->string('answer', 100)->nullable()->after('question')->comment('密保答案'); - } - }); - } - - /** - * 回滚:删除字段 - */ - public function down(): void - { - Schema::table('users', function (Blueprint $table) { - $columns = []; - foreach (['sign', 'question', 'answer'] as $col) { - if (Schema::hasColumn('users', $col)) { - $columns[] = $col; - } - } - if ($columns) { - $table->dropColumn($columns); - } - }); - } -}; diff --git a/database/migrations/2026_02_26_151000_lowercase_usersf_extension.php b/database/migrations/2026_02_26_151000_lowercase_usersf_extension.php deleted file mode 100644 index ebfcb32..0000000 --- a/database/migrations/2026_02_26_151000_lowercase_usersf_extension.php +++ /dev/null @@ -1,33 +0,0 @@ -where('usersf', 'LIKE', '%.GIF') - ->update(['usersf' => DB::raw("REPLACE(usersf, '.GIF', '.gif')")]); - } - - /** - * 回滚:将 .gif 转回 .GIF - */ - public function down(): void - { - DB::table('users') - ->where('usersf', 'LIKE', '%.gif') - ->update(['usersf' => DB::raw("REPLACE(usersf, '.gif', '.GIF')")]); - } -}; diff --git a/database/migrations/2026_02_27_003842_seed_leaderboard_limit_sysparam.php b/database/migrations/2026_02_27_003842_seed_leaderboard_limit_sysparam.php deleted file mode 100644 index 4473105..0000000 --- a/database/migrations/2026_02_27_003842_seed_leaderboard_limit_sysparam.php +++ /dev/null @@ -1,42 +0,0 @@ -where('alias', 'leaderboard_limit')->exists(); - - if (! $exists) { - DB::table('sysparam')->insert([ - 'alias' => 'leaderboard_limit', - 'body' => '20', - 'guidetxt' => '🏆 排行榜每榜显示人数', - ]); - } - } - - /** - * 回滚:删除配置记录 - */ - public function down(): void - { - DB::table('sysparam')->where('alias', 'leaderboard_limit')->delete(); - } -}; diff --git a/database/migrations/2026_02_27_004100_seed_jjb_per_heartbeat_sysparam.php b/database/migrations/2026_02_27_004100_seed_jjb_per_heartbeat_sysparam.php deleted file mode 100644 index 1ce1593..0000000 --- a/database/migrations/2026_02_27_004100_seed_jjb_per_heartbeat_sysparam.php +++ /dev/null @@ -1,50 +0,0 @@ -where('alias', 'jjb_per_heartbeat')->exists()) { - DB::table('sysparam')->insert([ - 'alias' => 'jjb_per_heartbeat', - 'body' => '1-3', - 'guidetxt' => '💰 每次心跳金币奖励(支持固定值如"1",或范围如"1-5";设为0关闭)', - ]); - } - - // 更新经验配置描述(说明支持范围格式) - DB::table('sysparam') - ->where('alias', 'exp_per_heartbeat') - ->update(['guidetxt' => '⚡ 每次心跳经验奖励(支持固定值如"1",或范围如"1-10";设为0关闭)']); - } - - /** - * 回滚:删除金币配置记录 - */ - public function down(): void - { - DB::table('sysparam')->where('alias', 'jjb_per_heartbeat')->delete(); - - DB::table('sysparam') - ->where('alias', 'exp_per_heartbeat') - ->update(['guidetxt' => '每次心跳经验值']); - } -}; diff --git a/database/migrations/2026_02_27_005300_create_gifts_table.php b/database/migrations/2026_02_27_005300_create_gifts_table.php deleted file mode 100644 index 7230a0a..0000000 --- a/database/migrations/2026_02_27_005300_create_gifts_table.php +++ /dev/null @@ -1,57 +0,0 @@ -id(); - $table->string('name', 50)->comment('礼物名称'); - $table->string('emoji', 10)->comment('显示图标/emoji'); - $table->string('image', 100)->nullable()->comment('礼物图片路径(相对 /images/gifts/)'); - $table->unsignedInteger('cost')->default(0)->comment('消耗金币数'); - $table->unsignedInteger('charm')->default(1)->comment('增加魅力值'); - $table->unsignedTinyInteger('sort_order')->default(0)->comment('排序(越小越靠前)'); - $table->boolean('is_active')->default(true)->comment('是否启用'); - $table->timestamps(); - }); - - // 填充默认鲜花数据(图片文件名与 sort_order 对应) - DB::table('gifts')->insert([ - ['name' => '小雏菊', 'emoji' => '🌼', 'image' => 'daisy.png', 'cost' => 5, 'charm' => 1, 'sort_order' => 1, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ['name' => '玫瑰花', 'emoji' => '🌹', 'image' => 'rose.png', 'cost' => 10, 'charm' => 2, 'sort_order' => 2, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ['name' => '向日葵', 'emoji' => '🌻', 'image' => 'sunflower.png', 'cost' => 20, 'charm' => 5, 'sort_order' => 3, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ['name' => '樱花束', 'emoji' => '🌸', 'image' => 'sakura.png', 'cost' => 50, 'charm' => 12, 'sort_order' => 4, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ['name' => '满天星', 'emoji' => '💐', 'image' => 'bouquet.png', 'cost' => 100, 'charm' => 30, 'sort_order' => 5, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ['name' => '蓝色妖姬', 'emoji' => '🪻', 'image' => 'bluerose.png', 'cost' => 200, 'charm' => 66, 'sort_order' => 6, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ['name' => '钻石花冠', 'emoji' => '👑', 'image' => 'crown.png', 'cost' => 520, 'charm' => 188, 'sort_order' => 7, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], - ]); - } - - /** - * 回滚:删除 gifts 表 - */ - public function down(): void - { - Schema::dropIfExists('gifts'); - } -}; diff --git a/database/migrations/2026_02_27_011200_seed_charm_sysparam.php b/database/migrations/2026_02_27_011200_seed_charm_sysparam.php deleted file mode 100644 index 475ec4a..0000000 --- a/database/migrations/2026_02_27_011200_seed_charm_sysparam.php +++ /dev/null @@ -1,50 +0,0 @@ -insert([ - [ - 'alias' => 'charm_cross_sex', - 'body' => '2', - 'guidetxt' => '异性聊天每条消息增加的魅力值(男→女 或 女→男)', - ], - [ - 'alias' => 'charm_same_sex', - 'body' => '1', - 'guidetxt' => '同性聊天每条消息增加的魅力值(男→男 或 女→女)', - ], - [ - 'alias' => 'charm_hourly_limit', - 'body' => '20', - 'guidetxt' => '每小时通过聊天获取的魅力值上限(防刷屏)', - ], - ]); - } - - /** - * 回滚:删除聊天魅力相关配置 - */ - public function down(): void - { - DB::table('sysparam') - ->whereIn('alias', ['charm_cross_sex', 'charm_same_sex', 'charm_hourly_limit']) - ->delete(); - } -}; diff --git a/public/js/effects/effect-manager.js b/public/js/effects/effect-manager.js new file mode 100644 index 0000000..33c8210 --- /dev/null +++ b/public/js/effects/effect-manager.js @@ -0,0 +1,91 @@ +/** + * 文件功能:聊天室特效管理器 + * + * 统一管理全屏 Canvas 特效的入口、防重入和资源清理。 + * 使用方式:EffectManager.play('fireworks' | 'rain' | 'lightning') + */ + +const EffectManager = (() => { + // 当前正在播放的特效名称(防止同时播放两个特效) + let _current = null; + // 全屏 Canvas 元素引用 + let _canvas = null; + + /** + * 获取或创建全屏 Canvas 元素 + * 属性:fixed 定位,覆盖全屏,pointer-events:none 不阻止用户交互 + */ + function _getCanvas() { + if (_canvas && document.body.contains(_canvas)) { + return _canvas; + } + const c = document.createElement("canvas"); + c.id = "effect-canvas"; + c.style.cssText = [ + "position:fixed", + "top:0", + "left:0", + "width:100vw", + "height:100vh", + "z-index:99999", + "pointer-events:none", + ].join(";"); + c.width = window.innerWidth; + c.height = window.innerHeight; + document.body.appendChild(c); + _canvas = c; + return c; + } + + /** + * 特效结束后清理 Canvas,重置状态 + */ + function _cleanup() { + if (_canvas && document.body.contains(_canvas)) { + document.body.removeChild(_canvas); + } + _canvas = null; + _current = null; + } + + /** + * 播放指定特效 + * + * @param {string} type 特效类型:fireworks / rain / lightning + */ + function play(type) { + // 防重入:同时只允许一个特效 + if (_current) { + console.log( + `[EffectManager] 特效 ${_current} 正在播放,忽略 ${type}`, + ); + return; + } + + const canvas = _getCanvas(); + _current = type; + + switch (type) { + case "fireworks": + if (typeof FireworksEffect !== "undefined") { + FireworksEffect.start(canvas, _cleanup); + } + break; + case "rain": + if (typeof RainEffect !== "undefined") { + RainEffect.start(canvas, _cleanup); + } + break; + case "lightning": + if (typeof LightningEffect !== "undefined") { + LightningEffect.start(canvas, _cleanup); + } + break; + default: + console.warn(`[EffectManager] 未知特效类型:${type}`); + _cleanup(); + } + } + + return { play }; +})(); diff --git a/public/js/effects/fireworks.js b/public/js/effects/fireworks.js new file mode 100644 index 0000000..c43025e --- /dev/null +++ b/public/js/effects/fireworks.js @@ -0,0 +1,141 @@ +/** + * 文件功能:聊天室烟花特效 + * + * 使用 Canvas 粒子系统在全屏播放多发烟花爆炸动画。 + * 特效总时长约 4 秒,结束后自动清理并回调。 + */ + +const FireworksEffect = (() => { + // 粒子类:模拟一个爆炸后的发光粒子 + class Particle { + constructor(x, y, color) { + this.x = x; + this.y = y; + this.color = color; + // 随机方向和速度 + const angle = Math.random() * Math.PI * 2; + const speed = Math.random() * 6 + 2; + this.vx = Math.cos(angle) * speed; + this.vy = Math.sin(angle) * speed; + this.alpha = 1; + this.gravity = 0.12; + this.decay = Math.random() * 0.012 + 0.012; // 透明度每帧衰减量 + this.radius = Math.random() * 3 + 1; + } + + /** 每帧更新粒子位置和状态 */ + update() { + this.vy += this.gravity; + this.x += this.vx; + this.y += this.vy; + this.vx *= 0.98; // 空气阻力 + this.vy *= 0.98; + this.alpha -= this.decay; + } + + /** 绘制粒子 */ + draw(ctx) { + ctx.save(); + ctx.globalAlpha = Math.max(0, this.alpha); + ctx.fillStyle = this.color; + ctx.beginPath(); + ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); + ctx.fill(); + ctx.restore(); + } + } + + // 预定义烟花颜色组 + const COLORS = [ + "#ff4444", + "#ff8800", + "#ffdd00", + "#44ff44", + "#44ddff", + "#8844ff", + "#ff44cc", + "#ffffff", + "#ffaaaa", + "#aaffaa", + "#aaaaff", + "#ffffaa", + ]; + + /** + * 发射一枚烟花,返回粒子数组 + * + * @param {number} x 爆炸中心 x + * @param {number} y 爆炸中心 y + * @param {number} count 粒子数量 + * @returns {Particle[]} + */ + function _burst(x, y, count) { + const color = COLORS[Math.floor(Math.random() * COLORS.length)]; + const particles = []; + for (let i = 0; i < count; i++) { + particles.push(new Particle(x, y, color)); + } + return particles; + } + + /** + * 启动烟花特效 + * + * @param {HTMLCanvasElement} canvas 全屏 Canvas + * @param {Function} onEnd 特效结束回调 + */ + function start(canvas, onEnd) { + const ctx = canvas.getContext("2d"); + const w = canvas.width; + const h = canvas.height; + const DURATION = 4500; // 总时长(ms) + + let particles = []; + let animId = null; + let launchCount = 0; + const MAX_LAUNCHES = 8; // 总共发射几枚烟花 + + // 定时发射烟花 + const launchInterval = setInterval(() => { + if (launchCount >= MAX_LAUNCHES) { + clearInterval(launchInterval); + return; + } + const x = w * (0.15 + Math.random() * 0.7); // 避免贴近边缘 + const y = h * (0.1 + Math.random() * 0.5); // 在屏幕上半区爆炸 + const count = Math.floor(Math.random() * 40) + 60; + particles = particles.concat(_burst(x, y, count)); + launchCount++; + }, 500); + + const startTime = performance.now(); + + // 动画循环 + function animate(now) { + // 用半透明黑色覆盖,产生运动拖尾效果 + ctx.fillStyle = "rgba(0, 0, 0, 0.18)"; + ctx.fillRect(0, 0, w, h); + + // 更新并绘制存活粒子 + particles = particles.filter((p) => p.alpha > 0.02); + particles.forEach((p) => { + p.update(); + p.draw(ctx); + }); + + if (now - startTime < DURATION) { + animId = requestAnimationFrame(animate); + } else { + // 特效结束:清空 canvas 后回调 + clearInterval(launchInterval); + cancelAnimationFrame(animId); + ctx.clearRect(0, 0, w, h); + onEnd(); + } + } + + animId = requestAnimationFrame(animate); + } + + return { start }; +})(); diff --git a/public/js/effects/lightning.js b/public/js/effects/lightning.js new file mode 100644 index 0000000..a3e9f4f --- /dev/null +++ b/public/js/effects/lightning.js @@ -0,0 +1,126 @@ +/** + * 文件功能:聊天室雷电特效 + * + * 使用递归分叉算法在 Canvas 上绘制真实感闪电路径, + * 配合全屏闪白效果模拟雷闪。总持续约 5 秒,结束后回调。 + */ + +const LightningEffect = (() => { + /** + * 递归绘制闪电路径(分裂算法) + * + * @param {CanvasRenderingContext2D} ctx + * @param {number} x1 起点 x + * @param {number} y1 起点 y + * @param {number} x2 终点 x + * @param {number} y2 终点 y + * @param {number} depth 当前递归深度(控制分叉层数) + * @param {number} width 线条宽度 + */ + function _drawBolt(ctx, x1, y1, x2, y2, depth, width) { + if (depth <= 0) return; + + // 中点随机偏移(越深层偏移越小,产生流畅感) + const mx = (x1 + x2) / 2 + (Math.random() - 0.5) * 80 * depth; + const my = (y1 + y2) / 2 + (Math.random() - 0.5) * 20 * depth; + const glow = ctx.createLinearGradient(x1, y1, x2, y2); + glow.addColorStop(0, "rgba(200, 220, 255, 0.9)"); + glow.addColorStop(1, "rgba(150, 180, 255, 0.6)"); + + ctx.save(); + ctx.strokeStyle = glow; + ctx.lineWidth = width; + ctx.shadowColor = "#aaccff"; + ctx.shadowBlur = 18; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.quadraticCurveTo(mx, my, x2, y2); + ctx.stroke(); + ctx.restore(); + + // 递归绘制两段子路径 + _drawBolt(ctx, x1, y1, mx, my, depth - 1, width * 0.65); + _drawBolt(ctx, mx, my, x2, y2, depth - 1, width * 0.65); + + // 随机在中途分叉一条小支路(50% 概率) + if (depth > 1 && Math.random() > 0.5) { + const bx = mx + (Math.random() - 0.5) * 120; + const by = my + Math.random() * 80 + 40; + _drawBolt(ctx, mx, my, bx, by, depth - 2, width * 0.4); + } + } + + /** + * 渲染一次闪电 + 闪屏效果 + * + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} ctx + */ + function _flash(canvas, ctx) { + const w = canvas.width; + const h = canvas.height; + + // 清空画布 + ctx.clearRect(0, 0, w, h); + + // 闪屏:全屏短暂泛白 + ctx.fillStyle = "rgba(220, 235, 255, 0.55)"; + ctx.fillRect(0, 0, w, h); + + // 绘制 1-3 条主闪电(从随机顶部位置向下延伸) + const boltCount = Math.floor(Math.random() * 2) + 1; + for (let i = 0; i < boltCount; i++) { + const x1 = w * (0.2 + Math.random() * 0.6); + const y1 = 0; + const x2 = x1 + (Math.random() - 0.5) * 300; + const y2 = h * (0.5 + Math.random() * 0.4); + _drawBolt(ctx, x1, y1, x2, y2, 4, 3); + } + + // 50ms 后让画布逐渐消退(模拟闪电短促感) + setTimeout(() => { + ctx.clearRect(0, 0, w, h); + }, 80); + } + + /** + * 启动雷电特效 + * + * @param {HTMLCanvasElement} canvas 全屏 Canvas + * @param {Function} onEnd 特效结束回调 + */ + function start(canvas, onEnd) { + const ctx = canvas.getContext("2d"); + const FLASHES = 5; // 总闪电次数 + const DURATION = 5000; // 总时长(ms) + let count = 0; + + // 间隔不规则触发多次闪电(模拟真实雷电节奏) + function nextFlash() { + if (count >= FLASHES) { + // 全部闪完,结束特效 + setTimeout(() => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + onEnd(); + }, 500); + return; + } + _flash(canvas, ctx); + count++; + // 下次闪电间隔:800ms ~ 1200ms 之间随机 + const delay = 700 + Math.random() * 500; + setTimeout(nextFlash, delay); + } + + // 短暂延迟后开始第一次闪电 + setTimeout(nextFlash, 300); + + // 安全兜底:超时强制结束 + setTimeout(() => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + onEnd(); + }, DURATION + 500); + } + + return { start }; +})(); diff --git a/public/js/effects/rain.js b/public/js/effects/rain.js new file mode 100644 index 0000000..6c30281 --- /dev/null +++ b/public/js/effects/rain.js @@ -0,0 +1,108 @@ +/** + * 文件功能:聊天室下雨特效 + * + * 使用 Canvas 绘制斜向雨线,模拟真实降雨视觉效果。 + * 特效总时长约 8 秒,结束后自动清理并回调。 + */ + +const RainEffect = (() => { + // 雨滴类:一条从顶部往下落的斜线 + class Drop { + constructor(w, h) { + this.reset(w, h); + } + + /** + * 重置/初始化雨滴位置 + * + * @param {number} w Canvas 宽度 + * @param {number} h Canvas 高度 + */ + reset(w, h) { + this.x = Math.random() * w; + this.y = Math.random() * -h; // 从屏幕上方随机位置开始 + this.len = Math.random() * 20 + 10; // 雨线长度 + this.speed = Math.random() * 8 + 6; // 下落速度 + this.angle = (Math.PI / 180) * (75 + Math.random() * 10); // 倾斜角(接近竖直偏右) + this.alpha = Math.random() * 0.3 + 0.2; // 透明度 + this.w = w; + this.h = h; + } + + /** 每帧更新雨滴位置 */ + update() { + this.x += Math.cos(this.angle) * this.speed * 0.3; + this.y += Math.sin(this.angle) * this.speed; + // 落出屏幕后重置 + if (this.y > this.h + this.len) { + this.reset(this.w, this.h); + } + } + + /** 绘制雨滴线段 */ + draw(ctx) { + ctx.save(); + ctx.strokeStyle = `rgba(155, 200, 255, ${this.alpha})`; + ctx.lineWidth = 0.8; + ctx.beginPath(); + ctx.moveTo(this.x, this.y); + ctx.lineTo( + this.x + Math.cos(this.angle) * this.len, + this.y + Math.sin(this.angle) * this.len, + ); + ctx.stroke(); + ctx.restore(); + } + } + + /** + * 启动下雨特效 + * + * @param {HTMLCanvasElement} canvas 全屏 Canvas + * @param {Function} onEnd 特效结束回调 + */ + function start(canvas, onEnd) { + const ctx = canvas.getContext("2d"); + const w = canvas.width; + const h = canvas.height; + const DURATION = 8000; // 总时长(ms) + const DROP_COUNT = 180; // 雨滴数量 + + // 初始化所有雨滴,随机分布在屏幕各处(避免开始时从顶部一起落) + const drops = Array.from({ length: DROP_COUNT }, () => { + const d = new Drop(w, h); + d.y = Math.random() * h; // 初始 Y 随机,不全部从顶部开始 + return d; + }); + + let animId = null; + const startTime = performance.now(); + + // 画"乌云"背景遮罩(让画面有阴暗感但不完全遮住聊天) + ctx.fillStyle = "rgba(30, 40, 60, 0.18)"; + ctx.fillRect(0, 0, w, h); + + function animate(now) { + // 用极轻微的透明背景刷新(保留少量拖尾感) + ctx.fillStyle = "rgba(30, 40, 60, 0.08)"; + ctx.fillRect(0, 0, w, h); + + drops.forEach((d) => { + d.update(); + d.draw(ctx); + }); + + if (now - startTime < DURATION) { + animId = requestAnimationFrame(animate); + } else { + cancelAnimationFrame(animId); + ctx.clearRect(0, 0, w, h); + onEnd(); + } + } + + animId = requestAnimationFrame(animate); + } + + return { start }; +})(); diff --git a/resources/js/chat.js b/resources/js/chat.js index a75170c..0855a9b 100644 --- a/resources/js/chat.js +++ b/resources/js/chat.js @@ -63,6 +63,11 @@ export function initChat(roomId) { window.dispatchEvent( new CustomEvent("chat:screen-cleared", { detail: e }), ); + }) + // 监听管理员触发的全屏特效(烟花/下雨/雷电) + .listen("EffectBroadcast", (e) => { + console.log("特效播放:", e); + window.dispatchEvent(new CustomEvent("chat:effect", { detail: e })); }); } diff --git a/resources/views/chat/frame.blade.php b/resources/views/chat/frame.blade.php index bb449f7..bfc2e4f 100644 --- a/resources/views/chat/frame.blade.php +++ b/resources/views/chat/frame.blade.php @@ -93,6 +93,13 @@ {{-- ═══════════ 聊天室交互脚本(独立文件维护) ═══════════ --}} @include('chat.partials.user-actions') + + {{-- 全屏特效系统:管理员烟花/下雨/雷电 --}} + + + + + @include('chat.partials.scripts') diff --git a/resources/views/chat/partials/input-bar.blade.php b/resources/views/chat/partials/input-bar.blade.php index d510035..18405ff 100644 --- a/resources/views/chat/partials/input-bar.blade.php +++ b/resources/views/chat/partials/input-bar.blade.php @@ -77,6 +77,16 @@ + {{-- 全屏特效按钮组(仅管理员可见) --}} + + + @endif