feat(health): 添加 erp-health 健康管理模块骨架
新建 erp-health 原生 Rust crate,覆盖设计规格中定义的 5 大业务域: - 16 个 SeaORM Entity(患者/家属/标签/医生/健康档案/体征/化验单/预约/排班/随访/咨询等) - 16 表数据库迁移(含索引、外键、默认值、可回滚) - 40+ API 路由骨架(患者管理/健康数据/预约排班/随访/咨询/医生管理) - 12 个权限声明(health.patient/health-data/appointment/follow-up/consultation/doctor 各 .list/.manage) - DTO / Service / Handler / Event 四层架构,Service 使用 todo!() 占位 - erp-server 集成:模块注册 + AppState FromRef 桥接 + 路由挂载 同步更新 CLAUDE.md 项目进度、wiki 知识库、设计规格文档。
This commit is contained in:
128
wiki/erp-core.md
128
wiki/erp-core.md
@@ -1,57 +1,27 @@
|
||||
---
|
||||
title: erp-core
|
||||
updated: 2026-04-23
|
||||
status: stable
|
||||
tags: [core, error, event-bus, module-trait, shared-types]
|
||||
---
|
||||
|
||||
# erp-core
|
||||
|
||||
## 设计思想
|
||||
> 从 [[index]] 导航。关联: [[erp-server]] [[database]] [[wasm-plugin]] [[architecture]]
|
||||
|
||||
`erp-core` 是整个 ERP 平台的 L1 基础层,所有业务模块的唯一共同依赖。它的职责是定义**跨模块共享的契约**,而非实现业务逻辑。
|
||||
## 1. 设计决策
|
||||
|
||||
核心设计决策:
|
||||
- **AppError 统一错误体系** — 6 种错误变体映射到 HTTP 状态码,业务 crate 只需 `?` 传播错误,由 Axum `IntoResponse` 自动转换
|
||||
- **EventBus 进程内广播** — 用 `tokio::sync::broadcast` 实现发布/订阅,模块间零耦合通信
|
||||
- **ErpModule 插件 trait** — 每个业务模块实现此 trait,由 `ModuleRegistry` 统一注册路由和事件处理器
|
||||
- **BaseFields 强制多租户** — 所有实体的基础字段模板,确保 `tenant_id` 从第一天就存在
|
||||
`erp-core` 是 L1 基础层,所有业务模块的唯一共同依赖。定义**跨模块共享的契约**,不含业务逻辑。
|
||||
|
||||
## 代码逻辑
|
||||
核心决策:
|
||||
- **AppError 统一错误体系** — 6 种变体映射 HTTP 状态码,`?` 传播 + Axum `IntoResponse` 自动转换
|
||||
- **EventBus 进程内广播** — `tokio::sync::broadcast` 实现零耦合通信
|
||||
- **ErpModule 插件 trait** — 统一注册路由和事件处理器
|
||||
- **BaseFields 强制多租户** — 所有实体基础字段模板
|
||||
|
||||
### 错误处理链
|
||||
```
|
||||
业务 crate (thiserror) → AppError → IntoResponse → HTTP JSON 响应
|
||||
数据库 (sea_orm::DbErr) → From 转换 → AppError (自动识别 duplicate key → Conflict)
|
||||
```
|
||||
## 2. 关键文件 + 数据流
|
||||
|
||||
错误响应统一格式:`{ "error": "not_found", "message": "资源不存在", "details": null }`
|
||||
|
||||
### 事件总线
|
||||
```
|
||||
发布者: EventBus::publish(DomainEvent) → broadcast channel
|
||||
订阅者: EventBus::subscribe() → Receiver<DomainEvent>
|
||||
事件字段: id(UUIDv7), event_type("user.created"), tenant_id, payload(JSON), timestamp, correlation_id
|
||||
```
|
||||
|
||||
事件类型命名规则:`{模块}.{动作}` 如 `user.created`, `workflow.task.completed`
|
||||
|
||||
### 模块注册
|
||||
```
|
||||
业务模块实现 ErpModule trait → ModuleRegistry::register() →
|
||||
build_router(): 折叠所有模块的 register_routes() → Axum Router
|
||||
register_handlers(): 注册所有模块的事件处理器 → EventBus
|
||||
```
|
||||
|
||||
### 共享类型
|
||||
- `TenantContext` — 中间件注入的租户上下文(tenant_id, user_id, roles, permissions)
|
||||
- `Pagination` / `PaginatedResponse<T>` — 分页查询标准化(每页上限 100)
|
||||
- `ApiResponse<T>` — API 统一信封 `{ success, data, message }`
|
||||
|
||||
## 关联模块
|
||||
|
||||
- **[[erp-server]]** — 消费所有 erp-core 类型和 trait,是唯一组装点
|
||||
- **[[erp-auth]]** — 实现 `ErpModule` trait,发布认证事件
|
||||
- **[[erp-workflow]]** — 实现 `ErpModule` trait,订阅业务事件
|
||||
- **[[erp-message]]** — 实现 `ErpModule` trait,订阅通知事件
|
||||
- **[[erp-config]]** — 实现 `ErpModule` trait
|
||||
- **[[database]]** — 迁移表结构必须与 `BaseFields` 对齐
|
||||
- **[[wasm-plugin]]** — WASM 插件通过 Host Bridge 桥接 EventBus
|
||||
|
||||
## 关键文件
|
||||
### 核心文件
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
@@ -61,9 +31,63 @@
|
||||
| `crates/erp-core/src/types.rs` | BaseFields、Pagination、ApiResponse、TenantContext |
|
||||
| `crates/erp-core/src/lib.rs` | 模块导出入口 |
|
||||
|
||||
## 当前状态
|
||||
### 集成契约
|
||||
|
||||
**已实现,Phase 1 可用。** 但以下部分尚未被 erp-server 集成:
|
||||
- `ModuleRegistry` 未在 `main.rs` 中使用
|
||||
- `EventBus` 未创建实例
|
||||
- `TenantContext` 未通过中间件注入
|
||||
| 方向 | 模块 | 接口 | 触发时机 |
|
||||
|------|------|------|---------|
|
||||
| 提供 → | 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-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) → broadcast channel → Receiver<DomainEvent>
|
||||
事件字段: id(UUIDv7), event_type("user.created"), tenant_id, payload(JSON), timestamp
|
||||
```
|
||||
|
||||
命名规则:`{模块}.{动作}` 如 `user.created`, `workflow.task.completed`
|
||||
|
||||
### 模块注册
|
||||
|
||||
```
|
||||
ErpModule trait → ModuleRegistry::register() →
|
||||
build_router(): 折叠所有模块路由 → Axum Router
|
||||
register_handlers(): 注册事件处理器 → EventBus
|
||||
```
|
||||
|
||||
### 共享类型
|
||||
|
||||
- `TenantContext` — 租户上下文(tenant_id, user_id, roles, permissions, department_ids)
|
||||
- `Pagination` / `PaginatedResponse<T>` — 分页标准化(每页上限 100)
|
||||
- `ApiResponse<T>` — 统一信封 `{ success, data, message }`
|
||||
|
||||
⚡ **不变量**: erp-core 不依赖任何业务 crate,只被依赖
|
||||
⚡ **不变量**: 所有 API 使用 `/api/v1/` 前缀
|
||||
⚡ **不变量**: tenant_id 从 JWT 中间件注入,应用层不可伪造
|
||||
|
||||
## 4. 活跃问题 + 陷阱
|
||||
|
||||
⚠️ crate 内部可用 `anyhow`,但跨 crate 边界必须转 `AppError`
|
||||
⚠️ EventBus 当前为内存 broadcast,outbox 持久化通过后台任务实现
|
||||
|
||||
## 5. 变更记录
|
||||
|
||||
| 日期 | 变更 |
|
||||
|------|------|
|
||||
| 2026-04-23 | 重构为 5 节结构,更新为已完全集成状态 |
|
||||
|
||||
Reference in New Issue
Block a user