完善审计日志展示与导出权限

This commit is contained in:
Cheng Zhou
2026-06-10 10:20:04 +08:00
parent 1fef12cd86
commit ea3f19e241
33 changed files with 1862 additions and 240 deletions
+50 -11
View File
@@ -1,4 +1,5 @@
import uuid
import json
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
@@ -49,6 +50,41 @@ async def _ensure_site_active(db: AsyncSession, site_id: uuid.UUID | None):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="中心已停用")
async def _site_label(db: AsyncSession, site_id: uuid.UUID | None, fallback: str = "项目级") -> str:
if not site_id:
return fallback
site = await site_crud.get_site(db, site_id)
return site.name if site else fallback
async def _feasibility_audit_detail(db: AsyncSession, action: str, record) -> str:
site_name = await _site_label(db, record.site_id)
project_no = str(record.project_no or "").strip()
name = f"{site_name} / {project_no}" if project_no else site_name
return json.dumps({"targetName": name, "description": f"{action}立项记录“{name}"}, ensure_ascii=False)
async def _ethics_audit_detail(db: AsyncSession, action: str, record) -> str:
site_name = await _site_label(db, record.site_id)
approval_no = str(record.approval_no or "").strip()
name = f"{site_name} / {approval_no}" if approval_no else site_name
return json.dumps({"targetName": name, "description": f"{action}伦理记录“{name}"}, ensure_ascii=False)
async def _kickoff_audit_detail(db: AsyncSession, action: str, meeting) -> str:
site_name = await _site_label(db, meeting.site_id)
date_text = meeting.kickoff_date.isoformat() if meeting.kickoff_date else ""
name = f"{site_name} / {date_text}" if date_text else site_name
return json.dumps({"targetName": name, "description": f"{action}启动会记录“{name}"}, ensure_ascii=False)
def _training_audit_detail(action: str, record) -> str:
name = str(record.name or "").strip() or "培训授权人员"
site_name = str(record.site_name or "").strip()
target_name = f"{name} / {site_name}" if site_name else name
return json.dumps({"targetName": target_name, "description": f"{action}培训授权人员“{target_name}"}, ensure_ascii=False)
@router.post(
"/feasibility",
response_model=StartupFeasibilityRead,
@@ -73,7 +109,7 @@ async def create_feasibility(
entity_type="startup_feasibility",
entity_id=record.id,
action="CREATE_STARTUP_FEASIBILITY",
detail="立项记录已创建",
detail=await _feasibility_audit_detail(db, "创建", record),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -147,7 +183,7 @@ async def update_feasibility(
entity_type="startup_feasibility",
entity_id=record_id,
action="UPDATE_STARTUP_FEASIBILITY",
detail="立项记录已更新",
detail=await _feasibility_audit_detail(db, "更新", record),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -173,6 +209,7 @@ async def delete_feasibility(
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_detail = await _feasibility_audit_detail(db, "删除", record)
await startup_crud.delete_feasibility(db, record)
await audit_crud.log_action(
db,
@@ -180,7 +217,7 @@ async def delete_feasibility(
entity_type="startup_feasibility",
entity_id=record_id,
action="DELETE_STARTUP_FEASIBILITY",
detail="立项记录已删除",
detail=record_detail,
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -210,7 +247,7 @@ async def create_ethics(
entity_type="startup_ethics",
entity_id=record.id,
action="CREATE_STARTUP_ETHICS",
detail="伦理记录已创建",
detail=await _ethics_audit_detail(db, "创建", record),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -284,7 +321,7 @@ async def update_ethics(
entity_type="startup_ethics",
entity_id=record_id,
action="UPDATE_STARTUP_ETHICS",
detail="伦理记录已更新",
detail=await _ethics_audit_detail(db, "更新", record),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -310,6 +347,7 @@ async def delete_ethics(
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_detail = await _ethics_audit_detail(db, "删除", record)
await startup_crud.delete_ethics(db, record)
await audit_crud.log_action(
db,
@@ -317,7 +355,7 @@ async def delete_ethics(
entity_type="startup_ethics",
entity_id=record_id,
action="DELETE_STARTUP_ETHICS",
detail="伦理记录已删除",
detail=record_detail,
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -347,7 +385,7 @@ async def create_kickoff(
entity_type="startup_kickoff",
entity_id=meeting.id,
action="CREATE_KICKOFF_MEETING",
detail="启动会记录已创建",
detail=await _kickoff_audit_detail(db, "创建", meeting),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -421,7 +459,7 @@ async def update_kickoff(
entity_type="startup_kickoff",
entity_id=meeting_id,
action="UPDATE_KICKOFF_MEETING",
detail="启动会记录已更新",
detail=await _kickoff_audit_detail(db, "更新", meeting),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -452,7 +490,7 @@ async def create_training_authorization(
entity_type="training_authorization",
entity_id=record.id,
action="CREATE_TRAINING_AUTH",
detail="培训授权人员已创建",
detail=_training_audit_detail("创建", record),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -527,7 +565,7 @@ async def update_training_authorization(
entity_type="training_authorization",
entity_id=record_id,
action="UPDATE_TRAINING_AUTH",
detail="培训授权人员已更新",
detail=_training_audit_detail("更新", record),
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)
@@ -553,6 +591,7 @@ async def delete_training_authorization(
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_detail = _training_audit_detail("删除", record)
await startup_crud.delete_training_authorization(db, record)
await audit_crud.log_action(
db,
@@ -560,7 +599,7 @@ async def delete_training_authorization(
entity_type="training_authorization",
entity_id=record_id,
action="DELETE_TRAINING_AUTH",
detail="培训授权人员已删除",
detail=record_detail,
operator_id=current_user.id,
operator_role=await get_operator_role_label(db, study_id, current_user),
)