PMFlow/backend/app/api/users_api/routes_query.py

177 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)