fix(web): 修复角色测试发现的权限守卫、API 500、权限配置问题
Some checks failed
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled

1. CRITICAL: 前端路由权限守卫 — routePermissions 从 3 条扩展到 31 条,
   覆盖全部 /health/* 路由;匹配逻辑从宽松模块级前缀改为精确权限码匹配
2. HIGH: health-data API 500 — jsonb_array_elements() 添加 CASE WHEN 类型守卫,
   防止 items 字段为非数组 JSON 时崩溃
3. MEDIUM: Doctor 补充 ai.prompt.list、ai.usage.list、follow-up-templates 权限
4. Operator 清理 AI 分析、统计报表菜单关联
5. 更新 5 角色测试计划文档
This commit is contained in:
iven
2026-05-06 22:29:54 +08:00
parent 5467394ffe
commit 43f0ba7057
9 changed files with 608 additions and 302 deletions

View File

@@ -213,7 +213,7 @@ async fn count_abnormal_lab_items(
let sql = r#"
SELECT COALESCE(SUM(jsonb_array_length(
COALESCE(
(SELECT jsonb_agg(elem) FROM jsonb_array_elements(items) elem WHERE elem->>'is_abnormal' = 'true'),
(SELECT jsonb_agg(elem) FROM jsonb_array_elements(CASE WHEN jsonb_typeof(items) = 'array' THEN items ELSE '[]'::jsonb END) elem WHERE elem->>'is_abnormal' = 'true'),
'[]'::jsonb
)
)), 0::bigint) AS total

View File

@@ -125,6 +125,7 @@ mod m20260505_000122_create_ai_knowledge_guides;
mod m20260505_000123_update_ai_prompts_system_instruction;
mod m20260505_000124_freeze_deferred_menus;
mod m20260506_000125_restructure_menus_and_roles;
mod m20260506_000126_fix_role_permissions_cleanup;
pub struct Migrator;
@@ -257,6 +258,7 @@ impl MigratorTrait for Migrator {
Box::new(m20260505_000123_update_ai_prompts_system_instruction::Migration),
Box::new(m20260505_000124_freeze_deferred_menus::Migration),
Box::new(m20260506_000125_restructure_menus_and_roles::Migration),
Box::new(m20260506_000126_fix_role_permissions_cleanup::Migration),
]
}
}

View File

@@ -0,0 +1,105 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
// === Doctor 角色权限修复 ===
let doctor_add = vec![
"health.follow-up-templates.list",
"health.follow-up-templates.manage",
"ai.usage.list",
"ai.prompt.list",
];
for code in &doctor_add {
db.execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
format!(
"INSERT INTO role_permissions (role_id, permission_id, tenant_id, data_scope, created_at, updated_at, created_by, updated_by, deleted_at, version) \
SELECT r.id, p.id, r.tenant_id, 'all', NOW(), NOW(), r.id, r.id, NULL, 1 \
FROM roles r \
JOIN permissions p ON p.tenant_id = r.tenant_id AND p.code = '{code}' AND p.deleted_at IS NULL \
WHERE r.code = 'doctor' AND r.deleted_at IS NULL \
ON CONFLICT (role_id, permission_id) DO NOTHING",
),
)).await?;
}
// Doctor 移除多余权限
let doctor_remove = vec![
"health.articles.list",
"health.articles.manage",
"health.articles.review",
"health.points.list",
"health.points.manage",
];
for code in &doctor_remove {
db.execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
format!(
"UPDATE role_permissions SET deleted_at = NOW() \
FROM roles r, permissions p \
WHERE role_permissions.role_id = r.id AND role_permissions.permission_id = p.id \
AND r.code = 'doctor' AND p.code = '{code}' AND role_permissions.deleted_at IS NULL",
),
)).await?;
}
// === Nurse 角色权限清理 ===
let nurse_remove = vec![
"health.doctor.list",
"health.alerts.manage",
];
for code in &nurse_remove {
db.execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
format!(
"UPDATE role_permissions SET deleted_at = NOW() \
FROM roles r, permissions p \
WHERE role_permissions.role_id = r.id AND role_permissions.permission_id = p.id \
AND r.code = 'nurse' AND p.code = '{code}' AND role_permissions.deleted_at IS NULL",
),
)).await?;
}
// === Operator 角色 menu_roles 清理 ===
// menus 表的标题字段是 title 不是 name
let operator_menu_remove = vec![
"随访管理",
"咨询管理",
"行动收件箱",
"诊断记录",
"知情同意",
"医护管理",
"实时监控",
"BLE 网关",
"危急值阈值",
"AI 分析",
"AI Prompt 管理",
"随访模板管理",
"AI 分析历史",
"统计报表",
];
for title in &operator_menu_remove {
db.execute(sea_orm::Statement::from_string(
sea_orm::DatabaseBackend::Postgres,
format!(
"DELETE FROM menu_roles \
USING roles r, menus m \
WHERE menu_roles.role_id = r.id AND menu_roles.menu_id = m.id \
AND r.code = 'operator' AND m.title = '{title}'",
),
)).await?;
}
Ok(())
}
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
Ok(())
}
}