566 lines
21 KiB
Python
566 lines
21 KiB
Python
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.deps import get_cra_site_scope, get_current_user, get_db_session, require_study_member
|
|
from app.crud import audit as audit_crud
|
|
from app.crud import site as site_crud
|
|
from app.crud import startup as startup_crud
|
|
from app.crud import study as study_crud
|
|
from app.schemas.startup import (
|
|
KickoffMeetingCreate,
|
|
KickoffMeetingRead,
|
|
KickoffMeetingUpdate,
|
|
StartupEthicsCreate,
|
|
StartupEthicsRead,
|
|
StartupEthicsUpdate,
|
|
StartupFeasibilityCreate,
|
|
StartupFeasibilityRead,
|
|
StartupFeasibilityUpdate,
|
|
TrainingAuthorizationCreate,
|
|
TrainingAuthorizationRead,
|
|
TrainingAuthorizationUpdate,
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
async def _ensure_study_exists(db: AsyncSession, study_id: uuid.UUID):
|
|
study = await study_crud.get(db, study_id)
|
|
if not study:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="项目不存在")
|
|
return study
|
|
|
|
|
|
async def _ensure_site_name_active(db: AsyncSession, study_id: uuid.UUID, site_name: str | None):
|
|
if not site_name:
|
|
return
|
|
active_names = await site_crud.list_active_names(db, study_id)
|
|
if site_name not in active_names:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="中心已停用")
|
|
|
|
|
|
async def _ensure_site_active(db: AsyncSession, site_id: uuid.UUID | None):
|
|
if not site_id:
|
|
return
|
|
site = await site_crud.get_site(db, site_id)
|
|
if not site or not site.is_active:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="中心已停用")
|
|
|
|
|
|
@router.post(
|
|
"/feasibility",
|
|
response_model=StartupFeasibilityRead,
|
|
status_code=status.HTTP_201_CREATED,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def create_feasibility(
|
|
study_id: uuid.UUID,
|
|
record_in: StartupFeasibilityCreate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> StartupFeasibilityRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record_in.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, record_in.site_id)
|
|
record = await startup_crud.create_feasibility(db, study_id, record_in, created_by=current_user.id)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_feasibility",
|
|
entity_id=record.id,
|
|
action="CREATE_STARTUP_FEASIBILITY",
|
|
detail="立项记录已创建",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return StartupFeasibilityRead.model_validate(record)
|
|
|
|
|
|
@router.get(
|
|
"/feasibility",
|
|
response_model=list[StartupFeasibilityRead],
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def list_feasibilities(
|
|
study_id: uuid.UUID,
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> list[StartupFeasibilityRead]:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
site_ids = cra_scope[0] if cra_scope else None
|
|
items = await startup_crud.list_feasibilities(db, study_id, skip=skip, limit=limit, site_ids=site_ids)
|
|
return [StartupFeasibilityRead.model_validate(item) for item in items]
|
|
|
|
|
|
@router.get(
|
|
"/feasibility/{record_id}",
|
|
response_model=StartupFeasibilityRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def get_feasibility(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> StartupFeasibilityRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_feasibility(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="立项记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
return StartupFeasibilityRead.model_validate(record)
|
|
|
|
|
|
@router.patch(
|
|
"/feasibility/{record_id}",
|
|
response_model=StartupFeasibilityRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def update_feasibility(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
record_in: StartupFeasibilityUpdate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> StartupFeasibilityRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_feasibility(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="立项记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, record.site_id)
|
|
record = await startup_crud.update_feasibility(db, record, record_in)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_feasibility",
|
|
entity_id=record_id,
|
|
action="UPDATE_STARTUP_FEASIBILITY",
|
|
detail="立项记录已更新",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return StartupFeasibilityRead.model_validate(record)
|
|
|
|
|
|
@router.delete(
|
|
"/feasibility/{record_id}",
|
|
status_code=status.HTTP_204_NO_CONTENT,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def delete_feasibility(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> None:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_feasibility(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="立项记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, record.site_id)
|
|
await startup_crud.delete_feasibility(db, record)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_feasibility",
|
|
entity_id=record_id,
|
|
action="DELETE_STARTUP_FEASIBILITY",
|
|
detail="立项记录已删除",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/ethics",
|
|
response_model=StartupEthicsRead,
|
|
status_code=status.HTTP_201_CREATED,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def create_ethics(
|
|
study_id: uuid.UUID,
|
|
record_in: StartupEthicsCreate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> StartupEthicsRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record_in.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, record_in.site_id)
|
|
record = await startup_crud.create_ethics(db, study_id, record_in, created_by=current_user.id)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_ethics",
|
|
entity_id=record.id,
|
|
action="CREATE_STARTUP_ETHICS",
|
|
detail="伦理记录已创建",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return StartupEthicsRead.model_validate(record)
|
|
|
|
|
|
@router.get(
|
|
"/ethics",
|
|
response_model=list[StartupEthicsRead],
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def list_ethics(
|
|
study_id: uuid.UUID,
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> list[StartupEthicsRead]:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
site_ids = cra_scope[0] if cra_scope else None
|
|
items = await startup_crud.list_ethics(db, study_id, skip=skip, limit=limit, site_ids=site_ids)
|
|
return [StartupEthicsRead.model_validate(item) for item in items]
|
|
|
|
|
|
@router.get(
|
|
"/ethics/{record_id}",
|
|
response_model=StartupEthicsRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def get_ethics(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> StartupEthicsRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_ethics(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="伦理记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
return StartupEthicsRead.model_validate(record)
|
|
|
|
|
|
@router.patch(
|
|
"/ethics/{record_id}",
|
|
response_model=StartupEthicsRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def update_ethics(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
record_in: StartupEthicsUpdate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> StartupEthicsRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_ethics(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="伦理记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, record.site_id)
|
|
record = await startup_crud.update_ethics(db, record, record_in)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_ethics",
|
|
entity_id=record_id,
|
|
action="UPDATE_STARTUP_ETHICS",
|
|
detail="伦理记录已更新",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return StartupEthicsRead.model_validate(record)
|
|
|
|
|
|
@router.delete(
|
|
"/ethics/{record_id}",
|
|
status_code=status.HTTP_204_NO_CONTENT,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def delete_ethics(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> None:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_ethics(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="伦理记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, record.site_id)
|
|
await startup_crud.delete_ethics(db, record)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_ethics",
|
|
entity_id=record_id,
|
|
action="DELETE_STARTUP_ETHICS",
|
|
detail="伦理记录已删除",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/kickoff",
|
|
response_model=KickoffMeetingRead,
|
|
status_code=status.HTTP_201_CREATED,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def create_kickoff(
|
|
study_id: uuid.UUID,
|
|
meeting_in: KickoffMeetingCreate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> KickoffMeetingRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and meeting_in.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, meeting_in.site_id)
|
|
meeting = await startup_crud.create_kickoff(db, study_id, meeting_in, created_by=current_user.id)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_kickoff",
|
|
entity_id=meeting.id,
|
|
action="CREATE_KICKOFF_MEETING",
|
|
detail="启动会记录已创建",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return KickoffMeetingRead.model_validate(meeting)
|
|
|
|
|
|
@router.get(
|
|
"/kickoff",
|
|
response_model=list[KickoffMeetingRead],
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def list_kickoffs(
|
|
study_id: uuid.UUID,
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> list[KickoffMeetingRead]:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
site_ids = cra_scope[0] if cra_scope else None
|
|
items = await startup_crud.list_kickoffs(db, study_id, skip=skip, limit=limit, site_ids=site_ids)
|
|
return [KickoffMeetingRead.model_validate(item) for item in items]
|
|
|
|
|
|
@router.get(
|
|
"/kickoff/{meeting_id}",
|
|
response_model=KickoffMeetingRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def get_kickoff(
|
|
study_id: uuid.UUID,
|
|
meeting_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> KickoffMeetingRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
meeting = await startup_crud.get_kickoff(db, meeting_id)
|
|
if not meeting or meeting.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="启动会记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and meeting.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
return KickoffMeetingRead.model_validate(meeting)
|
|
|
|
|
|
@router.patch(
|
|
"/kickoff/{meeting_id}",
|
|
response_model=KickoffMeetingRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def update_kickoff(
|
|
study_id: uuid.UUID,
|
|
meeting_id: uuid.UUID,
|
|
meeting_in: KickoffMeetingUpdate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> KickoffMeetingRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
meeting = await startup_crud.get_kickoff(db, meeting_id)
|
|
if not meeting or meeting.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="启动会记录不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and meeting.site_id not in cra_scope[0]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_active(db, meeting.site_id)
|
|
meeting = await startup_crud.update_kickoff(db, meeting, meeting_in)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="startup_kickoff",
|
|
entity_id=meeting_id,
|
|
action="UPDATE_KICKOFF_MEETING",
|
|
detail="启动会记录已更新",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return KickoffMeetingRead.model_validate(meeting)
|
|
|
|
|
|
@router.post(
|
|
"/training-authorizations",
|
|
response_model=TrainingAuthorizationRead,
|
|
status_code=status.HTTP_201_CREATED,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def create_training_authorization(
|
|
study_id: uuid.UUID,
|
|
record_in: TrainingAuthorizationCreate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> TrainingAuthorizationRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record_in.site_name not in cra_scope[1]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_name_active(db, study_id, record_in.site_name)
|
|
record = await startup_crud.create_training_authorization(db, study_id, record_in, created_by=current_user.id)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="training_authorization",
|
|
entity_id=record.id,
|
|
action="CREATE_TRAINING_AUTH",
|
|
detail="培训授权人员已创建",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return TrainingAuthorizationRead.model_validate(record)
|
|
|
|
|
|
@router.get(
|
|
"/training-authorizations",
|
|
response_model=list[TrainingAuthorizationRead],
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def list_training_authorizations(
|
|
study_id: uuid.UUID,
|
|
skip: int = 0,
|
|
limit: int = 200,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> list[TrainingAuthorizationRead]:
|
|
await _ensure_study_exists(db, study_id)
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
site_names = cra_scope[1] if cra_scope else None
|
|
items = await startup_crud.list_training_authorizations(db, study_id, skip=skip, limit=limit, site_names=site_names)
|
|
return [TrainingAuthorizationRead.model_validate(item) for item in items]
|
|
|
|
|
|
@router.get(
|
|
"/training-authorizations/{record_id}",
|
|
response_model=TrainingAuthorizationRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def get_training_authorization(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> TrainingAuthorizationRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_training_authorization(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="培训授权人员不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_name not in cra_scope[1]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
return TrainingAuthorizationRead.model_validate(record)
|
|
|
|
|
|
@router.patch(
|
|
"/training-authorizations/{record_id}",
|
|
response_model=TrainingAuthorizationRead,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def update_training_authorization(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
record_in: TrainingAuthorizationUpdate,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> TrainingAuthorizationRead:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_training_authorization(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="培训授权人员不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_name not in cra_scope[1]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_name_active(db, study_id, record.site_name)
|
|
record = await startup_crud.update_training_authorization(db, record, record_in)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="training_authorization",
|
|
entity_id=record_id,
|
|
action="UPDATE_TRAINING_AUTH",
|
|
detail="培训授权人员已更新",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|
|
return TrainingAuthorizationRead.model_validate(record)
|
|
|
|
|
|
@router.delete(
|
|
"/training-authorizations/{record_id}",
|
|
status_code=status.HTTP_204_NO_CONTENT,
|
|
dependencies=[Depends(require_study_member())],
|
|
)
|
|
async def delete_training_authorization(
|
|
study_id: uuid.UUID,
|
|
record_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db_session),
|
|
current_user=Depends(get_current_user),
|
|
) -> None:
|
|
await _ensure_study_exists(db, study_id)
|
|
record = await startup_crud.get_training_authorization(db, record_id)
|
|
if not record or record.study_id != study_id:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="培训授权人员不存在")
|
|
cra_scope = await get_cra_site_scope(db, study_id, current_user)
|
|
if cra_scope and record.site_name not in cra_scope[1]:
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="权限不足")
|
|
await _ensure_site_name_active(db, study_id, record.site_name)
|
|
await startup_crud.delete_training_authorization(db, record)
|
|
await audit_crud.log_action(
|
|
db,
|
|
study_id=study_id,
|
|
entity_type="training_authorization",
|
|
entity_id=record_id,
|
|
action="DELETE_TRAINING_AUTH",
|
|
detail="培训授权人员已删除",
|
|
operator_id=current_user.id,
|
|
operator_role=current_user.role,
|
|
)
|