基于 3 个并行探索代理的全面扫描结果,更新 wiki 数据至实际状态: - index.md: 18 crate / 76 迁移 / 44 实体 / 77k 行 / 409 提交 - erp-health.md: 44 实体 / 21 handler / 22 权限 / 25 事件 / 6 消费者 - erp-server.md: 9 后台任务 / RLS 中间件栈 - architecture.md: 新增 erp-ai/dialysis 到依赖图 / 测试覆盖表 - testing.md: 225 单元 + 159 集成 / 4 模块零测试警告 - database.md: 76 迁移 / RLS+哈希链+盲索引+Dead Letter - erp-core.md: PiiCrypto 加密体系 / EventBus 完整描述 - frontend.md: 163 文件 / 5 store / 10 API 文件 - CLAUDE.md: 新增 health/ai/dialysis/assessment scope 头脑风暴 4 个议题决策: - dialysis: 接入激活 - 测试: 按风险排序(workflow > ai > message > config) - AI: 数据桥接优先 - 路线图: AI 驱动 3 个月 5 Phase
4.7 KiB
4.7 KiB
title, updated, status, tags
| title | updated | status | tags | |||||
|---|---|---|---|---|---|---|---|---|
| erp-core | 2026-04-28 | stable |
|
erp-core
从 index 导航。关联: erp-server database wasm-plugin architecture erp-health
1. 设计决策
erp-core 是 L1 基础层,所有业务模块的唯一共同依赖。定义跨模块共享的契约,不含业务逻辑。
核心决策:
- AppError 统一错误体系 — 8 种变体映射 HTTP 状态码,
?传播 + AxumIntoResponse自动转换 - 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<T>— 分页标准化(每页上限 100)ApiResponse<T>— 统一信封{ 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 节结构,更新为已完全集成状态 |