from __future__ import annotations from .shared import * from .helpers_roles import _build_role_filter, _has_any_role, _normalize_role_input, _parse_int_list from .helpers_user import _collect_user_enrichment, _serialize_user from ...services.platform_access_service import get_platform_context @router.get("/me", response_model=UserResponse) async def get_current_user_info( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db), ): """当前用户信息。""" return UserResponse( **{ **_serialize_user(current_user), "platform_context": await get_platform_context(db, user=current_user), } ) @router.get("/") async def list_users( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), search: Optional[str] = Query(None, description="用户名/姓名/邮箱关键字"), role: Optional[str] = Query(None), status_value: Optional[str] = Query(None, alias="status"), is_active: Optional[bool] = Query(None), project_id: Optional[int] = Query(None), workload_state: Optional[str] = Query(None, description="idle|normal|overloaded"), skill_ids: Optional[str] = Query(None, description="逗号分隔技能ID"), tenant_id: int = Depends(get_tenant_id), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """用户分页列表(支持多维过滤)。""" _ = current_user skill_id_list = _parse_int_list(skill_ids) query = select(User).where(User.tenant_id == tenant_id) if role: try: role_values = _normalize_role_input(role, allow_empty=True) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) if role_values: query = query.where(_build_role_filter(User.role, role_values)) if status_value: query = query.where(User.status == status_value) if is_active is not None: query = query.where(User.is_active == is_active) if search: q = f"%{search.strip()}%" query = query.where( or_( User.username.ilike(q), User.name.ilike(q), User.email.ilike(q), User.phone_e164.ilike(q), User.phone_number.ilike(q), ) ) if project_id is not None: query = query.join( UserProject, and_( UserProject.user_id == User.id, UserProject.tenant_id == tenant_id, ), ).where(UserProject.project_id == project_id) if skill_id_list: query = query.join( UserSkillBinding, and_( UserSkillBinding.user_id == User.id, UserSkillBinding.tenant_id == tenant_id, ), ).where(UserSkillBinding.skill_tag_id.in_(skill_id_list)) if workload_state: query = query.outerjoin( UserWorkload, and_( UserWorkload.user_id == User.id, UserWorkload.tenant_id == tenant_id, ), ) if workload_state == "idle": query = query.where(func.coalesce(UserWorkload.workload_ratio, 0.0) < 70.0) elif workload_state == "normal": query = query.where( and_( func.coalesce(UserWorkload.workload_ratio, 0.0) >= 70.0, func.coalesce(UserWorkload.workload_ratio, 0.0) <= 100.0, ) ) elif workload_state == "overloaded": query = query.where(func.coalesce(UserWorkload.workload_ratio, 0.0) > 100.0) total_result = await db.execute( select(func.count()).select_from(query.distinct().subquery()) ) total = int(total_result.scalar() or 0) result = await db.execute( query.distinct() .order_by(User.updated_at.desc(), User.id.desc()) .offset((page - 1) * page_size) .limit(page_size) ) users = list(result.scalars().all()) workload_map, top_skills, project_map = await _collect_user_enrichment(db, tenant_id, users) return { "items": [ _serialize_user( u, workload_map=workload_map, top_skills=top_skills, project_map=project_map, ) for u in users ], "page": page, "page_size": page_size, "total": total, } @router.get("/available") async def get_available_staff( role: Optional[str] = Query(None, description="按角色过滤"), tenant_id: int = Depends(get_tenant_id), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """获取可分配任务成员(启用 + active)。""" _ = current_user query = select(User).where( and_( User.tenant_id == tenant_id, User.status == UserStatus.ACTIVE.value, User.is_active == True, ) ) if role: try: role_values = _normalize_role_input(role, allow_empty=True) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) if role_values: query = query.where(_build_role_filter(User.role, role_values)) result = await db.execute(query.order_by(User.workload.asc(), User.id.asc())) users = result.scalars().all() return [_serialize_user(u) for u in users] @router.get("/{user_id}") async def get_user( user_id: int, tenant_id: int = Depends(get_tenant_id), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """获取单用户详情。""" _ = current_user result = await db.execute( select(User).where(and_(User.tenant_id == tenant_id, User.id == user_id)) ) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") workloads, skills, projects = await _collect_user_enrichment(db, tenant_id, [user]) return _serialize_user(user, workloads, skills, projects)