feat: create user api

This commit is contained in:
tao.chen
2026-08-06 10:42:17 +08:00
parent bf2cd9bd69
commit d813508461
2 changed files with 74 additions and 3 deletions
+51 -2
View File
@@ -28,6 +28,7 @@ Workspace membership CRUD::
Platform employee roster::
GET /employees — list all non-deleted users
POST /employees — create a user without workspace membership
Invariants
----------
@@ -50,10 +51,11 @@ from typing import Any, Literal
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy import func, select, update
from sqlalchemy import func, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from backend.dependencies import current_user, database_session
from common.auth.passwords import hash_password
from common.db.models import Roles, Users, WorkspaceMembers, Workspaces
from common.ids import new_ulid
@@ -111,6 +113,15 @@ class MemberUpdate(BaseModel):
member_status: Literal["active", "disabled", "locked"] | None = None
class PlatformEmployeeCreate(BaseModel):
model_config = ConfigDict(extra="forbid")
username: str = Field(min_length=2, max_length=64)
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)
# ---------------------------------------------------------------------------
# System-admin context dependency
# ---------------------------------------------------------------------------
@@ -302,6 +313,39 @@ async def list_platform_employees(
)
@router.post("/employees", status_code=status.HTTP_201_CREATED)
async def create_platform_employee(
payload: PlatformEmployeeCreate,
context: SystemAdminContext = Depends(system_admin_context),
session: AsyncSession = Depends(database_session),
) -> dict[str, Any]:
"""Create a platform user without assigning workspace membership."""
username = payload.username.strip()
display_name = payload.display_name.strip()
duplicate_conditions = [Users.username == username]
if payload.email:
duplicate_conditions.append(Users.email == payload.email.strip())
duplicate = await session.scalar(
select(Users.user_id).where(or_(*duplicate_conditions))
)
if duplicate is not None:
raise HTTPException(status.HTTP_409_CONFLICT, "用户名或邮箱已存在")
user = Users(
user_id=new_ulid(),
username=username,
display_name=display_name,
email=payload.email.strip() if payload.email else None,
password_hash=hash_password(payload.password),
status="active",
platform_role_id=None,
)
session.add(user)
await session.flush()
await session.refresh(user)
return _envelope(context.request_id, platform_employee_payload(user, None))
# ---------------------------------------------------------------------------
# Workspace CRUD
# ---------------------------------------------------------------------------
@@ -502,8 +546,13 @@ async def add_member(
"""Add a user to a workspace. The new row starts with member_status='active'."""
await _load_workspace(session, workspace_id)
user = await session.get(Users, payload.user_id)
if user is None:
if user is None or user.is_deleted != 0:
raise HTTPException(status.HTTP_404_NOT_FOUND, "用户不存在")
if user.status != "active":
raise HTTPException(
status.HTTP_409_CONFLICT,
f"用户状态为 {user.status},无法加入 workspace",
)
role = await _load_role_by_code(session, payload.role_code)
duplicate = await session.scalar(
select(WorkspaceMembers.user_id).where(