import uuid from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_operator_role_label, get_current_user, get_db_session, require_roles, require_api_permission from app.crud import audit as audit_crud from app.schemas.audit import AuditEventCreate, AuditLogRead router = APIRouter() @router.get( "/", response_model=list[AuditLogRead], dependencies=[Depends(require_api_permission("audit_logs:read"))], ) async def list_audit_logs( study_id: uuid.UUID, entity_type: str | None = None, entity_id: uuid.UUID | None = None, action: str | None = None, operator_id: uuid.UUID | None = None, skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_db_session), ) -> list[AuditLogRead]: logs = await audit_crud.list_logs( db, study_id=study_id, entity_type=entity_type, entity_id=entity_id, action=action, operator_id=operator_id, skip=skip, limit=limit, ) return list(logs) @router.delete( "/{log_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(require_roles(["ADMIN"]))], ) async def delete_audit_log( study_id: uuid.UUID, log_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), ): log = await audit_crud.get_log(db, log_id) if not log or log.study_id != study_id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="审计日志不存在") await audit_crud.delete_log(db, log) @router.post( "/events", status_code=status.HTTP_201_CREATED, ) async def create_audit_event( study_id: uuid.UUID, payload: AuditEventCreate, _export_permission=Depends(require_api_permission("audit_logs:export")), db: AsyncSession = Depends(get_db_session), current_user=Depends(get_current_user), ): allowed_actions = {"AUDIT_EXPORT_SYSTEM", "AUDIT_EXPORT_PROJECT"} if payload.action not in allowed_actions: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="不支持的审计事件") await audit_crud.log_action( db, study_id=study_id, entity_type=payload.entity_type or "audit_log", entity_id=payload.entity_id, action=payload.action, detail=payload.detail, operator_id=current_user.id, operator_role=await get_operator_role_label(db, study_id, current_user), ) return {"ok": True}