重构:运维工具迁移为独立页面,侧边栏新增「运维工具」菜单
- 新建 OpsController,承接四项运维操作
- 新建 admin/ops/index.blade.php 独立页面(卡片式布局)
- admin 路由改为 /admin/ops/* -> admin.ops.*
- 侧边栏「AI 厂商配置」下方新增「🛠️ 运维工具」菜单入口
- SystemController 移除运维方法,职责回归纯参数配置
- system/edit 移除内嵌运维块,页面保持简洁
This commit is contained in:
108
app/Http/Controllers/Admin/OpsController.php
Normal file
108
app/Http/Controllers/Admin/OpsController.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:运维工具控制器
|
||||
* 提供缓存清理、路由清理、视图清理、房间在线名单清理等一键运维操作
|
||||
* 仅 id=1 超管可访问
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class OpsController extends Controller
|
||||
{
|
||||
/**
|
||||
* 运维工具主页
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
return view('admin.ops.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理应用缓存(config:clear + cache:clear)
|
||||
*/
|
||||
public function clearCache(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
|
||||
return redirect()->route('admin.ops.index')
|
||||
->with('ops_success', '✅ 应用缓存已清除(config:clear + cache:clear)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理路由缓存(route:clear)
|
||||
*/
|
||||
public function clearRoutes(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
Artisan::call('route:clear');
|
||||
|
||||
return redirect()->route('admin.ops.index')
|
||||
->with('ops_success', '✅ 路由缓存已清除(route:clear)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理视图缓存(view:clear)
|
||||
*/
|
||||
public function clearViews(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
Artisan::call('view:clear');
|
||||
|
||||
return redirect()->route('admin.ops.index')
|
||||
->with('ops_success', '✅ 视图缓存已清除(view:clear)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有房间 Redis 在线名单(清除幽灵在线脏数据)
|
||||
*/
|
||||
public function clearRoomOnline(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
$prefix = config('database.redis.options.prefix', '');
|
||||
$cursor = '0';
|
||||
$cleaned = 0;
|
||||
|
||||
do {
|
||||
[$cursor, $keys] = Redis::scan($cursor, ['match' => $prefix.'room:*:users', 'count' => 100]);
|
||||
foreach ($keys ?? [] as $fullKey) {
|
||||
// 去掉前缀,还原为 Laravel Facade 使用的短 Key
|
||||
$shortKey = $prefix ? substr($fullKey, strlen($prefix)) : $fullKey;
|
||||
Redis::del($shortKey);
|
||||
$cleaned++;
|
||||
}
|
||||
} while ($cursor !== '0');
|
||||
|
||||
return redirect()->route('admin.ops.index')
|
||||
->with('ops_success', "✅ 已清理 {$cleaned} 个房间的在线名单(幽灵在线已清除)");
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* 文件功能:系统参数配置控制器
|
||||
* (替代原版 VIEWSYS.ASP / SetSYS.ASP)
|
||||
* 同时提供运维工具:缓存清理、路由清理、视图清理、房间在线名单清理
|
||||
* 运维工具已迁移至 OpsController
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
@@ -17,9 +17,6 @@ use App\Models\SysParam;
|
||||
use App\Services\ChatStateService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class SystemController extends Controller
|
||||
@@ -68,77 +65,4 @@ class SystemController extends Controller
|
||||
|
||||
return redirect()->route('admin.system.edit')->with('success', '系统参数已成功更新并生效!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 运维工具:清理应用缓存(config + cache + application)
|
||||
* 仅 id=1 超管可用
|
||||
*/
|
||||
public function clearCache(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
|
||||
return redirect()->route('admin.system.edit')->with('ops_success', '✅ 应用缓存已清除(config:clear + cache:clear)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 运维工具:清理路由缓存
|
||||
* 仅 id=1 超管可用
|
||||
*/
|
||||
public function clearRoutes(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
Artisan::call('route:clear');
|
||||
|
||||
return redirect()->route('admin.system.edit')->with('ops_success', '✅ 路由缓存已清除(route:clear)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 运维工具:清理视图缓存
|
||||
* 仅 id=1 超管可用
|
||||
*/
|
||||
public function clearViews(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
Artisan::call('view:clear');
|
||||
|
||||
return redirect()->route('admin.system.edit')->with('ops_success', '✅ 视图缓存已清除(view:clear)');
|
||||
}
|
||||
|
||||
/**
|
||||
* 运维工具:清理所有房间 Redis 在线名单(清除幽灵在线脏数据)
|
||||
* 仅 id=1 超管可用
|
||||
*/
|
||||
public function clearRoomOnline(): RedirectResponse
|
||||
{
|
||||
if (Auth::id() !== 1) {
|
||||
abort(403, '无权限操作');
|
||||
}
|
||||
|
||||
$prefix = config('database.redis.options.prefix', '');
|
||||
$cursor = '0';
|
||||
$cleaned = 0;
|
||||
|
||||
do {
|
||||
[$cursor, $keys] = Redis::scan($cursor, ['match' => $prefix.'room:*:users', 'count' => 100]);
|
||||
foreach ($keys ?? [] as $fullKey) {
|
||||
// 去掉前缀,还原为 Laravel Facade 使用的短 Key
|
||||
$shortKey = $prefix ? substr($fullKey, strlen($prefix)) : $fullKey;
|
||||
Redis::del($shortKey);
|
||||
$cleaned++;
|
||||
}
|
||||
} while ($cursor !== '0');
|
||||
|
||||
return redirect()->route('admin.system.edit')->with('ops_success', "✅ 已清理 {$cleaned} 个房间的在线名单(幽灵在线已清除)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,10 @@
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.ai-providers.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
🤖 AI 厂商配置
|
||||
</a>
|
||||
<a href="{{ route('admin.ops.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.ops.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
🛠️ 运维工具
|
||||
</a>
|
||||
<a href="{{ route('admin.changelogs.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.changelogs.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
📋 开发日志
|
||||
|
||||
117
resources/views/admin/ops/index.blade.php
Normal file
117
resources/views/admin/ops/index.blade.php
Normal file
@@ -0,0 +1,117 @@
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '运维工具')
|
||||
|
||||
@section('content')
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="p-6 border-b border-gray-100 bg-gray-50">
|
||||
<h2 class="text-lg font-bold text-gray-800">🛠️ 运维工具</h2>
|
||||
<p class="text-xs text-gray-500 mt-1">仅站长可见。每项操作不可撤销,请确认后执行。</p>
|
||||
</div>
|
||||
|
||||
{{-- 操作结果提示 --}}
|
||||
@if (session('ops_success'))
|
||||
<div class="mx-6 mt-5 p-4 bg-blue-50 border border-blue-200 rounded-lg text-blue-700 text-sm font-medium">
|
||||
{{ session('ops_success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
|
||||
{{-- 应用缓存清理 --}}
|
||||
<div class="border border-gray-200 rounded-xl p-5 hover:shadow-sm transition">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="text-2xl">🗑️</span>
|
||||
<div>
|
||||
<div class="font-bold text-gray-800 text-sm">应用缓存清理</div>
|
||||
<div class="text-xs text-gray-400">config:clear + cache:clear</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mb-4 leading-relaxed">
|
||||
清除 Laravel 配置和应用缓存。<br>
|
||||
修改 <code class="bg-gray-100 px-1 rounded">.env</code> 后、部署新版本后建议执行。
|
||||
</p>
|
||||
<form action="{{ route('admin.ops.clear-cache') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理应用缓存?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-amber-500 text-white rounded-lg text-sm font-bold hover:bg-amber-600 transition shadow-sm">
|
||||
立即执行
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 路由缓存清理 --}}
|
||||
<div class="border border-gray-200 rounded-xl p-5 hover:shadow-sm transition">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="text-2xl">🗺️</span>
|
||||
<div>
|
||||
<div class="font-bold text-gray-800 text-sm">路由缓存清理</div>
|
||||
<div class="text-xs text-gray-400">route:clear</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mb-4 leading-relaxed">
|
||||
清除路由缓存文件。<br>
|
||||
部署后出现 404 路由错误时执行。
|
||||
</p>
|
||||
<form action="{{ route('admin.ops.clear-routes') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理路由缓存?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-amber-500 text-white rounded-lg text-sm font-bold hover:bg-amber-600 transition shadow-sm">
|
||||
立即执行
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 视图缓存清理 --}}
|
||||
<div class="border border-gray-200 rounded-xl p-5 hover:shadow-sm transition">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="text-2xl">🖼️</span>
|
||||
<div>
|
||||
<div class="font-bold text-gray-800 text-sm">视图缓存清理</div>
|
||||
<div class="text-xs text-gray-400">view:clear</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mb-4 leading-relaxed">
|
||||
清除已编译的 Blade 视图缓存。<br>
|
||||
页面样式或内容更新后未生效时执行。
|
||||
</p>
|
||||
<form action="{{ route('admin.ops.clear-views') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理视图缓存?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-amber-500 text-white rounded-lg text-sm font-bold hover:bg-amber-600 transition shadow-sm">
|
||||
立即执行
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 房间在线名单清理 --}}
|
||||
<div class="border border-red-100 rounded-xl p-5 bg-red-50 hover:shadow-sm transition">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="text-2xl">👻</span>
|
||||
<div>
|
||||
<div class="font-bold text-red-700 text-sm">清理幽灵在线名单</div>
|
||||
<div class="text-xs text-red-400">Redis room:*:users</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-red-500 mb-4 leading-relaxed">
|
||||
清空所有房间的 Redis 在线记录,解决在线人数虚高问题。<br>
|
||||
<strong>执行后在线用户需重新进房才能出现在名单中。</strong>
|
||||
</p>
|
||||
<form action="{{ route('admin.ops.clear-room-online') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理所有房间在线名单?\n在线用户需重进房间才能重新出现在名单!')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-red-500 text-white rounded-lg text-sm font-bold hover:bg-red-600 transition shadow-sm">
|
||||
立即执行
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -11,17 +11,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Flash 提示 --}}
|
||||
@if (session('success'))
|
||||
<div class="mx-6 mt-4 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
|
||||
✅ {{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if (session('ops_success'))
|
||||
<div class="mx-6 mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg text-blue-700 text-sm font-medium">
|
||||
{{ session('ops_success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6">
|
||||
<form action="{{ route('admin.system.update') }}" method="POST">
|
||||
@@ -53,78 +47,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 运维工具(仅 id=1 超管可见) --}}
|
||||
@if (auth()->id() === 1)
|
||||
<div class="mt-6 bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="p-6 border-b border-gray-100 bg-gray-50">
|
||||
<h2 class="text-lg font-bold text-gray-800">🛠️ 运维工具</h2>
|
||||
<p class="text-xs text-gray-500 mt-1">仅站长可见。操作不可撤销,请确认后执行。</p>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
||||
{{-- 应用缓存清理 --}}
|
||||
<div class="border border-gray-200 rounded-lg p-4">
|
||||
<div class="font-bold text-gray-700 text-sm mb-1">🗑️ 应用缓存清理</div>
|
||||
<p class="text-xs text-gray-400 mb-3">执行 <code class="bg-gray-100 px-1 rounded">config:clear +
|
||||
cache:clear</code>,修改 .env 后需执行。</p>
|
||||
<form action="{{ route('admin.system.ops.clear-cache') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理应用缓存?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-1.5 bg-amber-500 text-white rounded-md text-sm font-bold hover:bg-amber-600 transition">
|
||||
立即清理
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 路由缓存清理 --}}
|
||||
<div class="border border-gray-200 rounded-lg p-4">
|
||||
<div class="font-bold text-gray-700 text-sm mb-1">🗺️ 路由缓存清理</div>
|
||||
<p class="text-xs text-gray-400 mb-3">执行 <code
|
||||
class="bg-gray-100 px-1 rounded">route:clear</code>,修改路由后若出现 404 时执行。</p>
|
||||
<form action="{{ route('admin.system.ops.clear-routes') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理路由缓存?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-1.5 bg-amber-500 text-white rounded-md text-sm font-bold hover:bg-amber-600 transition">
|
||||
立即清理
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 视图缓存清理 --}}
|
||||
<div class="border border-gray-200 rounded-lg p-4">
|
||||
<div class="font-bold text-gray-700 text-sm mb-1">🖼️ 视图缓存清理</div>
|
||||
<p class="text-xs text-gray-400 mb-3">执行 <code
|
||||
class="bg-gray-100 px-1 rounded">view:clear</code>,页面样式/内容不更新时执行。</p>
|
||||
<form action="{{ route('admin.system.ops.clear-views') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理视图缓存?')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-1.5 bg-amber-500 text-white rounded-md text-sm font-bold hover:bg-amber-600 transition">
|
||||
立即清理
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 房间在线名单清理 --}}
|
||||
<div class="border border-red-100 rounded-lg p-4 bg-red-50">
|
||||
<div class="font-bold text-red-700 text-sm mb-1">👻 清理幽灵在线名单</div>
|
||||
<p class="text-xs text-red-400 mb-3">清空所有房间 Redis 在线记录,解决人数虚高问题。<br>执行后在线用户需重新进房才能出现在名单。</p>
|
||||
<form action="{{ route('admin.system.ops.clear-room-online') }}" method="POST"
|
||||
onsubmit="return confirm('确定清理所有房间在线名单?在线用户需重进房间!')">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="px-4 py-1.5 bg-red-500 text-white rounded-md text-sm font-bold hover:bg-red-600 transition">
|
||||
立即清理
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@@ -274,10 +274,11 @@ Route::middleware(['chat.auth', 'chat.has_position'])->prefix('admin')->name('ad
|
||||
Route::put('/system', [\App\Http\Controllers\Admin\SystemController::class, 'update'])->name('system.update');
|
||||
|
||||
// 运维工具(仅 id=1 超管可用)
|
||||
Route::post('/system/ops/clear-cache', [\App\Http\Controllers\Admin\SystemController::class, 'clearCache'])->name('system.ops.clear-cache');
|
||||
Route::post('/system/ops/clear-routes', [\App\Http\Controllers\Admin\SystemController::class, 'clearRoutes'])->name('system.ops.clear-routes');
|
||||
Route::post('/system/ops/clear-views', [\App\Http\Controllers\Admin\SystemController::class, 'clearViews'])->name('system.ops.clear-views');
|
||||
Route::post('/system/ops/clear-room-online', [\App\Http\Controllers\Admin\SystemController::class, 'clearRoomOnline'])->name('system.ops.clear-room-online');
|
||||
Route::get('/ops', [\App\Http\Controllers\Admin\OpsController::class, 'index'])->name('ops.index');
|
||||
Route::post('/ops/clear-cache', [\App\Http\Controllers\Admin\OpsController::class, 'clearCache'])->name('ops.clear-cache');
|
||||
Route::post('/ops/clear-routes', [\App\Http\Controllers\Admin\OpsController::class, 'clearRoutes'])->name('ops.clear-routes');
|
||||
Route::post('/ops/clear-views', [\App\Http\Controllers\Admin\OpsController::class, 'clearViews'])->name('ops.clear-views');
|
||||
Route::post('/ops/clear-room-online', [\App\Http\Controllers\Admin\OpsController::class, 'clearRoomOnline'])->name('ops.clear-room-online');
|
||||
|
||||
// 房间管理(含新增/编辑/删除)
|
||||
Route::get('/rooms', [\App\Http\Controllers\Admin\RoomManagerController::class, 'index'])->name('rooms.index');
|
||||
|
||||
Reference in New Issue
Block a user