From 2947f0f74151c4ddb31ae7927cce692327d73276 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sun, 1 Mar 2026 12:54:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=9A=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=A2=9E=E5=8A=A0=E5=9C=A8=E7=BA=BF=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=88=97=EF=BC=8C=E6=94=AF=E6=8C=81=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UserManagerController 注入 ChatStateService,从 Redis 聚合 所有活跃房间在线用户名(跨房间去重) - 排序白名单加入 'online',在线排序用 orderByRaw CASE WHEN 虚拟列 desc = 在线用户优先显示,asc = 离线用户优先 - 视图表头加「在线 ↕」可排序列(绿色高亮箭头) - 每行显示绿色实心点+「在线」/灰点+「离线」小徽章 - my-duty-logs 分页已有 paginate(30)+withQueryString+links(),无需改动 --- .../Admin/UserManagerController.php | 32 ++++++++++++++++--- resources/views/admin/users/index.blade.php | 18 ++++++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Admin/UserManagerController.php b/app/Http/Controllers/Admin/UserManagerController.php index fccbaa7..c413b58 100644 --- a/app/Http/Controllers/Admin/UserManagerController.php +++ b/app/Http/Controllers/Admin/UserManagerController.php @@ -14,6 +14,7 @@ namespace App\Http\Controllers\Admin; use App\Enums\CurrencySource; use App\Http\Controllers\Controller; use App\Models\User; +use App\Services\ChatStateService; use App\Services\UserCurrencyService; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; @@ -25,14 +26,15 @@ use Illuminate\View\View; class UserManagerController extends Controller { /** - * 注入统一积分服务(用于管理员调整经验/金币/魅力时记录流水) + * 注入统一积分服务和聊天室状态服务 */ public function __construct( private readonly UserCurrencyService $currencyService, + private readonly ChatStateService $chatState, ) {} /** - * 显示用户列表及搜索(支持按等级/经验/金币/魅力排序) + * 显示用户列表及搜索(支持按等级/经验/金币/魅力/在线状态排序) */ public function index(Request $request): View { @@ -42,21 +44,41 @@ class UserManagerController extends Controller $query->where('username', 'like', '%'.$request->input('username').'%'); } + // 从 Redis 获取所有在线用户名(跨所有房间去重) + $onlineUsernames = collect(); + foreach ($this->chatState->getAllActiveRoomIds() as $roomId) { + $onlineUsernames = $onlineUsernames->merge(array_keys($this->chatState->getRoomUsers($roomId))); + } + $onlineUsernames = $onlineUsernames->unique()->values(); + // 排序:允许的字段白名单,防止 SQL 注入 - $sortable = ['user_level', 'exp_num', 'jjb', 'meili', 'id']; + $sortable = ['user_level', 'exp_num', 'jjb', 'meili', 'id', 'online']; $sortBy = in_array($request->input('sort_by'), $sortable) ? $request->input('sort_by') : 'id'; $sortDir = $request->input('sort_dir') === 'asc' ? 'asc' : 'desc'; + if ($sortBy === 'online') { + // 用虚拟列排序:在线用户标记为 1,离线为 0;desc = 在线优先 + if ($onlineUsernames->isNotEmpty()) { + $placeholders = implode(',', array_fill(0, $onlineUsernames->count(), '?')); + $query->orderByRaw( + "CASE WHEN username IN ({$placeholders}) THEN 1 ELSE 0 END {$sortDir}", + $onlineUsernames->toArray(), + ); + } + $query->orderBy('id', 'desc'); // 二级排序 + } else { + $query->orderBy($sortBy, $sortDir); + } + $users = $query ->with(['activePosition.position.department', 'vipLevel']) - ->orderBy($sortBy, $sortDir) ->paginate(20) ->withQueryString(); // VIP 等级选项列表(供编辑弹窗使用) $vipLevels = \App\Models\VipLevel::orderBy('sort_order')->get(); - return view('admin.users.index', compact('users', 'vipLevels', 'sortBy', 'sortDir')); + return view('admin.users.index', compact('users', 'vipLevels', 'sortBy', 'sortDir', 'onlineUsernames')); } /** diff --git a/resources/views/admin/users/index.blade.php b/resources/views/admin/users/index.blade.php index eacecbb..b15cb37 100644 --- a/resources/views/admin/users/index.blade.php +++ b/resources/views/admin/users/index.blade.php @@ -67,6 +67,11 @@ 注册时间 + + + 在线{{ $arrow('online') }} + + 管理操作 @@ -114,6 +119,16 @@ {{ $user->created_at->format('Y/m/d H:i') }} + + @php $isOnline = $onlineUsernames->contains($user->username); @endphp + + + {{ $isOnline ? '在线' : '离线' }} + +