//! 补全缺失权限码注册 //! //! CI check-permissions.sh 发现 23 个后端 handler 已使用但 seed 迁移未注册的权限码。 //! 本迁移统一补注册到 permissions 表,并绑定 admin 角色。 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(); let sys = "00000000-0000-0000-0000-000000000000"; // (code, name, resource, action) let permissions: &[(&str, &str, &str, &str)] = &[ // AI 模块 ("ai.analysis.list", "查看 AI 分析", "ai", "analysis.list"), ( "ai.analysis.manage", "管理 AI 分析", "ai", "analysis.manage", ), ("ai.prompt.list", "查看 AI 提示词", "ai", "prompt.list"), ("ai.prompt.manage", "管理 AI 提示词", "ai", "prompt.manage"), ( "ai.provider.manage", "管理 AI Provider", "ai", "provider.manage", ), ( "ai.suggestion.list", "查看 AI 建议", "ai", "suggestion.list", ), ( "ai.suggestion.manage", "管理 AI 建议", "ai", "suggestion.manage", ), ("ai.usage.list", "查看 AI 用量", "ai", "usage.list"), // Copilot ( "copilot.insights.list", "查看 Copilot 洞察", "copilot", "insights.list", ), ( "copilot.insights.manage", "管理 Copilot 洞察", "copilot", "insights.manage", ), ("copilot.risk.view", "查看风险快照", "copilot", "risk.view"), ( "copilot.rules.list", "查看 Copilot 规则", "copilot", "rules.list", ), ( "copilot.rules.manage", "管理 Copilot 规则", "copilot", "rules.manage", ), // Health — IoT/设备 ( "health.ble-gateways.manage", "管理 BLE 网关", "health", "ble-gateways.manage", ), ( "health.critical-alerts.manage", "管理危急值告警", "health", "critical-alerts.manage", ), ( "health.critical-value-thresholds.manage", "管理危急值阈值", "health", "critical-value-thresholds.manage", ), ( "health.device-readings.manage", "管理设备读数", "health", "device-readings.manage", ), ( "health.devices.manage", "管理患者设备", "health", "devices.manage", ), ( "health.medication-reminders.manage", "管理药物提醒", "health", "medication-reminders.manage", ), // Health — 其他 ( "health.family-proxy.list", "查看家庭健康代理", "health", "family-proxy.list", ), ( "health.family-proxy.manage", "管理家庭健康代理", "health", "family-proxy.manage", ), ( "health.oauth.manage", "管理 OAuth 合作方", "health", "oauth.manage", ), ( "health.shifts.manage", "管理班次", "health", "shifts.manage", ), // Plugin / Tenant ("plugin.admin", "插件管理", "plugin", "admin"), ("plugin.list", "查看插件列表", "plugin", "list"), ("tenant.manage", "租户管理", "tenant", "manage"), ]; for &(code, name, resource, action) in permissions { db.execute_unprepared(&format!( r#" INSERT INTO permissions (id, tenant_id, code, name, resource, action, description, created_at, updated_at, created_by, updated_by, deleted_at, version) SELECT gen_random_uuid(), t.id, '{code}', '{name}', '{resource}', '{action}', '{name}', NOW(), NOW(), '{sys}', '{sys}', NULL, 1 FROM tenant t WHERE NOT EXISTS ( SELECT 1 FROM permissions p WHERE p.code = '{code}' AND p.tenant_id = t.id AND p.deleted_at IS NULL ) "# )).await?; } // 绑定所有新权限到 admin 角色 let codes: Vec<&str> = permissions.iter().map(|p| p.0).collect(); let codes_sql = codes .iter() .map(|c| format!("'{c}'")) .collect::>() .join(", "); db.execute_unprepared(&format!( r#" INSERT INTO role_permissions (role_id, permission_id, tenant_id, created_by, updated_by, version) SELECT r.id, p.id, t.id, r.id, r.id, 1 FROM tenant t JOIN roles r ON r.tenant_id = t.id AND r.code = 'admin' AND r.deleted_at IS NULL JOIN permissions p ON p.tenant_id = t.id AND p.code IN ({codes_sql}) AND p.deleted_at IS NULL WHERE NOT EXISTS ( SELECT 1 FROM role_permissions rp WHERE rp.permission_id = p.id AND rp.role_id = r.id ) "# )).await?; Ok(()) } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { let db = manager.get_connection(); let codes = [ "ai.analysis.list", "ai.analysis.manage", "ai.prompt.list", "ai.prompt.manage", "ai.provider.manage", "ai.suggestion.list", "ai.suggestion.manage", "ai.usage.list", "copilot.insights.list", "copilot.insights.manage", "copilot.risk.view", "copilot.rules.list", "copilot.rules.manage", "health.ble-gateways.manage", "health.critical-alerts.manage", "health.critical-value-thresholds.manage", "health.device-readings.manage", "health.devices.manage", "health.family-proxy.list", "health.family-proxy.manage", "health.medication-reminders.manage", "health.oauth.manage", "health.shifts.manage", "plugin.admin", "plugin.list", "tenant.manage", ]; let codes_sql = codes .iter() .map(|c| format!("'{c}'")) .collect::>() .join(", "); db.execute_unprepared(&format!( "DELETE FROM role_permissions WHERE permission_id IN \ (SELECT id FROM permissions WHERE code IN ({codes_sql}))" )) .await .ok(); db.execute_unprepared(&format!( "DELETE FROM permissions WHERE code IN ({codes_sql})" )) .await .ok(); Ok(()) } }