diff --git a/crates/erp-server/migration/src/lib.rs b/crates/erp-server/migration/src/lib.rs index 6f7c61c..12fe4aa 100644 --- a/crates/erp-server/migration/src/lib.rs +++ b/crates/erp-server/migration/src/lib.rs @@ -96,6 +96,7 @@ mod m20260429_000093_trend_analysis_prompt_v2; mod m20260429_000094_device_readings_unique_constraint; mod m20260429_000095_seed_alert_device_menus; mod m20260430_000096_create_medication_reminder; +mod m20260501_000097_seed_menu_permissions; pub struct Migrator; @@ -199,6 +200,7 @@ impl MigratorTrait for Migrator { Box::new(m20260429_000094_device_readings_unique_constraint::Migration), Box::new(m20260429_000095_seed_alert_device_menus::Migration), Box::new(m20260430_000096_create_medication_reminder::Migration), + Box::new(m20260501_000097_seed_menu_permissions::Migration), ] } } diff --git a/crates/erp-server/migration/src/m20260501_000097_seed_menu_permissions.rs b/crates/erp-server/migration/src/m20260501_000097_seed_menu_permissions.rs new file mode 100644 index 0000000..0ac22b5 --- /dev/null +++ b/crates/erp-server/migration/src/m20260501_000097_seed_menu_permissions.rs @@ -0,0 +1,128 @@ +//! 为菜单种子数据关联权限码 + 补全缺失菜单项 + +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(); + + // === 更新已有菜单的 permission 字段 === + + // 健康管理菜单 + update_perm(db, "b0000003-0000-0000-0000-000000000002", "health.patient.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000003", "health.doctor.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000004", "health.appointment.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000005", "health.appointment.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000006", "health.follow-up.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000007", "health.consultation.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000008", "health.patient.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000009", "health.points.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000010", "health.points.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000011", "health.points.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000012", "health.points.list").await?; + // AI 模块菜单 + update_perm(db, "b0000003-0000-0000-0000-000000000013", "ai.prompt.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000014", "ai.analysis.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000015", "ai.usage.list").await?; + // 告警菜单 + update_perm(db, "b0000003-0000-0000-0000-000000000016", "health.alerts.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000017", "health.alerts.list").await?; + update_perm(db, "b0000003-0000-0000-0000-000000000018", "health.alert-rules.list").await?; + // 设备菜单 + update_perm(db, "b0000003-0000-0000-0000-000000000019", "health.devices.list").await?; + + // === 补全缺失菜单 === + + let result = db.query_one(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + "SELECT id::text FROM tenant LIMIT 1".to_string(), + )) + .await?; + + let tid = match result { + Some(row) => row.try_get_by_index::(0).unwrap_or_default(), + None => return Ok(()), + }; + + let sys = "00000000-0000-0000-0000-000000000000"; + let d3 = "a0000000-0000-0000-0000-000000000003"; // 健康管理目录 + + // 透析管理(sort 19) + insert_menu_with_perm(db, &tid, d3, "b0000003-0000-0000-0000-000000000020", "透析管理", "/health/dialysis", "ExperimentOutlined", 19, "health.dialysis.list", sys).await?; + // 资讯管理(sort 20) + insert_menu_with_perm(db, &tid, d3, "b0000003-0000-0000-0000-000000000021", "资讯管理", "/health/articles", "ReadOutlined", 20, "health.articles.list", sys).await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let db = manager.get_connection(); + + // 清除所有菜单的 permission + db.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + "UPDATE menus SET permission = NULL WHERE deleted_at IS NULL".to_string(), + )) + .await?; + + // 删除新增菜单 + for id in &[ + "b0000003-0000-0000-0000-000000000020", + "b0000003-0000-0000-0000-000000000021", + ] { + db.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!("DELETE FROM menus WHERE id = '{id}'"), + )) + .await + .ok(); + } + + Ok(()) + } +} + +async fn update_perm( + db: &sea_orm_migration::SchemaManagerConnection<'_>, + menu_id: &str, + permission: &str, +) -> Result<(), DbErr> { + db.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!( + "UPDATE menus SET permission = '{permission}', updated_at = NOW() \ + WHERE id = '{menu_id}' AND deleted_at IS NULL" + ), + )) + .await?; + Ok(()) +} + +async fn insert_menu_with_perm( + db: &sea_orm_migration::SchemaManagerConnection<'_>, + tenant_id: &str, + parent_id: &str, + id: &str, + title: &str, + path: &str, + icon: &str, + sort: i32, + permission: &str, + sys: &str, +) -> Result<(), DbErr> { + let esc_title = title.replace('\'', "''"); + db.execute(sea_orm::Statement::from_string( + sea_orm::DatabaseBackend::Postgres, + format!( + "INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order, visible, menu_type, permission, created_at, updated_at, created_by, updated_by, deleted_at, version) \ + VALUES ('{id}', '{tenant_id}', '{parent_id}', '{esc_title}', '{path}', '{icon}', {sort}, true, 'menu', '{permission}', NOW(), NOW(), '{sys}', '{sys}', NULL, 1) \ + ON CONFLICT (id) DO UPDATE SET permission = EXCLUDED.permission, updated_at = NOW()" + ), + )) + .await?; + Ok(()) +} diff --git a/crates/erp-server/tests/integration/health_consultation_tests.rs b/crates/erp-server/tests/integration/health_consultation_tests.rs index 6157d32..f4cc0b9 100644 --- a/crates/erp-server/tests/integration/health_consultation_tests.rs +++ b/crates/erp-server/tests/integration/health_consultation_tests.rs @@ -102,10 +102,9 @@ async fn test_consultation_message_send() { let msg = consultation_service::create_message( app.health_state(), app.tenant_id(), Some(app.operator_id()), + app.operator_id(), "doctor".to_string(), CreateMessageReq { session_id: session.id, - sender_id: app.operator_id(), - sender_role: "doctor".to_string(), content_type: Some("text".to_string()), content: "您好,有什么可以帮您?".to_string(), }, @@ -132,10 +131,9 @@ async fn test_consultation_message_list() { for i in 0..3 { consultation_service::create_message( app.health_state(), app.tenant_id(), Some(app.operator_id()), + app.operator_id(), "doctor".to_string(), CreateMessageReq { session_id: session.id, - sender_id: app.operator_id(), - sender_role: "doctor".to_string(), content_type: None, content: format!("消息{}", i + 1), }, diff --git a/crates/erp-server/tests/integration/health_pii_encryption_tests.rs b/crates/erp-server/tests/integration/health_pii_encryption_tests.rs index 6dbc69e..0c58bca 100644 --- a/crates/erp-server/tests/integration/health_pii_encryption_tests.rs +++ b/crates/erp-server/tests/integration/health_pii_encryption_tests.rs @@ -269,10 +269,10 @@ async fn test_consultation_message_content_encrypted() { &state, tenant_id, Some(sender_id), + sender_id, + "patient".to_string(), CreateMessageReq { session_id: session.id, - sender_id, - sender_role: "patient".to_string(), content_type: Some("text".to_string()), content: plain_content.to_string(), },