Files
ctms/backend/app/api/v1/documents.py
T
2026-01-14 11:35:37 +08:00

215 lines
7.9 KiB
Python

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)