Phase A (P1 production blockers): - A1: Apply IP rate limiting to public routes (login/refresh) - A2: Publish domain events for workflow instance state transitions (completed/suspended/resumed/terminated) via outbox pattern - A3: Replace hardcoded nil UUID default tenant with dynamic DB lookup - A4: Add GET /api/v1/audit-logs query endpoint with pagination - A5: Enhance CORS wildcard warning for production environments Phase B (P2 functional gaps): - B1: Remove dead erp-common crate (zero references in codebase) - B2: Refactor 5 settings pages to use typed API modules instead of direct client calls; create api/themes.ts; delete dead errors.ts - B3: Add resume/suspend buttons to InstanceMonitor page - B4: Remove unused EventHandler trait from erp-core - B5: Handle task.completed events in message module (send notifications) - B6: Wire TimeoutChecker as 60s background task - B7: Auto-skip ServiceTask nodes instead of crashing the process - B8: Remove empty register_routes() from ErpModule trait and modules
77 lines
2.0 KiB
Rust
77 lines
2.0 KiB
Rust
use std::any::Any;
|
||
use std::sync::Arc;
|
||
|
||
use uuid::Uuid;
|
||
|
||
use crate::error::AppResult;
|
||
use crate::events::EventBus;
|
||
|
||
/// 模块注册接口
|
||
/// 所有业务模块(Auth, Workflow, Message, Config, 行业模块)都实现此 trait
|
||
#[async_trait::async_trait]
|
||
pub trait ErpModule: Send + Sync {
|
||
/// 模块名称(唯一标识)
|
||
fn name(&self) -> &str;
|
||
|
||
/// 模块版本
|
||
fn version(&self) -> &str {
|
||
env!("CARGO_PKG_VERSION")
|
||
}
|
||
|
||
/// 依赖的其他模块名称
|
||
fn dependencies(&self) -> Vec<&str> {
|
||
vec![]
|
||
}
|
||
|
||
/// 注册事件处理器
|
||
fn register_event_handlers(&self, _bus: &EventBus) {}
|
||
|
||
/// 租户创建时的初始化钩子
|
||
async fn on_tenant_created(&self, _tenant_id: Uuid) -> AppResult<()> {
|
||
Ok(())
|
||
}
|
||
|
||
/// 租户删除时的清理钩子
|
||
async fn on_tenant_deleted(&self, _tenant_id: Uuid) -> AppResult<()> {
|
||
Ok(())
|
||
}
|
||
|
||
/// 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<Vec<Arc<dyn ErpModule>>>,
|
||
}
|
||
|
||
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(), version = module.version(), "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<dyn ErpModule>] {
|
||
&self.modules
|
||
}
|
||
}
|