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 ? '在线' : '离线' }} + +