fix(web): 修复角色测试发现的权限守卫、API 500、权限配置问题
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:
@@ -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
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user