From 786f57c151c4f26ab52e78df1add8adeb217ab4f Mon Sep 17 00:00:00 2001 From: iven Date: Thu, 7 May 2026 15:54:37 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=8F=91=E7=8E=B0=E7=9A=84=205=20=E4=B8=AA?= =?UTF-8?q?=E5=85=B1=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复前端路由守卫前缀碰撞(/health/articles 匹配 /health/article-categories) - 补全 6 条缺失路由权限映射(appointments/follow-up-records/article-categories/article-tags/plugins/market) - 修复 critical-alerts API 500(escalation_level 字段 INT2/i16 与 Entity i32 类型不匹配) - 新增迁移 000128:告警状态修正 + 菜单权限码补全 + 非admin角色移除基础模块权限 --- apps/web/src/App.tsx | 10 ++- .../erp-health/src/entity/critical_alert.rs | 2 +- crates/erp-server/migration/src/lib.rs | 2 + ..._000128_fix_alert_status_and_menu_perms.rs | 81 +++++++++++++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 crates/erp-server/migration/src/m20260507_000128_fix_alert_status_and_menu_perms.rs diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index df2fb5d..75396ce 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -106,9 +106,12 @@ const ROUTE_PERMISSIONS: Record = { '/messages': ['message.list'], '/settings': ['config.settings.list', 'config.settings.manage'], '/plugins/admin': ['plugin.list', 'plugin.manage'], + '/plugins/market': ['plugin.list', 'plugin.manage'], '/health/patients': ['health.patient.list', 'health.patient.manage'], '/health/doctors': ['health.doctor.list', 'health.doctor.manage'], + '/health/appointments': ['health.appointment.list', 'health.appointment.manage'], '/health/follow-up-tasks': ['health.follow-up.list', 'health.follow-up.manage'], + '/health/follow-up-records': ['health.follow-up.list', 'health.follow-up.manage'], '/health/consultations': ['health.consultation.list', 'health.consultation.manage'], '/health/action-inbox': ['health.action-inbox.list', 'health.action-inbox.manage'], '/health/follow-up-templates': ['health.follow-up-templates.list', 'health.follow-up-templates.manage'], @@ -121,6 +124,8 @@ const ROUTE_PERMISSIONS: Record = { '/health/ble-gateways': ['health.ble-gateways.list', 'health.ble-gateways.manage'], '/health/critical-value-thresholds': ['health.critical-value-thresholds.list', 'health.critical-value-thresholds.manage'], '/health/articles': ['health.articles.list', 'health.articles.manage'], + '/health/article-categories': ['health.articles.list', 'health.articles.manage'], + '/health/article-tags': ['health.articles.list', 'health.articles.manage'], '/health/points-rules': ['health.points.list', 'health.points.manage'], '/health/points-products': ['health.points.list', 'health.points.manage'], '/health/points-orders': ['health.points.list', 'health.points.manage'], @@ -153,13 +158,14 @@ function PrivateRoute({ children }: { children: React.ReactNode }) { // 首页/工作台始终放行 if (path === '/' || path === '') return <>{children}; - const matchedPrefix = Object.keys(ROUTE_PERMISSIONS).find((prefix) => path.startsWith(prefix)); + const matchedPrefix = Object.keys(ROUTE_PERMISSIONS).find( + (prefix) => path === prefix || path.startsWith(prefix + '/'), + ); if (matchedPrefix) { const required = ROUTE_PERMISSIONS[matchedPrefix]; const hasAccess = required.some((r) => permissions.includes(r)); if (!hasAccess) return ; } else { - // 未在 ROUTE_PERMISSIONS 中注册的路由,默认拒绝 return ; } diff --git a/crates/erp-health/src/entity/critical_alert.rs b/crates/erp-health/src/entity/critical_alert.rs index d795332..f7015ca 100644 --- a/crates/erp-health/src/entity/critical_alert.rs +++ b/crates/erp-health/src/entity/critical_alert.rs @@ -18,7 +18,7 @@ pub struct Model { pub acknowledged_by: Option, #[sea_orm(skip_serializing_if = "Option::is_none")] pub acknowledged_at: Option, - pub escalation_level: i32, + pub escalation_level: i16, pub created_at: DateTimeUtc, pub updated_at: DateTimeUtc, #[sea_orm(skip_serializing_if = "Option::is_none")] diff --git a/crates/erp-server/migration/src/lib.rs b/crates/erp-server/migration/src/lib.rs index 2602d87..aa61c77 100644 --- a/crates/erp-server/migration/src/lib.rs +++ b/crates/erp-server/migration/src/lib.rs @@ -127,6 +127,7 @@ mod m20260505_000124_freeze_deferred_menus; mod m20260506_000125_restructure_menus_and_roles; mod m20260506_000126_fix_role_permissions_cleanup; mod m20260507_000127_fix_doctor_extra_permissions; +mod m20260507_000128_fix_alert_status_and_menu_perms; pub struct Migrator; @@ -261,6 +262,7 @@ impl MigratorTrait for Migrator { Box::new(m20260506_000125_restructure_menus_and_roles::Migration), Box::new(m20260506_000126_fix_role_permissions_cleanup::Migration), Box::new(m20260507_000127_fix_doctor_extra_permissions::Migration), + Box::new(m20260507_000128_fix_alert_status_and_menu_perms::Migration), ] } } diff --git a/crates/erp-server/migration/src/m20260507_000128_fix_alert_status_and_menu_perms.rs b/crates/erp-server/migration/src/m20260507_000128_fix_alert_status_and_menu_perms.rs new file mode 100644 index 0000000..e40f6ad --- /dev/null +++ b/crates/erp-server/migration/src/m20260507_000128_fix_alert_status_and_menu_perms.rs @@ -0,0 +1,81 @@ +//! 修复告警状态 + 菜单权限码 + 角色权限清理 + +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(); + + // === 1. 修复告警 seed 数据状态:active → pending === + // alerts 表中 status='active' 的记录统一改为 'pending' + db.execute_unprepared( + "UPDATE alerts SET status = 'pending', updated_at = NOW() \ + WHERE status = 'active' AND deleted_at IS NULL", + ) + .await?; + + // critical_alert 表同理 + db.execute_unprepared( + "UPDATE critical_alert SET status = 'pending', updated_at = NOW() \ + WHERE status = 'active' AND deleted_at IS NULL", + ) + .await?; + + // === 2. 为缺失 permission 的菜单补上权限码 === + + // m20260505_000116 创建的菜单缺少 permission 字段 + let menu_perms: &[(&str, &str)] = &[ + ("/health/care-plans", "health.care-plan.list"), + ("/health/shifts", "health.shifts.list"), + ("/health/medications", "health.medication-records.manage"), + ("/health/ble-gateways", "health.ble-gateways.list"), + ("/health/critical-value-thresholds", "health.critical-value-thresholds.list"), + ("/health/diagnoses", "health.health-data.list"), + ("/health/family-proxy", "health.patient.list"), + ("/health/consents", "health.consent.list"), + ("/health/realtime-monitor", "health.device-readings.list"), + ("/health/oauth-clients", "health.oauth.list"), + ("/health/follow-up-templates", "health.follow-up-templates.list"), + ]; + + for &(path, perm) in menu_perms { + db.execute_unprepared(&format!( + "UPDATE menus SET permission = '{perm}', updated_at = NOW() \ + WHERE path = '{path}' AND deleted_at IS NULL AND (permission IS NULL OR permission = '')" + )) + .await?; + } + + // === 3. 清理非 admin 角色不应有的基础模块权限 === + // user.list / user.manage / config.settings.list / config.settings.manage + // 应只属于 admin 角色 + let over_privileged_codes = vec![ + "user.list", + "user.manage", + "config.settings.list", + "config.settings.manage", + ]; + for code in &over_privileged_codes { + db.execute_unprepared(&format!( + "UPDATE role_permissions SET deleted_at = NOW(), updated_at = NOW() \ + FROM roles r, permissions p \ + WHERE role_permissions.role_id = r.id \ + AND role_permissions.permission_id = p.id \ + AND r.code NOT IN ('admin') \ + AND p.code = '{code}' \ + AND role_permissions.deleted_at IS NULL" + )) + .await?; + } + + Ok(()) + } + + async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> { + Ok(()) + } +}