diff --git a/backend/src/backend/api/platform/_deps.py b/backend/src/backend/api/platform/_deps.py index 5732e6e..3657fe9 100644 --- a/backend/src/backend/api/platform/_deps.py +++ b/backend/src/backend/api/platform/_deps.py @@ -79,7 +79,22 @@ async def _is_system_admin(session: AsyncSession, user: Users) -> bool: async def _load_role_by_code(session: AsyncSession, role_code: str) -> Roles: - role = await session.scalar(select(Roles).where(Roles.role_code == role_code)) + """Load a live platform-scoped role by code; 422 if missing. + + 之所以把 ``is_deleted == 0`` 和 ``role_scope == "platform"`` 写进 helper, + 是因为本 helper 的所有调用方(``employees`` create/update / 两个 count + helper)都需要的是 platform 角色。Workspace 角色不应被赋值到 + ``users.platform_role_id``,软删除的 role 也不应被新引用 —— 在 helper + 层兜底一次,后续 schema 放宽 ``role_code`` 为 ``str`` 时不必每个端点 + 再补守卫。 + """ + role = await session.scalar( + select(Roles).where( + Roles.role_code == role_code, + Roles.is_deleted == 0, + Roles.role_scope == "platform", + ) + ) if role is None: raise HTTPException( status.HTTP_422_UNPROCESSABLE_ENTITY, diff --git a/backend/src/backend/api/platform/employees.py b/backend/src/backend/api/platform/employees.py index 7c14131..398cc06 100644 --- a/backend/src/backend/api/platform/employees.py +++ b/backend/src/backend/api/platform/employees.py @@ -26,6 +26,13 @@ from backend.api.platform._deps import ( system_admin_context, ) +# 平台角色 code 的字符串约束:与 RoleCreate.role_code 一致。 +# 之所以从 Literal["admin","developer"] 放宽为 str,是因为 ``listPlatformRoles`` +# 现已返回用户自建的 platform 角色(``role_scope=="platform"`` 且 ``is_builtin==0``), +# 前端 dialog 用 ``listPlatformRoles()`` 渲染选项,提交非内置 code 时会被 Pydantic +# Literal 校验直接 422 拒掉。schema 不应枚举运行时数据;改用 ``_load_role_by_code`` +# 做存在性 + 平台作用域校验(并隐式排除 is_deleted=1)。 + router = APIRouter(prefix="/api/v1/platform", tags=["platform"]) @@ -42,7 +49,7 @@ class PlatformEmployeeCreate(BaseModel): display_name: str = Field(min_length=1, max_length=100) email: str | None = Field(default=None, max_length=255) password: str = Field(min_length=8, max_length=72) - role_code: Literal["admin", "developer"] | None = None + role_code: str | None = Field(default=None, min_length=2, max_length=64) # 修改平台用户资料、状态或平台角色的请求体。 @@ -52,7 +59,7 @@ class PlatformEmployeeUpdate(BaseModel): display_name: str | None = Field(default=None, min_length=1, max_length=100) email: str | None = Field(default=None, max_length=255) status: Literal["active", "disabled", "locked"] | None = None - role_code: Literal["admin", "developer"] | None = None + role_code: str | None = Field(default=None, min_length=2, max_length=64) # ---------------------------------------------------------------------------