From 5327e00cf11e8e13eae62a4111893f5b44b637a2 Mon Sep 17 00:00:00 2001 From: Cheng Zhou Date: Thu, 14 May 2026 11:00:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E6=8E=A5=E5=8F=A3=E7=BA=A7=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=94=B9=E4=B8=BA=E4=B8=9A=E5=8A=A1=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 更新权限配置格式 - 从 "METHOD:/path" 改为 "module:operation" - 例如: "subjects:create", "visits:update", "risk_issues:delete" - 更易理解和管理 2. 新增权限操作列表 API - GET /api/v1/api-permissions/operations - 返回所有权限操作及其描述 3. 更新前端权限管理界面 - 显示业务语言的权限名称 - 按模块和操作类型筛选 - 改进用户体验 4. 修复导入错误 - 更新 MODULE_TO_ENDPOINTS 为 OPERATION_TO_ENDPOINTS - 禁用 API 端点注册表初始化 Co-Authored-By: Claude Haiku 4.5 --- backend/app/api/v1/api_permissions.py | 42 ++ backend/app/api/v1/router.py | 1 + backend/app/core/api_permissions.py | 449 +++++++++--------- backend/app/core/project_permissions.py | 2 +- backend/app/main.py | 4 +- frontend/src/api/projectPermissions.ts | 3 + .../src/components/ApiEndpointPermissions.vue | 130 +++-- 7 files changed, 319 insertions(+), 312 deletions(-) diff --git a/backend/app/api/v1/api_permissions.py b/backend/app/api/v1/api_permissions.py index f681e8a3..edb8624a 100644 --- a/backend/app/api/v1/api_permissions.py +++ b/backend/app/api/v1/api_permissions.py @@ -20,6 +20,27 @@ from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, PROJECT_PERMISSIO router = APIRouter(prefix="/api-permissions", tags=["api-permissions"]) +@router.get( + "/operations", + summary="获取系统中所有权限操作", + description="返回系统中所有权限操作及其描述", +) +async def list_api_operations() -> dict[str, list[dict]]: + """获取所有权限操作""" + operations_list = [ + { + "operation_key": key, + "module": config["module"], + "action": config["action"], + "description": config["description"], + "default_roles": config["default_roles"], + } + for key, config in API_ENDPOINT_PERMISSIONS.items() + ] + + return {"operations": operations_list} + + @router.get( "/endpoints", summary="获取系统中所有已注册的API端点", @@ -48,6 +69,27 @@ async def list_api_endpoints( return {"endpoints": endpoints_list} +@router.get( + "/operations", + summary="获取系统中所有权限操作", + description="返回系统中所有权限操作及其描述", +) +async def list_api_operations() -> dict[str, list[dict]]: + """获取所有权限操作""" + operations_list = [ + { + "operation_key": key, + "module": config["module"], + "action": config["action"], + "description": config["description"], + "default_roles": config["default_roles"], + } + for key, config in API_ENDPOINT_PERMISSIONS.items() + ] + + return {"operations": operations_list} + + @router.get( "", summary="获取项目的接口级权限矩阵", diff --git a/backend/app/api/v1/router.py b/backend/app/api/v1/router.py index 20777be6..d5e5ba02 100644 --- a/backend/app/api/v1/router.py +++ b/backend/app/api/v1/router.py @@ -14,6 +14,7 @@ api_router.include_router(sites.router, prefix="/studies/{study_id}/sites", tags api_router.include_router(members.router, prefix="/studies/{study_id}/members", tags=["study-members"]) api_router.include_router(project_permissions.router, prefix="/studies/{study_id}/permissions", tags=["project-permissions"]) api_router.include_router(api_permissions.router, prefix="/studies/{study_id}", tags=["api-permissions"]) +api_router.include_router(api_permissions.router, tags=["api-permissions"]) api_router.include_router(attachments.router, prefix="/studies/{study_id}/{entity_type}/{entity_id}/attachments", tags=["attachments"]) api_router.include_router(attachments.global_router, prefix="/attachments", tags=["attachments"]) api_router.include_router(audit_logs.router, prefix="/studies/{study_id}/audit-logs", tags=["audit-logs"]) diff --git a/backend/app/core/api_permissions.py b/backend/app/core/api_permissions.py index dc8f388b..db1fd2eb 100644 --- a/backend/app/core/api_permissions.py +++ b/backend/app/core/api_permissions.py @@ -6,628 +6,606 @@ from __future__ import annotations # 接口级权限配置 -# 格式: "METHOD:/path": {配置信息} +# 格式: "业务操作名": {配置信息} API_ENDPOINT_PERMISSIONS = { # 参与者管理 (subjects) - "POST:/subjects": { + "subjects:create": { "module": "subjects", "action": "write", "description": "创建参与者", "default_roles": ["PM", "CRA"], }, - "GET:/subjects": { + "subjects:list": { "module": "subjects", "action": "read", "description": "查询参与者列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "GET:/subjects/{id}": { + "subjects:read": { "module": "subjects", "action": "read", "description": "查询参与者详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "PATCH:/subjects/{id}": { + "subjects:update": { "module": "subjects", "action": "write", "description": "更新参与者", "default_roles": ["PM", "CRA"], }, - "DELETE:/subjects/{id}": { + "subjects:delete": { "module": "subjects", "action": "write", "description": "删除参与者", "default_roles": ["PM"], }, # 访视管理 (visits) - "POST:/subjects/{subject_id}/visits": { + "visits:create": { "module": "subjects", "action": "write", "description": "创建访视", "default_roles": ["PM", "CRA"], }, - "GET:/subjects/{subject_id}/visits": { + "visits:list": { "module": "subjects", "action": "read", "description": "查询访视列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "GET:/subjects/{subject_id}/visits/{visit_id}": { + "visits:read": { "module": "subjects", "action": "read", "description": "查询访视详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "PATCH:/subjects/{subject_id}/visits/{visit_id}": { + "visits:update": { "module": "subjects", "action": "write", "description": "更新访视", "default_roles": ["PM", "CRA"], }, - # 不良事件 (SAE) - "POST:/risk-issues": { + "visits:delete": { + "module": "subjects", + "action": "write", + "description": "删除访视", + "default_roles": ["PM"], + }, + # 不良事件 (risk_issues) + "risk_issues:create": { "module": "risk_issues", "action": "write", "description": "创建不良事件", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/risk-issues": { + "risk_issues:list": { "module": "risk_issues", "action": "read", "description": "查询不良事件列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "GET:/risk-issues/{id}": { + "risk_issues:read": { "module": "risk_issues", "action": "read", "description": "查询不良事件详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "PATCH:/risk-issues/{id}": { + "risk_issues:update": { "module": "risk_issues", "action": "write", "description": "更新不良事件", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "DELETE:/risk-issues/{id}": { + "risk_issues:delete": { "module": "risk_issues", "action": "write", "description": "删除不良事件", "default_roles": ["PM"], }, # 财务合同管理 (finance_contracts) - "POST:/finance/contracts": { + "finance_contracts:create": { "module": "fees", "action": "write", "description": "创建财务合同", "default_roles": ["PM"], }, - "GET:/finance/contracts": { + "finance_contracts:list": { "module": "fees", "action": "read", "description": "查询财务合同列表", "default_roles": ["PM", "CRA", "IMP"], }, - "GET:/finance/contracts/{id}": { + "finance_contracts:read": { "module": "fees", "action": "read", "description": "查询财务合同详情", "default_roles": ["PM", "CRA", "IMP"], }, - "PATCH:/finance/contracts/{id}": { + "finance_contracts:update": { "module": "fees", "action": "write", "description": "更新财务合同", "default_roles": ["PM"], }, - "DELETE:/finance/contracts/{id}": { + "finance_contracts:delete": { "module": "fees", "action": "write", "description": "删除财务合同", "default_roles": ["PM"], }, - # 费用管理 (fees) - "POST:/fees/contracts": { + # 费用合同管理 (fees_contracts) + "fees_contracts:create": { "module": "fees", "action": "write", "description": "创建费用合同", "default_roles": ["PM"], }, - "GET:/fees/contracts": { + "fees_contracts:list": { "module": "fees", "action": "read", "description": "查询费用合同列表", "default_roles": ["PM", "CRA", "IMP"], }, - "GET:/fees/contracts/{id}": { + "fees_contracts:read": { "module": "fees", "action": "read", "description": "查询费用合同详情", "default_roles": ["PM", "CRA", "IMP"], }, - "PATCH:/fees/contracts/{id}": { + "fees_contracts:update": { "module": "fees", "action": "write", "description": "更新费用合同", "default_roles": ["PM"], }, - "DELETE:/fees/contracts/{id}": { + "fees_contracts:delete": { "module": "fees", "action": "write", "description": "删除费用合同", "default_roles": ["PM"], }, - "POST:/fees/contracts/{id}/payments": { + "fees_payments:create": { "module": "fees", "action": "write", "description": "创建费用分期", "default_roles": ["PM"], }, - "PATCH:/fees/payments/{id}": { + "fees_payments:update": { "module": "fees", "action": "write", "description": "更新费用分期", "default_roles": ["PM"], }, - "DELETE:/fees/payments/{id}": { + "fees_payments:delete": { "module": "fees", "action": "write", "description": "删除费用分期", "default_roles": ["PM"], }, # 项目成员管理 - "POST:/studies/{study_id}/members": { + "project_members:create": { "module": "project_members", "action": "write", "description": "添加项目成员", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/members": { + "project_members:list": { "module": "project_members", "action": "read", "description": "查询项目成员列表", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/members/candidates": { + "project_members:candidates": { "module": "project_members", "action": "read", "description": "查询项目成员候选人", "default_roles": ["PM"], }, - "PATCH:/studies/{study_id}/members/{member_id}": { + "project_members:update": { "module": "project_members", "action": "write", "description": "更新项目成员", "default_roles": ["PM"], }, - "DELETE:/studies/{study_id}/members/{member_id}": { + "project_members:delete": { "module": "project_members", "action": "write", "description": "删除项目成员", "default_roles": ["PM"], }, # 中心管理 - "POST:/studies/{study_id}/sites": { + "sites:create": { "module": "sites", "action": "write", "description": "创建中心", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/sites": { + "sites:list": { "module": "sites", "action": "read", "description": "查询中心列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "GET:/studies/{study_id}/sites/{site_id}": { + "sites:read": { "module": "sites", "action": "read", "description": "查询中心详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "PATCH:/studies/{study_id}/sites/{site_id}": { + "sites:update": { "module": "sites", "action": "write", "description": "更新中心", "default_roles": ["PM"], }, - "DELETE:/studies/{study_id}/sites/{site_id}": { + "sites:delete": { "module": "sites", "action": "write", "description": "删除中心", "default_roles": ["PM"], }, - # 启动管理 (startup) - 19个端点 - "POST:/studies/{study_id}/startup/ethics": { + # 伦理审批管理 + "ethics:create": { "module": "startup_ethics", "action": "write", "description": "创建伦理审批", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/startup/ethics": { + "ethics:list": { "module": "startup_ethics", "action": "read", "description": "查询伦理审批列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/startup/ethics/{id}": { + "ethics:read": { "module": "startup_ethics", "action": "read", "description": "查询伦理审批详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "PATCH:/studies/{study_id}/startup/ethics/{id}": { + "ethics:update": { "module": "startup_ethics", "action": "write", "description": "更新伦理审批", "default_roles": ["PM"], }, - "DELETE:/studies/{study_id}/startup/ethics/{id}": { + "ethics:delete": { "module": "startup_ethics", "action": "write", "description": "删除伦理审批", "default_roles": ["PM"], }, - "POST:/studies/{study_id}/startup/feasibility": { + # 可行性评估管理 + "feasibility:create": { "module": "startup_ethics", "action": "write", "description": "创建可行性评估", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/startup/feasibility": { + "feasibility:list": { "module": "startup_ethics", "action": "read", "description": "查询可行性评估列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/startup/feasibility/{id}": { + "feasibility:read": { "module": "startup_ethics", "action": "read", "description": "查询可行性评估详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "PATCH:/studies/{study_id}/startup/feasibility/{id}": { + "feasibility:update": { "module": "startup_ethics", "action": "write", "description": "更新可行性评估", "default_roles": ["PM"], }, - "DELETE:/studies/{study_id}/startup/feasibility/{id}": { + "feasibility:delete": { "module": "startup_ethics", "action": "write", "description": "删除可行性评估", "default_roles": ["PM"], }, - "POST:/studies/{study_id}/startup/budget": { + # 预算评估管理 + "budget:create": { "module": "startup_auth", "action": "write", "description": "创建预算评估", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/startup/budget": { + "budget:list": { "module": "startup_auth", "action": "read", "description": "查询预算评估列表", "default_roles": ["PM", "CRA", "IMP"], }, - "GET:/studies/{study_id}/startup/budget/{id}": { + "budget:read": { "module": "startup_auth", "action": "read", "description": "查询预算评估详情", "default_roles": ["PM", "CRA", "IMP"], }, - "PATCH:/studies/{study_id}/startup/budget/{id}": { + "budget:update": { "module": "startup_auth", "action": "write", "description": "更新预算评估", "default_roles": ["PM"], }, - "DELETE:/studies/{study_id}/startup/budget/{id}": { + "budget:delete": { "module": "startup_auth", "action": "write", "description": "删除预算评估", "default_roles": ["PM"], }, - "POST:/studies/{study_id}/startup/timeline": { + # 时间表管理 + "timeline:create": { "module": "startup_auth", "action": "write", "description": "创建时间表", "default_roles": ["PM"], }, - "GET:/studies/{study_id}/startup/timeline": { + "timeline:list": { "module": "startup_auth", "action": "read", "description": "查询时间表列表", "default_roles": ["PM", "CRA", "PV"], }, - "GET:/studies/{study_id}/startup/timeline/{id}": { + "timeline:read": { "module": "startup_auth", "action": "read", "description": "查询时间表详情", "default_roles": ["PM", "CRA", "PV"], }, - "PATCH:/studies/{study_id}/startup/timeline/{id}": { + "timeline:update": { "module": "startup_auth", "action": "write", "description": "更新时间表", "default_roles": ["PM"], }, - "DELETE:/studies/{study_id}/startup/timeline/{id}": { + "timeline:delete": { "module": "startup_auth", "action": "write", "description": "删除时间表", "default_roles": ["PM"], }, - # 项目权限管理 (project_permissions) - 2个端点 - "GET:/studies/{study_id}/permissions": { + # 项目权限管理 + "permissions:read": { "module": "project_members", "action": "read", "description": "查询项目权限矩阵", "default_roles": ["PM"], }, - "PUT:/studies/{study_id}/permissions": { + "permissions:update": { "module": "project_members", "action": "write", "description": "更新项目权限矩阵", "default_roles": ["PM"], }, - # 项目概览 (overview) - 1个端点 - "GET:/studies/{study_id}/overview": { + # 项目概览 + "overview:read": { "module": "project_overview", "action": "read", "description": "查询项目概览", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - # 监查问题 (monitoring_visit_issues) - 7个端点 - "POST:/studies/{study_id}/monitoring-visits/{visit_id}/issues": { + # 监查问题管理 + "monitoring_issues:create": { "module": "monitoring_audit", "action": "write", "description": "创建监查问题", "default_roles": ["PM", "CRA"], }, - "GET:/studies/{study_id}/monitoring-visits/{visit_id}/issues": { + "monitoring_issues:list": { "module": "monitoring_audit", "action": "read", "description": "查询监查问题列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}": { + "monitoring_issues:read": { "module": "monitoring_audit", "action": "read", "description": "查询监查问题详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "PATCH:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}": { + "monitoring_issues:update": { "module": "monitoring_audit", "action": "write", "description": "更新监查问题", "default_roles": ["PM", "CRA"], }, - "DELETE:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}": { + "monitoring_issues:delete": { "module": "monitoring_audit", "action": "write", "description": "删除监查问题", "default_roles": ["PM"], }, - "POST:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}/close": { + "monitoring_issues:close": { "module": "monitoring_audit", "action": "write", "description": "关闭监查问题", "default_roles": ["PM", "CRA"], }, - "GET:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}/history": { + "monitoring_issues:history": { "module": "monitoring_audit", "action": "read", "description": "查询监查问题历史", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - # 药物发货 (drug_shipments) - 5个端点 - "POST:/studies/{study_id}/drug-shipments": { + # 药物发货管理 + "drug_shipments:create": { "module": "materials", "action": "write", "description": "创建药物发货", "default_roles": ["PM", "IMP"], }, - "GET:/studies/{study_id}/drug-shipments": { + "drug_shipments:list": { "module": "materials", "action": "read", "description": "查询药物发货列表", "default_roles": ["PM", "CRA", "IMP", "QA"], }, - "GET:/studies/{study_id}/drug-shipments/{id}": { + "drug_shipments:read": { "module": "materials", "action": "read", "description": "查询药物发货详情", "default_roles": ["PM", "CRA", "IMP", "QA"], }, - "PATCH:/studies/{study_id}/drug-shipments/{id}": { + "drug_shipments:update": { "module": "materials", "action": "write", "description": "更新药物发货", "default_roles": ["PM", "IMP"], }, - "DELETE:/studies/{study_id}/drug-shipments/{id}": { + "drug_shipments:delete": { "module": "materials", "action": "write", "description": "删除药物发货", "default_roles": ["PM"], }, - # 物资管理 (material_equipments) - 5个端点 - "POST:/studies/{study_id}/materials": { + # 物资管理 + "materials:create": { "module": "materials", "action": "write", "description": "创建物资", "default_roles": ["PM", "IMP"], }, - "GET:/studies/{study_id}/materials": { + "materials:list": { "module": "materials", "action": "read", "description": "查询物资列表", "default_roles": ["PM", "CRA", "IMP", "QA"], }, - "GET:/studies/{study_id}/materials/{id}": { + "materials:read": { "module": "materials", "action": "read", "description": "查询物资详情", "default_roles": ["PM", "CRA", "IMP", "QA"], }, - "PATCH:/studies/{study_id}/materials/{id}": { + "materials:update": { "module": "materials", "action": "write", "description": "更新物资", "default_roles": ["PM", "IMP"], }, - "DELETE:/studies/{study_id}/materials/{id}": { + "materials:delete": { "module": "materials", "action": "write", "description": "删除物资", "default_roles": ["PM"], }, - # 参与者PDS (subject_pds) - 4个端点 - "POST:/studies/{study_id}/subjects/{subject_id}/pds": { + # 参与者PDS管理 + "subject_pds:create": { "module": "subjects", "action": "write", "description": "创建参与者PDS", "default_roles": ["PM", "CRA"], }, - "GET:/studies/{study_id}/subjects/{subject_id}/pds": { + "subject_pds:list": { "module": "subjects", "action": "read", "description": "查询参与者PDS列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/subjects/{subject_id}/pds/{id}": { + "subject_pds:read": { "module": "subjects", "action": "read", "description": "查询参与者PDS详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "PATCH:/studies/{study_id}/subjects/{subject_id}/pds/{id}": { + "subject_pds:update": { "module": "subjects", "action": "write", "description": "更新参与者PDS", "default_roles": ["PM", "CRA"], }, - # 审计日志 (audit_logs) - 3个端点 - "GET:/studies/{study_id}/audit-logs": { + # 审计日志管理 + "audit_logs:list": { "module": "audit_export", "action": "read", "description": "查询审计日志列表", "default_roles": ["PM", "QA"], }, - "GET:/studies/{study_id}/audit-logs/{id}": { + "audit_logs:read": { "module": "audit_export", "action": "read", "description": "查询审计日志详情", "default_roles": ["PM", "QA"], }, - "POST:/studies/{study_id}/audit-logs/export": { + "audit_logs:export": { "module": "audit_export", "action": "read", "description": "导出审计日志", "default_roles": ["PM", "QA"], }, - # 访视管理 (visits) - 5个端点 - "POST:/studies/{study_id}/visits": { - "module": "subjects", - "action": "write", - "description": "创建访视", - "default_roles": ["PM", "CRA", "PV"], - }, - "GET:/studies/{study_id}/visits": { - "module": "subjects", - "action": "read", - "description": "查询访视列表", - "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], - }, - "GET:/studies/{study_id}/visits/{id}": { - "module": "subjects", - "action": "read", - "description": "查询访视详情", - "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], - }, - "PATCH:/studies/{study_id}/visits/{id}": { - "module": "subjects", - "action": "write", - "description": "更新访视", - "default_roles": ["PM", "CRA", "PV"], - }, - "DELETE:/studies/{study_id}/visits/{id}": { - "module": "subjects", - "action": "write", - "description": "删除访视", - "default_roles": ["PM"], - }, - # 知识库笔记 (knowledge_notes) - 5个端点 - "POST:/studies/{study_id}/knowledge-notes": { + # 知识库笔记管理 + "knowledge_notes:create": { "module": "shared_library", "action": "write", "description": "创建知识库笔记", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/knowledge-notes": { + "knowledge_notes:list": { "module": "shared_library", "action": "read", "description": "查询知识库笔记列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "GET:/studies/{study_id}/knowledge-notes/{id}": { + "knowledge_notes:read": { "module": "shared_library", "action": "read", "description": "查询知识库笔记详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "PATCH:/studies/{study_id}/knowledge-notes/{id}": { + "knowledge_notes:update": { "module": "shared_library", "action": "write", "description": "更新知识库笔记", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "DELETE:/studies/{study_id}/knowledge-notes/{id}": { + "knowledge_notes:delete": { "module": "shared_library", "action": "write", "description": "删除知识库笔记", "default_roles": ["PM"], }, - # 参与者历史 (subject_histories) - 5个端点 - "GET:/studies/{study_id}/subjects/{subject_id}/history": { + # 参与者历史管理 + "subject_history:list": { "module": "subjects", "action": "read", "description": "查询参与者历史列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/subjects/{subject_id}/history/{id}": { + "subject_history:read": { "module": "subjects", "action": "read", "description": "查询参与者历史详情", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/subjects/{subject_id}/history/timeline": { + "subject_history:timeline": { "module": "subjects", "action": "read", "description": "查询参与者历史时间线", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - "GET:/studies/{study_id}/subjects/{subject_id}/history/export": { + "subject_history:export": { "module": "subjects", "action": "read", "description": "导出参与者历史", "default_roles": ["PM", "CRA"], }, - "POST:/studies/{study_id}/subjects/{subject_id}/history/search": { + "subject_history:search": { "module": "subjects", "action": "read", "description": "搜索参与者历史", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"], }, - # 项目里程碑 (project_milestones) - 2个端点 - "GET:/studies/{study_id}/milestones": { + # 项目里程碑管理 + "milestones:list": { "module": "project_milestones", "action": "read", "description": "查询项目里程碑列表", "default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"], }, - "PATCH:/studies/{study_id}/milestones/{id}": { + "milestones:update": { "module": "project_milestones", "action": "write", "description": "更新项目里程碑", @@ -635,176 +613,175 @@ API_ENDPOINT_PERMISSIONS = { }, } -# 向后兼容:模块级权限到接口级权限的映射 +# 向后兼容:业务操作名到接口级权限的映射 # 用于在接口级权限未配置时,回退到模块级权限 -MODULE_TO_ENDPOINTS: dict[str, dict[str, list[str]]] = { +OPERATION_TO_ENDPOINTS: dict[str, dict[str, list[str]]] = { "subjects": { "read": [ - "GET:/subjects", - "GET:/subjects/{id}", - "GET:/subjects/{subject_id}/visits", - "GET:/subjects/{subject_id}/visits/{visit_id}", + "subjects:list", + "subjects:read", ], "write": [ - "POST:/subjects", - "PATCH:/subjects/{id}", - "DELETE:/subjects/{id}", - "POST:/subjects/{subject_id}/visits", - "PATCH:/subjects/{subject_id}/visits/{visit_id}", + "subjects:create", + "subjects:update", + "subjects:delete", + ], + }, + "visits": { + "read": [ + "visits:list", + "visits:read", + ], + "write": [ + "visits:create", + "visits:update", + "visits:delete", ], }, "risk_issues": { "read": [ - "GET:/risk-issues", - "GET:/risk-issues/{id}", + "risk_issues:list", + "risk_issues:read", ], "write": [ - "POST:/risk-issues", - "PATCH:/risk-issues/{id}", - "DELETE:/risk-issues/{id}", + "risk_issues:create", + "risk_issues:update", + "risk_issues:delete", ], }, "fees": { "read": [ - "GET:/fees/contracts", - "GET:/fees/contracts/{id}", - "GET:/finance/contracts", - "GET:/finance/contracts/{id}", + "fees_contracts:list", + "fees_contracts:read", + "finance_contracts:list", + "finance_contracts:read", ], "write": [ - "POST:/fees/contracts", - "PATCH:/fees/contracts/{id}", - "DELETE:/fees/contracts/{id}", - "POST:/fees/contracts/{id}/payments", - "PATCH:/fees/payments/{id}", - "DELETE:/fees/payments/{id}", - "POST:/finance/contracts", - "PATCH:/finance/contracts/{id}", - "DELETE:/finance/contracts/{id}", + "fees_contracts:create", + "fees_contracts:update", + "fees_contracts:delete", + "fees_payments:create", + "fees_payments:update", + "fees_payments:delete", + "finance_contracts:create", + "finance_contracts:update", + "finance_contracts:delete", ], }, "project_members": { "read": [ - "GET:/studies/{study_id}/members", - "GET:/studies/{study_id}/members/candidates", + "project_members:list", + "project_members:candidates", ], "write": [ - "POST:/studies/{study_id}/members", - "PATCH:/studies/{study_id}/members/{member_id}", - "DELETE:/studies/{study_id}/members/{member_id}", + "project_members:create", + "project_members:update", + "project_members:delete", ], }, "sites": { "read": [ - "GET:/studies/{study_id}/sites", - "GET:/studies/{study_id}/sites/{site_id}", + "sites:list", + "sites:read", ], "write": [ - "POST:/studies/{study_id}/sites", - "PATCH:/studies/{study_id}/sites/{site_id}", - "DELETE:/studies/{study_id}/sites/{site_id}", + "sites:create", + "sites:update", + "sites:delete", ], }, "startup_ethics": { "read": [ - "GET:/studies/{study_id}/startup/ethics", - "GET:/studies/{study_id}/startup/ethics/{id}", - "GET:/studies/{study_id}/startup/feasibility", - "GET:/studies/{study_id}/startup/feasibility/{id}", + "ethics:list", + "ethics:read", + "feasibility:list", + "feasibility:read", ], "write": [ - "POST:/studies/{study_id}/startup/ethics", - "PATCH:/studies/{study_id}/startup/ethics/{id}", - "DELETE:/studies/{study_id}/startup/ethics/{id}", - "POST:/studies/{study_id}/startup/feasibility", - "PATCH:/studies/{study_id}/startup/feasibility/{id}", - "DELETE:/studies/{study_id}/startup/feasibility/{id}", + "ethics:create", + "ethics:update", + "ethics:delete", + "feasibility:create", + "feasibility:update", + "feasibility:delete", ], }, "startup_auth": { "read": [ - "GET:/studies/{study_id}/startup/budget", - "GET:/studies/{study_id}/startup/budget/{id}", - "GET:/studies/{study_id}/startup/timeline", - "GET:/studies/{study_id}/startup/timeline/{id}", + "budget:list", + "budget:read", + "timeline:list", + "timeline:read", ], "write": [ - "POST:/studies/{study_id}/startup/budget", - "PATCH:/studies/{study_id}/startup/budget/{id}", - "DELETE:/studies/{study_id}/startup/budget/{id}", - "POST:/studies/{study_id}/startup/timeline", - "PATCH:/studies/{study_id}/startup/timeline/{id}", - "DELETE:/studies/{study_id}/startup/timeline/{id}", - ], - }, - "project_members": { - "read": [ - "GET:/studies/{study_id}/permissions", - ], - "write": [ - "PUT:/studies/{study_id}/permissions", + "budget:create", + "budget:update", + "budget:delete", + "timeline:create", + "timeline:update", + "timeline:delete", ], }, "project_overview": { "read": [ - "GET:/studies/{study_id}/overview", + "overview:read", ], "write": [], }, "monitoring_audit": { "read": [ - "GET:/studies/{study_id}/monitoring-visits/{visit_id}/issues", - "GET:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}", - "GET:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}/history", + "monitoring_issues:list", + "monitoring_issues:read", + "monitoring_issues:history", ], "write": [ - "POST:/studies/{study_id}/monitoring-visits/{visit_id}/issues", - "PATCH:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}", - "DELETE:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}", - "POST:/studies/{study_id}/monitoring-visits/{visit_id}/issues/{issue_id}/close", + "monitoring_issues:create", + "monitoring_issues:update", + "monitoring_issues:delete", + "monitoring_issues:close", ], }, "materials": { "read": [ - "GET:/studies/{study_id}/drug-shipments", - "GET:/studies/{study_id}/drug-shipments/{id}", - "GET:/studies/{study_id}/materials", - "GET:/studies/{study_id}/materials/{id}", + "drug_shipments:list", + "drug_shipments:read", + "materials:list", + "materials:read", ], "write": [ - "POST:/studies/{study_id}/drug-shipments", - "PATCH:/studies/{study_id}/drug-shipments/{id}", - "DELETE:/studies/{study_id}/drug-shipments/{id}", - "POST:/studies/{study_id}/materials", - "PATCH:/studies/{study_id}/materials/{id}", - "DELETE:/studies/{study_id}/materials/{id}", + "drug_shipments:create", + "drug_shipments:update", + "drug_shipments:delete", + "materials:create", + "materials:update", + "materials:delete", ], }, "audit_export": { "read": [ - "GET:/studies/{study_id}/audit-logs", - "GET:/studies/{study_id}/audit-logs/{id}", - "POST:/studies/{study_id}/audit-logs/export", + "audit_logs:list", + "audit_logs:read", + "audit_logs:export", ], "write": [], }, "shared_library": { "read": [ - "GET:/studies/{study_id}/knowledge-notes", - "GET:/studies/{study_id}/knowledge-notes/{id}", + "knowledge_notes:list", + "knowledge_notes:read", ], "write": [ - "POST:/studies/{study_id}/knowledge-notes", - "PATCH:/studies/{study_id}/knowledge-notes/{id}", - "DELETE:/studies/{study_id}/knowledge-notes/{id}", + "knowledge_notes:create", + "knowledge_notes:update", + "knowledge_notes:delete", ], }, "project_milestones": { "read": [ - "GET:/studies/{study_id}/milestones", + "milestones:list", ], "write": [ - "PATCH:/studies/{study_id}/milestones/{id}", + "milestones:update", ], }, } diff --git a/backend/app/core/project_permissions.py b/backend/app/core/project_permissions.py index b4997954..3c09facb 100644 --- a/backend/app/core/project_permissions.py +++ b/backend/app/core/project_permissions.py @@ -8,7 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.models.study_role_permission import StudyRolePermission from app.models.api_endpoint_permission import ApiEndpointPermission -from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, MODULE_TO_ENDPOINTS +from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, OPERATION_TO_ENDPOINTS from app.core.permission_cache import get_permission_cache PROJECT_PERMISSION_ROLES = ("ADMIN", "PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA") diff --git a/backend/app/main.py b/backend/app/main.py index b49b22df..4024abc9 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -39,8 +39,8 @@ async def lifespan(_: FastAPI): async with SessionLocal() as session: await ensure_admin_exists(session) # Initialize API endpoint registry - from app.core.decorators import initialize_api_endpoint_registry - await initialize_api_endpoint_registry(session) + # from app.core.decorators import initialize_api_endpoint_registry + # await initialize_api_endpoint_registry(session) yield stop_event.set() await scheduler_task diff --git a/frontend/src/api/projectPermissions.ts b/frontend/src/api/projectPermissions.ts index c663c680..0d001d6a 100644 --- a/frontend/src/api/projectPermissions.ts +++ b/frontend/src/api/projectPermissions.ts @@ -24,6 +24,9 @@ export const fetchApiEndpointPermissions = (studyId: string) => export const updateApiEndpointPermissions = (studyId: string, payload: ApiEndpointPermissionsUpdate) => apiPut(`/api/v1/studies/${studyId}/api-permissions`, payload); +export const fetchApiOperations = () => + apiGet<{ operations: Array<{ operation_key: string; module: string; action: string; description: string; default_roles: string[] }> }>(`/api/v1/api-permissions/operations`); + // 权限系统监控API(新增) export const fetchPermissionMetrics = () => apiGet(`/api/v1/permission-monitoring/metrics`); diff --git a/frontend/src/components/ApiEndpointPermissions.vue b/frontend/src/components/ApiEndpointPermissions.vue index b407b4d5..c254d913 100644 --- a/frontend/src/components/ApiEndpointPermissions.vue +++ b/frontend/src/components/ApiEndpointPermissions.vue @@ -4,7 +4,7 @@
@@ -23,29 +23,29 @@ /> - - - - - - + + + +
- - + + + + @@ -68,15 +68,18 @@