use std::any::Any; use std::collections::HashMap; use std::sync::Arc; use uuid::Uuid; use crate::error::{AppError, AppResult}; use crate::events::EventBus; /// 权限描述符,用于模块声明自己需要的权限。 /// /// 各业务模块通过 `ErpModule::permissions()` 返回此列表, /// 由 erp-server 在启动时统一注册到权限表。 #[derive(Clone, Debug)] pub struct PermissionDescriptor { /// 权限编码,全局唯一,格式建议 `{模块}.{动作}` 如 `plugin.admin` pub code: String, /// 权限显示名称 pub name: String, /// 权限描述 pub description: String, /// 所属模块名称 pub module: String, } /// 模块类型 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ModuleType { /// 内置模块(编译时链接) Builtin, /// 插件模块(运行时加载) Plugin, } /// 模块启动上下文 — 在 on_startup 时提供给模块 pub struct ModuleContext { pub db: sea_orm::DatabaseConnection, pub event_bus: EventBus, } /// 模块注册接口 /// 所有业务模块(Auth, Workflow, Message, Config, 行业模块)都实现此 trait #[async_trait::async_trait] pub trait ErpModule: Send + Sync { /// 模块名称(唯一标识) fn name(&self) -> &str; /// 模块唯一 ID(默认等于 name) fn id(&self) -> &str { self.name() } /// 模块版本 fn version(&self) -> &str { env!("CARGO_PKG_VERSION") } /// 模块类型 fn module_type(&self) -> ModuleType { ModuleType::Builtin } /// 依赖的其他模块名称 fn dependencies(&self) -> Vec<&str> { vec![] } /// 注册事件处理器 fn register_event_handlers(&self, _bus: &EventBus) {} /// 模块启动钩子 — 服务启动时调用 async fn on_startup(&self, _ctx: &ModuleContext) -> AppResult<()> { Ok(()) } /// 模块关闭钩子 — 服务关闭时调用 async fn on_shutdown(&self) -> AppResult<()> { Ok(()) } /// 健康检查 async fn health_check(&self) -> AppResult { Ok(serde_json::json!({"status": "healthy"})) } /// 租户创建时的初始化钩子。 /// /// 用于为新建租户创建默认角色、管理员用户等初始数据。 async fn on_tenant_created( &self, _tenant_id: Uuid, _db: &sea_orm::DatabaseConnection, _event_bus: &EventBus, ) -> AppResult<()> { Ok(()) } /// 租户删除时的清理钩子。 /// /// 用于软删除该租户下的所有关联数据。 async fn on_tenant_deleted( &self, _tenant_id: Uuid, _db: &sea_orm::DatabaseConnection, ) -> AppResult<()> { Ok(()) } /// 返回此模块需要注册的权限列表。 /// /// 默认返回空列表,有权限需求的模块(如 plugin)可覆写此方法。 fn permissions(&self) -> Vec { vec![] } /// Downcast support: return `self` as `&dyn Any` for concrete type access. /// /// This allows the server crate to retrieve module-specific methods /// (e.g. `AuthModule::public_routes()`) that are not part of the trait. fn as_any(&self) -> &dyn Any; } /// 模块注册器 — 用 Arc 包装使其可 Clone(用于 Axum State) #[derive(Clone, Default)] pub struct ModuleRegistry { modules: Arc>>, } impl ModuleRegistry { pub fn new() -> Self { Self { modules: Arc::new(vec![]), } } pub fn register(mut self, module: impl ErpModule + 'static) -> Self { tracing::info!( module = module.name(), id = module.id(), version = module.version(), module_type = ?module.module_type(), "Module registered" ); let mut modules = (*self.modules).clone(); modules.push(Arc::new(module)); self.modules = Arc::new(modules); self } pub fn register_handlers(&self, bus: &EventBus) { for module in self.modules.iter() { module.register_event_handlers(bus); } } pub fn modules(&self) -> &[Arc] { &self.modules } /// 按名称获取模块 pub fn get_module(&self, name: &str) -> Option> { self.modules.iter().find(|m| m.name() == name).cloned() } /// 按拓扑排序返回模块(依赖在前,被依赖在后) /// /// 使用 Kahn 算法,环检测返回 Validation 错误。 pub fn sorted_modules(&self) -> AppResult>> { let modules = &*self.modules; let n = modules.len(); if n == 0 { return Ok(vec![]); } // 构建名称到索引的映射 let name_to_idx: HashMap<&str, usize> = modules .iter() .enumerate() .map(|(i, m)| (m.name(), i)) .collect(); // 构建邻接表和入度 let mut adjacency: Vec> = vec![vec![]; n]; let mut in_degree: Vec = vec![0; n]; for (idx, module) in modules.iter().enumerate() { for dep in module.dependencies() { if let Some(&dep_idx) = name_to_idx.get(dep) { adjacency[dep_idx].push(idx); in_degree[idx] += 1; } // 依赖未注册的模块不阻断(可能是可选依赖) } } // Kahn 算法 let mut queue: Vec = (0..n).filter(|&i| in_degree[i] == 0).collect(); let mut sorted_indices = Vec::with_capacity(n); while let Some(idx) = queue.pop() { sorted_indices.push(idx); for &next in &adjacency[idx] { in_degree[next] -= 1; if in_degree[next] == 0 { queue.push(next); } } } if sorted_indices.len() != n { let cycle_modules: Vec<&str> = (0..n) .filter(|i| !sorted_indices.contains(i)) .filter_map(|i| modules.get(i).map(|m| m.name())) .collect(); return Err(AppError::Validation(format!( "模块依赖存在循环: {}", cycle_modules.join(", ") ))); } Ok(sorted_indices .into_iter() .map(|i| modules[i].clone()) .collect()) } /// 按拓扑顺序启动所有模块 pub async fn startup_all(&self, ctx: &ModuleContext) -> AppResult<()> { let sorted = self.sorted_modules()?; for module in sorted { tracing::info!(module = module.name(), "Starting module"); module.on_startup(ctx).await?; tracing::info!(module = module.name(), "Module started"); } Ok(()) } /// 按拓扑逆序关闭所有模块 pub async fn shutdown_all(&self) -> AppResult<()> { let sorted = self.sorted_modules()?; for module in sorted.into_iter().rev() { tracing::info!(module = module.name(), "Shutting down module"); if let Err(e) = module.on_shutdown().await { tracing::error!(module = module.name(), error = %e, "Module shutdown failed"); } } Ok(()) } /// 对所有模块执行健康检查 pub async fn health_check_all(&self) -> Vec<(String, AppResult)> { let mut results = Vec::with_capacity(self.modules.len()); for module in self.modules.iter() { let result = module.health_check().await; results.push((module.name().to_string(), result)); } results } } #[cfg(test)] mod tests { use super::*; struct TestModule { name: &'static str, deps: Vec<&'static str>, } #[async_trait::async_trait] impl ErpModule for TestModule { fn name(&self) -> &str { self.name } fn dependencies(&self) -> Vec<&str> { self.deps.clone() } fn as_any(&self) -> &dyn Any { self } } #[test] fn sorted_modules_empty() { let registry = ModuleRegistry::new(); let sorted = registry.sorted_modules().unwrap(); assert!(sorted.is_empty()); } #[test] fn sorted_modules_no_deps() { let registry = ModuleRegistry::new() .register(TestModule { name: "a", deps: vec![], }) .register(TestModule { name: "b", deps: vec![], }); let sorted = registry.sorted_modules().unwrap(); assert_eq!(sorted.len(), 2); } #[test] fn sorted_modules_with_deps() { let registry = ModuleRegistry::new() .register(TestModule { name: "auth", deps: vec![], }) .register(TestModule { name: "plugin", deps: vec!["auth", "config"], }) .register(TestModule { name: "config", deps: vec!["auth"], }); let sorted = registry.sorted_modules().unwrap(); let names: Vec<&str> = sorted.iter().map(|m| m.name()).collect(); let auth_pos = names.iter().position(|&n| n == "auth").unwrap(); let config_pos = names.iter().position(|&n| n == "config").unwrap(); let plugin_pos = names.iter().position(|&n| n == "plugin").unwrap(); assert!(auth_pos < config_pos); assert!(config_pos < plugin_pos); } #[test] fn sorted_modules_circular_dep() { let registry = ModuleRegistry::new() .register(TestModule { name: "a", deps: vec!["b"], }) .register(TestModule { name: "b", deps: vec!["a"], }); let result = registry.sorted_modules(); assert!(result.is_err()); match result.err().unwrap() { AppError::Validation(msg) => assert!(msg.contains("循环")), other => panic!("Expected Validation, got {:?}", other), } } #[test] fn get_module_found() { let registry = ModuleRegistry::new().register(TestModule { name: "auth", deps: vec![], }); assert!(registry.get_module("auth").is_some()); assert!(registry.get_module("unknown").is_none()); } }