功能:后台系统配置页新增「运维工具」面板(仅 id=1 可见)

- 应用缓存清理:config:clear + cache:clear
- 路由缓存清理:route:clear
- 视图缓存清理:view:clear
- 幽灵在线清理:扫描并清空所有房间 Redis 在线名单

所有操作均有确认弹窗,执行结果 Flash 提示反馈。
后端 abort(403) 双重校验,非超管无法访问接口。
This commit is contained in:
2026-03-03 15:00:54 +08:00
parent b03de378b0
commit adb9f157e6
3 changed files with 170 additions and 1 deletions
@@ -3,10 +3,11 @@
/**
* 文件功能:系统参数配置控制器
* (替代原版 VIEWSYS.ASP / SetSYS.ASP)
* 同时提供运维工具:缓存清理、路由清理、视图清理、房间在线名单清理
*
* @author ChatRoom Laravel
*
* @version 1.0.0
* @version 1.1.0
*/
namespace App\Http\Controllers\Admin;
@@ -16,6 +17,9 @@ 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
@@ -64,4 +68,77 @@ 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} 个房间的在线名单(幽灵在线已清除)");
}
}
@@ -11,6 +11,18 @@
</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">
@csrf
@@ -41,4 +53,78 @@
</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
+6
View File
@@ -273,6 +273,12 @@ Route::middleware(['chat.auth', 'chat.has_position'])->prefix('admin')->name('ad
Route::get('/system', [\App\Http\Controllers\Admin\SystemController::class, 'edit'])->name('system.edit');
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('/rooms', [\App\Http\Controllers\Admin\RoomManagerController::class, 'index'])->name('rooms.index');
Route::post('/rooms', [\App\Http\Controllers\Admin\RoomManagerController::class, 'store'])->name('rooms.store');