import uuid from typing import Optional from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, UploadFile, status from fastapi.responses import FileResponse from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_current_user, get_db_session from app.crud import document_version as version_crud from app.schemas.acknowledgement import AcknowledgementCreate, AcknowledgementRead from app.schemas.common import PaginatedResponse from app.schemas.distribution import DistributionCreate, DistributionRead from app.schemas.document import DocumentCreate, DocumentDetail, DocumentSummary from app.schemas.document_version import DocumentVersionRead, VersionActionRequest, VersionSubmitRequest from app.schemas.workflow_template import WorkflowTemplateRead from app.services import document_service from app.utils.pagination import paginate router = APIRouter() @router.post( "/documents", response_model=DocumentSummary, status_code=status.HTTP_201_CREATED, ) async def create_document( payload: DocumentCreate, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentSummary: doc = await document_service.create_document(db, payload, current_user) return DocumentSummary.model_validate(doc) @router.get("/documents", response_model=PaginatedResponse[DocumentSummary]) async def list_documents( trial_id: uuid.UUID = Query(...), site_id: Optional[uuid.UUID] = Query(None), doc_type: Optional[str] = Query(None), scope_type: Optional[str] = Query(None), status_value: Optional[str] = Query(None, alias="status"), skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> PaginatedResponse[DocumentSummary]: items = await document_service.list_documents( db, trial_id=trial_id, site_id=site_id, doc_type=doc_type, status=status_value, scope_type=scope_type, skip=skip, limit=limit, current_user=current_user, ) return paginate(items, total=len(items)) @router.get("/documents/{document_id}", response_model=DocumentDetail) async def get_document_detail( document_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentDetail: return await document_service.get_document_detail(db, document_id, current_user) @router.delete("/documents/{document_id}", response_model=DocumentSummary) async def delete_document( document_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentSummary: doc = await document_service.delete_document(db, document_id, current_user) return DocumentSummary.model_validate(doc) @router.post( "/documents/{document_id}/versions", response_model=DocumentVersionRead, status_code=status.HTTP_201_CREATED, ) async def create_version( document_id: uuid.UUID, version_no: str = Form(...), change_summary: Optional[str] = Form(None), parent_version_id: Optional[uuid.UUID] = Form(None), file: UploadFile = File(...), db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentVersionRead: version = await document_service.create_version( db, document_id, version_no=version_no, change_summary=change_summary, parent_version_id=parent_version_id, file=file, current_user=current_user, ) return DocumentVersionRead.model_validate(version) @router.post("/versions/{version_id}/submit", response_model=DocumentVersionRead) async def submit_version( version_id: uuid.UUID, payload: VersionSubmitRequest, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentVersionRead: await document_service.submit_version(db, version_id, payload.template_id, payload.comment, current_user) version = await version_crud.get(db, version_id) if not version: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="版本不存在") return DocumentVersionRead.model_validate(version) @router.post("/versions/{version_id}/approve", response_model=DocumentVersionRead) async def approve_version( version_id: uuid.UUID, payload: VersionActionRequest, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentVersionRead: await document_service.approve_version(db, version_id, payload.comment, current_user) version = await version_crud.get(db, version_id) if not version: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="版本不存在") return DocumentVersionRead.model_validate(version) @router.post("/versions/{version_id}/reject", response_model=DocumentVersionRead) async def reject_version( version_id: uuid.UUID, payload: VersionActionRequest, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentVersionRead: await document_service.reject_version(db, version_id, payload.comment, current_user) version = await version_crud.get(db, version_id) if not version: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="版本不存在") return DocumentVersionRead.model_validate(version) @router.post("/versions/{version_id}/make-effective", response_model=DocumentVersionRead) async def make_version_effective( version_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> DocumentVersionRead: version = await document_service.make_version_effective(db, version_id, current_user) return DocumentVersionRead.model_validate(version) @router.get("/versions/{version_id}/download", response_class=FileResponse) async def download_version( version_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ): return await document_service.get_version_download_response(db, version_id, current_user) @router.get("/workflow-templates", response_model=list[WorkflowTemplateRead]) async def list_workflow_templates( trial_id: uuid.UUID | None = None, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> list[WorkflowTemplateRead]: templates = await document_service.list_workflow_templates(db, trial_id, current_user) return [WorkflowTemplateRead.model_validate(t) for t in templates] @router.post( "/versions/{version_id}/distributions", response_model=list[DistributionRead], status_code=status.HTTP_201_CREATED, ) async def create_distributions( version_id: uuid.UUID, payload: DistributionCreate, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> list[DistributionRead]: distributions = await document_service.create_distributions(db, version_id, payload, current_user) return [DistributionRead.model_validate(d) for d in distributions] @router.get("/versions/{version_id}/distributions", response_model=list[DistributionRead]) async def list_distributions( version_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> list[DistributionRead]: return await document_service.list_distributions(db, version_id, current_user) @router.post( "/distributions/{distribution_id}/acknowledgements", response_model=AcknowledgementRead, status_code=status.HTTP_201_CREATED, ) async def create_acknowledgement( distribution_id: uuid.UUID, payload: AcknowledgementCreate, db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ) -> AcknowledgementRead: ack = await document_service.create_acknowledgement(db, distribution_id, payload, current_user) return AcknowledgementRead.model_validate(ack)