"""项目角色生效管理API""" from __future__ import annotations import uuid from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_db_session, require_study_roles from app.models.study import Study router = APIRouter(prefix="/active-roles", tags=["active-roles"]) def _normalize_active_roles(value: object) -> list[str]: if not isinstance(value, list): raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="active_roles 必须是数组") roles: list[str] = [] for item in value: role = str(item or "").strip() if not role: continue if role == "ADMIN": raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="ADMIN 不能作为项目角色") if role == "QA": raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="QA 不能作为项目角色") if len(role) > 20: raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="项目角色长度不能超过20个字符") if role not in roles: roles.append(role) return roles @router.get("", summary="获取项目已生效角色列表") async def get_active_roles( study_id: uuid.UUID, _=Depends(require_study_roles(["PM"])), db: Annotated[AsyncSession, Depends(get_db_session)] = None, ) -> dict: result = await db.execute(select(Study).where(Study.id == study_id)) study = result.scalar_one_or_none() if not study: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="项目不存在") return {"active_roles": study.active_roles or []} @router.put("", summary="更新项目已生效角色列表") async def update_active_roles( study_id: uuid.UUID, payload: dict, _=Depends(require_study_roles(["PM"])), db: Annotated[AsyncSession, Depends(get_db_session)] = None, ) -> dict: result = await db.execute(select(Study).where(Study.id == study_id)) study = result.scalar_one_or_none() if not study: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="项目不存在") study.active_roles = _normalize_active_roles(payload.get("active_roles", [])) await db.commit() return {"active_roles": study.active_roles}