Files
ctms/backend/app/api/v1/documents.py
T
2026-02-04 10:52:34 +08:00

169 lines
6.0 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.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
from app.models.user import UserRole
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:
role_value = current_user.role.value if hasattr(current_user.role, "value") else current_user.role
if role_value != UserRole.ADMIN.value:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="仅管理员可删除文档")
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(...),
version_date: 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,
version_date=version_date,
change_summary=change_summary,
parent_version_id=parent_version_id,
file=file,
current_user=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.delete("/versions/{version_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_version(
version_id: uuid.UUID,
db: AsyncSession = Depends(get_db_session),
current_user=Depends(get_current_user),
) -> None:
role_value = current_user.role.value if hasattr(current_user.role, "value") else current_user.role
if role_value != UserRole.ADMIN.value:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="仅管理员可删除版本")
await document_service.delete_version(db, version_id, current_user)
@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)