--- title: erp-core updated: 2026-04-28 status: stable tags: [core, error, event-bus, module-trait, shared-types] --- # erp-core > 从 [[index]] 导航。关联: [[erp-server]] [[database]] [[wasm-plugin]] [[architecture]] [[erp-health]] ## 1. 设计决策 `erp-core` 是 L1 基础层,所有业务模块的唯一共同依赖。定义**跨模块共享的契约**,不含业务逻辑。 核心决策: - **AppError 统一错误体系** — 8 种变体映射 HTTP 状态码,`?` 传播 + Axum `IntoResponse` 自动转换 - **EventBus 进程内广播** — broadcast channel (1024) + PostgreSQL Outbox + LISTEN/NOTIFY + 幂等消费 + Dead Letter - **PiiCrypto 加密体系** — AES-256-GCM + KEK/DEK 分层管理 + HMAC 盲索引 + 数据脱敏 - **ErpModule 插件 trait** — 统一注册路由和事件处理器 + 拓扑排序启动 - **BaseFields 强制多租户** — 所有实体基础字段模板 ## 2. 关键文件 + 数据流 ### 核心文件 | 文件 | 职责 | |------|------| | `crates/erp-core/src/error.rs` | AppError 定义、HTTP 映射、From 转换 | | `crates/erp-core/src/events.rs` | DomainEvent、EventBus、EventHandler trait | | `crates/erp-core/src/module.rs` | ErpModule trait、ModuleRegistry | | `crates/erp-core/src/types.rs` | BaseFields、Pagination、ApiResponse、TenantContext | | `crates/erp-core/src/crypto/` | PiiCrypto(engine/key_manager/masking/hmac_index),AES-256-GCM + KEK/DEK | | `crates/erp-core/src/sanitize.rs` | strip_html_tags()、sanitize_string() | | `crates/erp-core/src/entity/` | audit_log, domain_event, processed_event, dead_letter_event | | `crates/erp-core/src/audit.rs` | 审计日志记录 | | `crates/erp-core/src/rbac.rs` | require_permission 权限检查 | | `crates/erp-core/src/lib.rs` | 模块导出入口 | ### 集成契约 | 方向 | 模块 | 接口 | 触发时机 | |------|------|------|---------| | 提供 → | erp-auth | ErpModule trait, AppError, EventBus | 模块实现 | | 提供 → | erp-config | ErpModule trait, AppError | 模块实现 | | 提供 → | erp-workflow | ErpModule trait, AppError, EventBus | 模块实现 | | 提供 → | erp-message | ErpModule trait, AppError, EventBus | 模块实现 | | 提供 → | erp-plugin | ErpModule trait, AppError, EventBus | 模块实现 | | 提供 → | [[erp-health]] | ErpModule trait, AppError, EventBus, sanitize | 模块实现 | | 消费 ← | [[erp-server]] | ModuleRegistry 组装 | 启动时 | | 桥接 ← | [[wasm-plugin]] | EventBus → 插件 handle_event | 运行时 | ## 3. 代码逻辑 ### 错误处理链 ``` 业务 crate (thiserror) → AppError → IntoResponse → HTTP JSON 数据库 (sea_orm::DbErr) → From 转换 → AppError (自动识别 duplicate key → Conflict) ``` 响应格式:`{ "error": "not_found", "message": "资源不存在", "details": null }` ### 事件总线 ``` EventBus::publish(DomainEvent) → INSERT pending → 内存 broadcast(channel 1024) → UPDATE published → NOTIFY outbox_channel Outbox Relay: PgListener (LISTEN/NOTIFY) + 30s 兜底轮询 + 自动重连 幂等消费: processed_events 表 + consumer_id Dead Letter: 失败事件 → dead_letter_events 表 ``` 事件字段: id(UUIDv7), event_type("user.created"), tenant_id, payload(JSON), timestamp 命名规则:`{模块}.{动作}` 如 `user.created`, `workflow.task.completed` 健康模块事件:`patient.created`, `appointment.confirmed`, `appointment.cancelled` ### 模块注册 ``` ErpModule trait → ModuleRegistry::register() → build_router(): 折叠所有模块路由 → Axum Router register_handlers(): 注册事件处理器 → EventBus ``` 已注册模块:AuthModule → ConfigModule → WorkflowModule → MessageModule → PluginModule → **HealthModule** → **AiModule** ### 共享类型 - `TenantContext` — 租户上下文(tenant_id, user_id, roles, permissions, department_ids) - `Pagination` / `PaginatedResponse` — 分页标准化(每页上限 100) - `ApiResponse` — 统一信封 `{ success, data, message }` - `sanitize_string()` — HTML 标签过滤,用于用户输入清理 ⚡ **不变量**: erp-core 不依赖任何业务 crate,只被依赖 ⚡ **不变量**: 所有 API 使用 `/api/v1/` 前缀 ⚡ **不变量**: tenant_id 从 JWT 中间件注入,应用层不可伪造 ## 4. 活跃问题 + 陷阱 ⚠️ crate 内部可用 `anyhow`,但跨 crate 边界必须转 `AppError` ⚠️ EventBus 当前为内存 broadcast,outbox 持久化通过后台任务实现 ⚠️ 微信注册路径的 display_name 需调用 `sanitize_string()` 防止 XSS ## 5. 变更记录 | 日期 | 变更 | |------|------| | 2026-04-25 | 添加 erp-health 集成契约、健康模块事件、sanitize 模块引用 | | 2026-04-23 | 重构为 5 节结构,更新为已完全集成状态 |