"""协作域路由(WP-12)。""" from typing import Annotated, Any from fastapi import APIRouter, Depends, HTTPException, Request, status from app.auth.dependencies import get_current_user, require_state_change from app.conversations.routes import _domain_of from app.db.session import DbSession from app.models import PersonalDomain, User from app.workspaces import service router = APIRouter(prefix="/v1/workspaces", tags=["workspaces"]) def _member_out(m: Any) -> dict[str, Any]: return { "id": m.id, "workspace_id": m.workspace_id, "member_domain_id": m.member_domain_id, "role": m.role, "created_at": m.created_at.isoformat(), } def _project_out(p: Any) -> dict[str, Any]: return { "id": p.id, "workspace_id": p.workspace_id, "name": p.name, "status": p.status, "current_version_id": p.current_version_id, "created_at": p.created_at.isoformat(), } def _version_out(v: Any) -> dict[str, Any]: return { "id": v.id, "project_id": v.project_id, "version": v.version, "payload": v.payload, "status": v.status, "submitted_by": v.submitted_by, "reviewed_by": v.reviewed_by, "created_at": v.created_at.isoformat(), } @router.post("", status_code=201) async def create_workspace( db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, body: dict[str, str], ) -> dict[str, Any]: domain = await _domain_of(db, user) ws = await service.create_workspace( db, domain.id, body.get("name", ""), getattr(request.state, "trace_id", None) ) return {"id": ws.id, "name": ws.name, "owner_domain_id": ws.owner_domain_id} @router.post("/{workspace_id}/invitations", status_code=201) async def invite( workspace_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, body: dict[str, str], ) -> dict[str, Any]: domain = await _domain_of(db, user) invitee = await db.get(PersonalDomain, body.get("invitee_domain_id", "")) if invitee is None: raise HTTPException(status.HTTP_404_NOT_FOUND, "被邀用户不存在") invitation = await service.invite( db, domain.id, workspace_id, invitee.id, body.get("role", "editor"), getattr(request.state, "trace_id", None), ) return {"id": invitation.id, "status": invitation.status, "role": invitation.role} @router.post("/invitations/{invitation_id}/acceptances", status_code=201) async def accept( invitation_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, ) -> dict[str, Any]: domain = await _domain_of(db, user) member = await service.accept_invitation( db, domain.id, invitation_id, getattr(request.state, "trace_id", None) ) return _member_out(member) @router.get("/{workspace_id}/members") async def list_members( workspace_id: str, db: DbSession, user: Annotated[User, Depends(get_current_user)], ) -> dict[str, Any]: domain = await _domain_of(db, user) rows = await service.list_members(db, domain.id, workspace_id) return {"data": [_member_out(m) for m in rows]} @router.delete("/{workspace_id}/members/{member_id}", status_code=204) async def remove_member( workspace_id: str, member_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, ) -> None: domain = await _domain_of(db, user) await service.remove_member( db, domain.id, workspace_id, member_id, getattr(request.state, "trace_id", None) ) @router.post("/{workspace_id}/projects", status_code=201) async def create_project( workspace_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, body: dict[str, str], ) -> dict[str, Any]: domain = await _domain_of(db, user) project = await service.create_project( db, domain.id, workspace_id, body.get("name", ""), getattr(request.state, "trace_id", None), ) return _project_out(project) @router.post("/projects/{project_id}/versions", status_code=201) async def submit_version( project_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, body: dict[str, Any], ) -> dict[str, Any]: domain = await _domain_of(db, user) version = await service.submit_version( db, domain.id, project_id, body.get("payload", {}), getattr(request.state, "trace_id", None), ) return _version_out(version) @router.post("/project-versions/{version_id}/reviews", status_code=201) async def review_version( version_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, body: dict[str, bool], ) -> dict[str, Any]: domain = await _domain_of(db, user) version = await service.review_version( db, domain.id, version_id, bool(body.get("approve")), getattr(request.state, "trace_id", None), ) return _version_out(version) @router.post("/projects/{project_id}/publications", status_code=200) async def publish( project_id: str, db: DbSession, user: Annotated[User, Depends(require_state_change)], request: Request, ) -> dict[str, Any]: domain = await _domain_of(db, user) project = await service.publish_project( db, domain.id, project_id, getattr(request.state, "trace_id", None) ) return _project_out(project)