权限监控与管理界面全面美化

重新设计权限系统所有 UI 组件的视觉风格,统一配色、圆角、阴影和交互动效:
- 实时概览:统计卡片加图标和渐变色条,健康评分改为环形进度,告警改为卡片式
- 趋势分析:图表卡片加彩色图标标识,ECharts 配色升级为渐变面积填充
- 访问日志:指标卡片带图标,日志审计改为卡片式入口,IP排行前三高亮
- IP属地:工具栏重设计,排行列表前三渐变高亮,指标卡片统一新风格
- 系统级权限:从 el-table 改为自定义卡片列表,模块块独立圆角卡片
- 项目权限配置:空状态引导优化,成员表格加头像,工具栏加背景容器
- 角色概览卡片:加进度条可视化,hover 微动效
- 接口权限矩阵:工具栏分离布局,表格圆角包裹
- 角色管理抽屉:侧边栏选中态渐变,操作行 hover 高亮

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Cheng Zhou
2026-05-20 14:36:35 +08:00
parent e95eeed90e
commit 6cefa620e4
68 changed files with 6927 additions and 984 deletions
+112
View File
@@ -2,13 +2,19 @@
import pytest
import uuid
from pathlib import Path
import re
from sqlalchemy import delete
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, PROJECT_PERMISSION_ROLES
from app.core.project_permissions import role_has_api_permission
from app.core.project_permissions import get_api_endpoint_permissions, replace_api_endpoint_permissions
from app.core.permission_cache import PermissionCache, set_permission_cache
from app.core.permission_monitor import PermissionMonitor, set_permission_monitor
from app.models.api_endpoint_permission import ApiEndpointPermission
from app.models.study import Study
from app.schemas.member import StudyMemberCreate
@pytest.mark.asyncio
@@ -33,6 +39,112 @@ async def test_api_permission_check_allowed(db_session: AsyncSession):
assert result is True
def test_all_backend_permission_guards_are_configurable():
"""确保后端实际鉴权使用的 operation key 都能在权限管理中配置。"""
api_dir = Path(__file__).resolve().parents[1] / "app" / "api" / "v1"
used_keys: set[str] = set()
pattern = re.compile(r"require_api_permission\(\s*[\"']([^\"']+)[\"']")
for path in api_dir.rglob("*.py"):
used_keys.update(pattern.findall(path.read_text()))
assert used_keys
assert used_keys <= set(API_ENDPOINT_PERMISSIONS)
@pytest.mark.asyncio
async def test_default_matrix_covers_every_role_and_permission(db_session: AsyncSession):
"""逐一验证 6 个预设角色在每个权限上的默认矩阵。"""
study_id = uuid.uuid4()
matrix = await get_api_endpoint_permissions(db_session, study_id)
assert set(matrix) == set(PROJECT_PERMISSION_ROLES)
for role in PROJECT_PERMISSION_ROLES:
assert set(matrix[role]) == set(API_ENDPOINT_PERMISSIONS)
for endpoint_key, config in API_ENDPOINT_PERMISSIONS.items():
assert matrix[role][endpoint_key]["allowed"] is (role in config["default_roles"])
@pytest.mark.asyncio
async def test_full_permission_matrix_round_trips_to_backend_checks(db_session: AsyncSession):
"""逐一验证前端提交格式会落库,并被后端鉴权函数按相同结果读取。"""
cache = PermissionCache()
monitor = PermissionMonitor()
set_permission_cache(cache)
set_permission_monitor(monitor)
study_id = uuid.uuid4()
payload = {
role: {
endpoint_key: index % 2 == role_index % 2
for index, endpoint_key in enumerate(API_ENDPOINT_PERMISSIONS)
}
for role_index, role in enumerate(PROJECT_PERMISSION_ROLES)
}
matrix = await replace_api_endpoint_permissions(db_session, study_id, payload)
for role in PROJECT_PERMISSION_ROLES:
for endpoint_key, expected in payload[role].items():
assert matrix[role][endpoint_key]["allowed"] is expected
allowed = await role_has_api_permission(
db_session,
study_id,
role,
endpoint_key,
check_prerequisites=False,
)
assert allowed is expected
@pytest.mark.asyncio
async def test_custom_active_role_permissions_take_effect(db_session: AsyncSession):
"""验证项目自定义角色配置权限后能被后端鉴权读取。"""
study_id = uuid.uuid4()
db_session.add(
Study(
id=study_id,
code=f"CUSTOM-{study_id.hex[:8]}",
name="自定义角色权限测试",
status="ACTIVE",
is_locked=False,
visit_schedule=[],
active_roles=["DATA_MANAGER"],
)
)
await db_session.commit()
matrix = await get_api_endpoint_permissions(db_session, study_id)
assert "DATA_MANAGER" in matrix
assert matrix["DATA_MANAGER"]["subjects:read"]["allowed"] is False
await replace_api_endpoint_permissions(
db_session,
study_id,
{"DATA_MANAGER": {"subjects:read": True, "subjects:update": False}},
)
assert await role_has_api_permission(
db_session,
study_id,
"DATA_MANAGER",
"subjects:read",
check_prerequisites=False,
) is True
assert await role_has_api_permission(
db_session,
study_id,
"DATA_MANAGER",
"subjects:update",
check_prerequisites=False,
) is False
def test_admin_cannot_be_used_as_project_custom_role():
"""避免项目角色 ADMIN 触发后端系统管理员绕过逻辑。"""
with pytest.raises(ValueError):
StudyMemberCreate(user_id=uuid.uuid4(), role_in_study="ADMIN")
@pytest.mark.asyncio
async def test_api_permission_check_denied(db_session: AsyncSession):
"""测试接口级权限检查 - 拒绝"""