- docs/: 设计规格、讨论记录、销售数据、健康管理文档 - scripts/: 辅助脚本 - package.json + pnpm-lock.yaml: monorepo 根配置
19 KiB
HMS 平台基座回顾与演进设计
日期: 2026-04-26 | 状态: Draft | 方法: 三专家多视角评审
1. 概述
1.1 回顾目的
HMS 健康管理平台经过 17 天密集开发(2026-04-10 ~ 2026-04-26),从 ERP 底座演进到包含 16 个 Rust crate、62 个前端页面、27 个小程序页面的综合医疗 SaaS 平台。本次回顾旨在:
- 验证基座设计 — 星形依赖拓扑、ErpModule trait、事件总线、多租户策略是否经得起实践检验
- 评估演进路径 — 从插件开发模式到原生模块开发的决策是否正确
- 识别缺口与风险 — 通过多专家视角发现盲点
- 制定演进路线 — 基于 P0/P1/P2 优先级指导后续迭代
1.2 评审方法
采用三专家独立评审,每个专家从不同视角分析相同的诊断和建议:
| 专家 | 视角 | 关注点 |
|---|---|---|
| 高级系统架构师 | 架构可持续性 | 模块边界、事件可靠性、技术债 |
| 医疗信息化专家 | 临床安全与合规 | 患者安全、PIPL 合规、领域模型 |
| 产品策略专家 | ROI 与开发节奏 | 优先级、技术债量化、路线图现实性 |
1.3 核心结论
基座设计方向正确,但深度不足。 星形依赖、trait 抽象、事件总线等基础架构经受住了实践检验。但在临床安全(危急值告警未闭环)、合规(知情同意缺失)、事件可靠性(无重放机制)方面存在需立即修复的缺口。插件系统已验证可行性但对 HMS 核心业务贡献有限,建议有条件冻结。
2. 基座设计验证
2.1 评分总览
| 维度 | 评分 | 说明 |
|---|---|---|
| 模块边界 | ★★★★ | 星形拓扑零循环依赖,trait 契约清晰 |
| ErpModule trait | ★★★★ | 生命周期/权限/事件/健康检查统一接口 |
| 事件总线 | ★★★☆ | 基础设施扎实(broadcast+outbox),但无重放机制,消费侧不完整 |
| 多租户 | ★★★☆ | JWT→TenantContext 全链路贯通,但缺 RLS 兜底和集成测试 |
| 权限体系 | ★★★★ | RBAC + 行级数据权限 + 按钮级控制 |
| 插件系统 | ★★★☆ | CRUD 场景验证通过,医疗场景天花板明显 |
| API 一致性 | ★★★★ | 统一 envelope、分页、OpenAPI 自动文档 |
| 数据库迁移 | ★★★★ | 59 个迁移,幂等、可回滚、fixup 模式健康 |
| 测试覆盖 | ★☆☆☆ | 36 后端 + 3 前端,覆盖率 < 5% |
| 合规性 | ★☆☆☆ | 知情同意缺失,审计不完整,PIE 加密范围不足 |
2.2 星形依赖拓扑
erp-core (L1)
/ | \ \ \ \
erp-auth workflow message config erp-health erp-plugin erp-ai
\ | / / / / /
erp-server (L3, 组装入口)
erp-core:零业务依赖,纯净基础层- 7 个业务 crate:各只依赖
erp-core,兄弟间无横向依赖 erp-server:唯一组装点,负责路由合并和模块初始化- 无循环依赖 — 架构师验证通过
2.3 ErpModule trait
当前 trait 提供统一的模块接口:
- 身份:
name()/id()/version() - 依赖声明:
dependencies()— 用于拓扑排序启动顺序 - 生命周期:
on_startup()/on_shutdown()/health_check() - 多租户:
on_tenant_created()/on_tenant_deleted() - 权限自描述:
permissions()— 模块声明自己需要的权限码 - 事件订阅:
register_event_handlers()/as_any()
已知张力:路由注册不在 trait 中,而是通过各模块的 inherent method (public_routes() / protected_routes()) 手动在 main.rs 中合并。原因是 Axum 的 Router<S> 泛型约束不适合 trait object。这是务实的妥协,但在添加新模块时有 boilerplate 成本。
2.4 事件总线
实现机制:tokio::sync::broadcast (容量 1024) + domain_events 表持久化(best-effort)+ Outbox relay (5秒轮询,3次重试)
发布侧(已识别的事件类型):
| 模块 | 事件类型数 | 示例 |
|---|---|---|
| erp-auth | 10 | user.login, user.created, role.created |
| erp-workflow | 4 | process_instance.started, task.completed |
| erp-message | 1 | message.sent |
| erp-health | 13 | patient.created, health_data.critical_alert, follow_up.overdue |
| erp-plugin | 2+ | plugin.config.updated, plugin.trigger.* |
消费侧(已识别的订阅者):
| 订阅者 | 订阅方式 | 处理的事件 |
|---|---|---|
| erp-message | subscribe() 全量 |
appointment.*, process_instance.*, task.* |
| erp-health | register_handlers_with_state |
workflow.task.completed |
| erp-plugin 通知 | subscribe_filtered("plugin.trigger.*") |
插件触发通知 |
| outbox relay | 轮询 DB | 重发 pending 事件 |
已识别缺陷:
- 无重放机制 — 内存 broadcast,服务重启后未消费的事件丢失
- 无幂等保护 —
follow_up.overdue每 6 小时检查会重复发布同一条逾期事件 - 全量订阅 — erp-message 使用
subscribe()而非subscribe_filtered(),所有事件都经过消息模块
2.5 多租户
已实现:
- JWT claims 提取
tenant_id→TenantContext注入请求扩展 - 所有 Entity 含
tenant_id字段,BaseFields 统一 - 所有 DomainEvent 携带
tenant_id on_tenant_created()/on_tenant_deleted()钩子(auth 和 health 已实现)- 部门级数据范围(
department_ids在 TenantContext 中)
缺失:
- 无 PostgreSQL RLS policy 作为兜底层
- 无强制 tenant_id 过滤的查询层机制 — 依赖每个 service 手动
.filter() - 当前实际只有 default_tenant,微信登录硬编码使用
default_tenant_id - 无多租户管理 API(创建/配置/迁移)
3. 演进路径回顾
3.1 时间线
4/10-4/16 基座搭建 (Phase 1-6)
→ core → auth → config → workflow → message
→ 全部原生 Rust 模块,30+ 数据库表
4/13-4/18 WASM 插件实验
→ 插件系统设计与实现 (Wasmtime + WIT bindgen)
→ CRM (5实体) → Inventory (6实体) → Freelance → ITOps
→ 证明:CRUD 密集型领域可行,沙盒隔离有效
→ 跨插件数据引用未解决
4/23-4/26 HMS 分叉 — 健康模块原生开发
→ 18+ 强类型实体 (患者/家属/医生/预约/排班/随访/咨询/体征/化验/透析/诊断/积分...)
→ PII 加密 (AES-256-GCM)、脱敏管道
→ AI 模块 (4 SSE 流式端点 + 3 REST 端点)
→ 微信小程序 (27 页面)
→ 按钮级权限控制
3.2 从插件到原生的决策链
原始插件愿景(设计规格 2026-04-13):
- 平台模块原生,行业模块 WASM 插件
- 插件通过 9 个 Host API 函数通信(db_insert/query/update/delete、event_publish、config_get 等)
- 数据存 JSONB 动态表,路由自动生成
- UI 配置驱动,通用 PluginCRUDPage 组件
健康模块原生的 5 个硬限制(设计规格 2026-04-23 §1.3):
| 限制 | 影响 | 不可妥协原因 |
|---|---|---|
| 20 实体上限 | 健康平台轻松超过 | 18+ 实体已是最低合理粒度 |
| JSONB 存储 | 无强类型、无外键约束 | 医疗数据需要引用完整性和精确索引 |
| 无自定义 API | 只有自动 CRUD | 趋势分析/统计报表/日历视图无法实现 |
| 无文件上传 | 沙盒阻止文件系统访问 | 化验单/体检报告需要文件存储 |
| WASM 沙盒限制 | 无 native crypto/外部 API/后台任务 | PII 加密、微信集成、定时任务全部需要 |
3.3 得失评估
得 — 正确的决策:
| 决策 | 收益 |
|---|---|
| 星形依赖拓扑 | 模块独立性强,可独立测试和替换 |
| ErpModule 统一接口 | 新模块注册流程标准化 |
| 事件总线 | 跨模块解耦通信的基础设施已就绪 |
| JWT→TenantContext | 多租户全链路贯通 |
| 健康模块原生 | 不受沙盒限制,加密/文件/后台任务全部可用 |
| 插件实验 | 验证了平台灵活性,CRM/库存可正常使用 |
失 — 需要修正的问题:
| 决策 | 代价 |
|---|---|
| 插件系统投入过大 | 22,000 行代码(41% Rust 总量),对 HMS 核心业务贡献接近零 |
| 积分系统混入 health | 8 实体/12+ 路由,增加合规复杂度和数据泄露面 |
| 事件消费侧忽视 | 13 个事件只有 3 个被消费,危急告警和逾期通知空转 |
| 测试覆盖极薄 | 36 后端 + 3 前端测试,覆盖率 < 5% |
| 合规意识不足 | 知情同意缺失、审计不完整、PIE 加密范围不足 |
4. 三专家评审摘要
4.1 高级系统架构师
诊断准确度:7/10 — 四个张力都真实存在,但优先级和细节有偏差。
关键补充:
| 发现 | 严重程度 |
|---|---|
| WIT 接口是同步调用(阻塞),WASM 运行时嵌入主进程(故障隔离不足) | 架构隐患 |
| EventBus 内存 broadcast 无重放机制,服务重启丢事件 | P1 |
follow_up.overdue 无幂等保护,每 6h 检查重复发布 |
P0 |
erp-message 用 subscribe() 全量订阅,性能隐患 |
P1 |
| RLS 不是 P0,多租户集成测试才是 | 观点 |
| 积分系统(8 独立实体、12+ 路由)不应在 erp-health 内 | 共识 |
| 缺监控/可观测性、数据备份策略、API 版本升级路线图 | 盲点 |
核心原则:先补测试再重构,先修事件再上功能,先验证再加固。
4.2 医疗信息化专家
发现了比原始诊断更深层的临床安全风险。
| 新发现 | 严重程度 |
|---|---|
| 危急值阈值全部硬编码(收缩压 180/80、心率 150/40),不可配置 | P0 |
daily_monitoring 表体征数据不经过危急值检测(合并前遗留) |
P0 |
| 过敏史更新直接覆盖,无变更历史 | P0 |
| 知情同意完全缺失(搜索 consent/同意/授权/隐私 零结果) | P0 — PIPL 违规 |
| 只有身份证号存储加密,姓名/过敏史/诊断/咨询内容明文 | P1 |
| 审计日志不完整 — 只有预约状态变更记录前后值 | P1 |
ip_address 和 user_agent 从未被填充 |
P1 |
| 读操作(查看患者详情/化验报告)完全没有审计记录 | P1 |
诊断记录 icd_code 只做字符串约束,无格式校验,无同行审核 |
P1 |
合规评估:PIPL 第 29 条要求处理敏感个人信息须取得单独同意。医疗数据属于敏感个人信息。知情同意缺失是法律红线。
领域模型建议:积分系统(6 实体 + 2 线下活动实体)应拆分为独立 erp-points 或 erp-engagement 模块,与健康数据分离以降低合规复杂度。
4.3 产品策略专家
开发节奏不可持续但不必恐慌。
| 分析 | 结论 |
|---|---|
| 峰值 68 提交/天,fix 提交占 21.6% | 短期冲刺可以,长期人会耗竭 |
| 41% Rust 代码在插件系统,对核心业务贡献接近零 | 最大 ROI 失衡 |
| 单人 + AI 的"速度幻觉" | 68 提交/天 = 审查不足,积分混入 health 就是例证 |
| 测试覆盖 < 5% | 正确水位不是 80%,而是关键路径不回退(目标 50-80 用例,3-4 天) |
关键风险缓解建议:
- ADR(架构决策记录)强制化
- 医疗安全代码双人外部 review
- 每日提交上限 15 次
- 每月需求裁剪
V2 血透路线图评估:技术储备已够(dialysis_service 286 行骨架在),但缺市场验证。建议先做 3-5 家目标客户调研,确认需求后再做 2 周 MVP 试运行。
5. 共识优先级
5.1 三专家加权共识矩阵
| 议题 | 架构师 | 医疗专家 | 产品策略 | 共识等级 |
|---|---|---|---|---|
| 危急值告警闭环 | P0 | P0 + 硬编码 | P0 | 三方一致 |
| 知情同意 (PIPL) | 未涉及 | P0 | P0 | 两方一致 |
| 审计日志补全 | 未涉及 | P1 | P0 | P0-P1 |
| EventBus 可靠性 | P1 | 未涉及 | P0 | P0-P1 |
| 随访逾期通知 | P0 | P0 | P0 | 三方一致 |
| 积分系统拆分 | 应拆 | 应拆(合规) | 占 19.5% | 三方一致 |
| RLS | 不是 P0 | P1 | P0 | 有分歧 |
| 插件系统 | 有条件冻结 | 未涉及 | 冻结 | 两方一致 |
| 测试覆盖 | 先补测试 | 上线前必修 | 50-80 用例 | 三方一致 |
| V2 血透 | 未涉及 | 缺标准流程 | 先调研 | 两方一致 |
5.2 P0 — 上线前必修(估计 2-3 周)
| 序号 | 项 | 工作量 | 负责 crate | 说明 |
|---|---|---|---|---|
| 1 | 危急值告警消费者 | 1 天 | erp-health + erp-message | health_data.critical_alert → 推送通知给责任医护 |
| 2 | 危急值阈值可配置化 | 2 天 | erp-health | 硬编码阈值改为数据库配置,支持科室/年龄差异化 |
| 3 | daily_monitoring 合并后告警验证 | 1 天 | erp-health | 确认合并到 vital_signs 后所有体征数据都经过告警检测 |
| 4 | 随访逾期通知 | 1 天 | erp-health + erp-message | follow_up.overdue → 催办通知 + 幂等保护 |
| 5 | 知情同意记录 | 3 天 | erp-health | 患者数据处理同意获取和记录机制 |
| 6 | 审计日志补全 | 3 天 | erp-core + erp-health | 临床数据变更记录前后值、读操作审计、IP/UA 填充 |
| 7 | EventBus 持久化增强 | 2 天 | erp-core | 服务重启不丢事件 + overdue 事件幂等 |
5.3 P1 — 治理(2-4 周)
| 序号 | 项 | 工作量 | 说明 |
|---|---|---|---|
| 8 | 积分系统剥离 | 5 天 | 从 erp-health 拆分为独立 erp-engagement crate |
| 9 | 关键路径测试 | 4 天 | 多租户隔离、患者安全路径、预约并发(50-80 用例) |
| 10 | 插件系统冻结声明 | 0.5 天 | 保留代码,README 声明实验性,不再投入 |
| 11 | erp-message 改用 subscribe_filtered |
1 天 | 减少无效事件传递 |
| 12 | 统一事件消费模式 | 2 天 | 消除 register_event_handlers vs on_startup 双路径 |
| 13 | 过敏史变更历史 | 1 天 | 更新时记录旧值 |
5.4 P2 — 扩展(后续迭代)
| 序号 | 项 | 前置条件 |
|---|---|---|
| 14 | PostgreSQL RLS | P1 测试覆盖完成 |
| 15 | 血透专科 | 3-5 家客户调研完成 |
| 16 | OCR 化验单提取 | 血透验证后 |
| 17 | IM 咨询 | 血透验证后 |
| 18 | health 模块按子域重组目录 | P1 测试覆盖完成 |
| 19 | 前端测试覆盖提升 | P1 后端测试完成 |
| 20 | 动态菜单系统 | 现有计划可用 |
6. 风险与缓解
6.1 开发模式风险
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| 单人认知单点 | 一人理解 16 个 crate,bus factor = 1 | ADR 强制化,关键决策留文档 |
| AI 生成"编译对但逻辑错" | 危急值阈值硬编码、积分混入 health 就是例证 | 医疗安全代码双人外部 review |
| 速度幻觉 | 68 提交/天 = 审查不足 | 每日提交上限 15 次 |
| AI 回音壁 | AI 不质疑需求合理性 | 每月需求裁剪,引入真实用户反馈 |
6.2 临床安全风险
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| 危急值告警未闭环 | 危急体征值无人响应,可致患者安全事故 | P0-1:实现消费者 + 阈值可配置 |
| 逾期随访无催办 | 患者失访,影响医疗质量指标 | P0-4:实现通知 + 幂等保护 |
| 过敏史无变更记录 | 无法追溯过敏史变更,用药风险 | P1-13:添加变更历史 |
| 告警阈值硬编码 | 无法适应儿科/老年科/血透科不同范围 | P0-2:数据库配置 |
6.3 合规风险
| 风险 | 法规依据 | 缓解措施 |
|---|---|---|
| 知情同意缺失 | PIPL 第 29 条 | P0-5:实现同意记录机制 |
| 审计不完整 | 医疗机构信息化建设要求 | P0-6:补全审计日志 |
| PIE 加密范围不足 | PIPL 第 51 条 | P1:扩展加密到姓名/过敏史/诊断 |
| 数据删除权缺失 | PIPL 第 47 条 | P2:实现患者数据导出/删除 |
6.4 架构风险
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| EventBus 无重放 | 服务重启丢事件 | P0-7:增强持久化 |
| 全量订阅 | 性能隐患,所有事件经消息模块 | P1-11:改用过滤订阅 |
| 路由手动合并 | 新模块 boilerplate 成本 | 长期:ErpModule trait v2 |
| erp-health 过大 | 18+ 实体,维护复杂度上升 | P2-18:按子域重组 |
7. 附录
7.1 关键文件索引
| 文件 | 说明 |
|---|---|
crates/erp-core/src/module.rs |
ErpModule trait + ModuleRegistry (拓扑排序) |
crates/erp-core/src/events.rs |
EventBus 实现 (broadcast + outbox) |
crates/erp-core/src/types.rs |
TenantContext, BaseFields, Pagination |
crates/erp-core/src/rbac.rs |
权限/角色检查 |
crates/erp-server/src/main.rs |
服务组装和手动路由合并 |
crates/erp-server/src/state.rs |
AppState + FromRef 桥接 |
crates/erp-server/src/outbox.rs |
Outbox relay (5s 轮询, 3 次重试) |
crates/erp-auth/src/middleware/jwt_auth.rs |
JWT 认证 + TenantContext 注入 |
crates/erp-health/src/module.rs |
HealthModule (ErpModule 实现 + 后台任务) |
crates/erp-health/src/event.rs |
健康模块事件订阅 |
crates/erp-health/src/crypto.rs |
AES-256-GCM 加密 |
crates/erp-health/src/service/masking.rs |
PII 脱敏管道 |
crates/erp-plugin/src/engine.rs |
WASM 插件引擎 |
docs/superpowers/specs/2026-04-13-wasm-plugin-system-design.md |
插件系统设计规格 |
docs/superpowers/specs/2026-04-23-health-management-module-design.md |
健康模块设计规格 |
docs/discussions/2026-04-18-plugin-platform-brainstorm.md |
插件平台演进讨论 |
7.2 迁移历史时间线
| 日期 | 迁移范围 | 说明 |
|---|---|---|
| 4/10-11 | 核心平台 | 租户、用户、凭证、角色、权限、组织、部门、岗位 |
| 4/12 | 配置 + 工作流 | 字典、菜单、设置、编号规则 + 流程定义/实例/令牌/任务 |
| 4/13 | 消息 + 审计 | 模板、消息、订阅 + 审计日志 |
| 4/14 | 修复 | 唯一索引与软删除冲突、标准字段补全 |
| 4/16 | 领域事件 | domain_events 表 |
| 4/17 | 插件系统 | 插件表、动态表 |
| 4/18 | 搜索 + 权限 | pg_trgm、实体注册表、数据范围 |
| 4/19 | 关联修复 | 用户部门、CRM 修复、插件市场 |
| 4/23 | 健康表 | 患者、微信用户、文章 |
| 4/24 | 索引修复 | 3 个 fixup 迁移 |
| 4/25 | 健康扩展 | 患者ID哈希、医生名、透析/化验增强、AI 表、积分 |
| 4/26 | 业务改进 | 诊断、列重命名、daily_monitoring 合并、菜单种子 |
总计:59 个迁移,17 天内。 fixup 迁移模式健康(不编辑旧迁移,单独修复)。
7.3 项目统计快照 (2026-04-26)
| 指标 | 值 |
|---|---|
| Rust crate 数 | 16 |
| Rust 代码行 | ~57,000 |
| 前端文件数 | 174 (TSX/TS) |
| 前端页面 | 62 |
| 小程序页面 | 27 |
| 数据库迁移 | 59 |
| 数据库表 | 30 基础 + 18 健康 + 3 AI |
| 后端测试 | 36 |
| 前端单元测试 | 3 |
| Git 提交 | 237 |
| 开发周期 | 17 天 |
本文档由三专家多视角评审生成,作为 HMS 平台基座演进的参考基准。后续实施计划将基于本文档的优先级排序展开。