where('is_active', true) ->with([ 'user', 'position.department', 'appointedBy', ]) ->join('positions', 'user_positions.position_id', '=', 'positions.id') ->join('departments', 'positions.department_id', '=', 'departments.id') ->orderByDesc('departments.rank') ->orderByDesc('positions.rank') ->select('user_positions.*') ->get(); // 部门+职务(供新增任命弹窗下拉选择,带在职人数统计) $departments = Department::with([ 'positions' => fn ($q) => $q->withCount('activeUserPositions')->ordered(), ])->ordered()->get(); return view('admin.appointments.index', compact('activePositions', 'departments')); } /** * 执行新增任命 * 管理员在后台直接任命用户,操作人为当前登录管理员 */ public function store(Request $request): RedirectResponse { $request->validate([ 'username' => 'required|string|exists:users,username', 'position_id' => 'required|exists:positions,id', 'remark' => 'nullable|string|max:255', ]); $operator = Auth::user(); $target = User::where('username', $request->username)->firstOrFail(); $targetPosition = Position::with('department')->findOrFail($request->position_id); $result = $this->appointmentService->appoint($operator, $target, $targetPosition, $request->remark); if ($result['ok']) { // 向所有当前有人在线的聊天室广播礼花公告(后台操作人不在聊天室内) foreach ($this->chatState->getAllActiveRoomIds() as $roomId) { broadcast(new AppointmentAnnounced( roomId: $roomId, targetUsername: $target->username, positionIcon: $targetPosition->icon ?? '🎖️', positionName: $targetPosition->name, departmentName: $targetPosition->department?->name ?? '', operatorName: $operator->username, )); } return redirect()->route('admin.appointments.index')->with('success', $result['message']); } return redirect()->route('admin.appointments.index')->with('error', $result['message']); } /** * 撤销职务 */ public function revoke(Request $request, UserPosition $userPosition): RedirectResponse { $operator = Auth::user(); $target = $userPosition->user; // 撤销前先记录职务信息(撤销后关联就断了) $userPosition->load('position.department'); $posIcon = $userPosition->position?->icon ?? '🎖️'; $posName = $userPosition->position?->name ?? ''; $deptName = $userPosition->position?->department?->name ?? ''; $result = $this->appointmentService->revoke($operator, $target, $request->remark); if ($result['ok']) { // 向所有活跃房间广播撤销公告 if ($posName) { foreach ($this->chatState->getAllActiveRoomIds() as $roomId) { broadcast(new AppointmentAnnounced( roomId: $roomId, targetUsername: $target->username, positionIcon: $posIcon, positionName: $posName, departmentName: $deptName, operatorName: $operator->username, type: 'revoke', )); } } return redirect()->route('admin.appointments.index')->with('success', $result['message']); } return redirect()->route('admin.appointments.index')->with('error', $result['message']); } /** * 查看某任职记录的在职登录日志 */ public function dutyLogs(UserPosition $userPosition): View { $userPosition->load(['user', 'position.department', 'appointedBy']); $logs = $userPosition->dutyLogs() ->orderByDesc('login_at') ->paginate(30); return view('admin.appointments.duty-logs', compact('userPosition', 'logs')); } /** * 查看某任职记录的权限操作日志 */ public function authorityLogs(UserPosition $userPosition): View { $userPosition->load(['user', 'position.department']); $logs = $userPosition->authorityLogs() ->with(['targetUser', 'targetPosition']) ->orderByDesc('created_at') ->paginate(30); return view('admin.appointments.authority-logs', compact('userPosition', 'logs')); } /** * 历史任职记录(全部 is_active=false 的记录) */ public function history(): View { $history = UserPosition::query() ->where('is_active', false) ->with(['user', 'position.department', 'appointedBy', 'revokedBy']) ->orderByDesc('revoked_at') ->paginate(30); return view('admin.appointments.history', compact('history')); } /** * 搜索用户(供任命弹窗 Ajax 快速查找) */ public function searchUsers(Request $request): JsonResponse { $keyword = $request->input('q', ''); $users = User::query() ->where('id', '!=', 1) // 排除超级管理员 ->where('username', 'like', "%{$keyword}%") ->whereDoesntHave('activePosition') // 排除已有职务的用户 ->select('id', 'username', 'user_level') ->limit(10) ->get(); return response()->json($users); } }