基于 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
14 KiB
14 KiB
@wiki/index.md 整个项目对话都使用中文进行,包括文档、代码注释、事件名称等。
HMS 健康管理平台 — 协作与实现规则
Health Management System (HMS) — 面向体检中心/医疗机构的综合健康管理平台。从 ERP 底座分叉独立,继承身份权限/工作流/消息/配置等基础能力,
erp-health原生模块承载医疗业务。
当前阶段: erp-health 模块开发。 设计规格已确认,开始实施。
1. 项目定位
1.1 这是什么
一个 健康管理 + ERP 基础设施 架构的医疗 SaaS 平台:
- 医疗核心 — 患者管理、健康数据、预约排班、随访管理、咨询管理(原生 Rust 模块 erp-health)
- 基础底座 — 身份权限、工作流引擎、消息中心、系统配置(继承自 ERP)
- 多租户 + 私有化 — 默认 SaaS 共享数据库隔离,支持独立 schema 私有部署
- Web 优先 — 浏览器 SPA 是 PC 管理后台主力,小程序(患者端/医护端)独立开发
1.2 决策原则
任何改动都要问:这对健康管理平台的医疗业务和可扩展性有帮助吗?
- ✅ 完善模块接口和 trait 定义 → 最高优先
- ✅ 确保多租户隔离的正确性 → 最高优先
- ✅ 按计划推进 Phase 交付物 → 高优先
- ✅ 清晰的模块边界和事件契约 → 高优先
- ❌ 跳过 Phase 顺序提前实现远期功能 → 禁止
- ❌ 在模块间创建直接耦合 → 永远不做
- ❌ 硬编码租户 ID 或绕过多租户中间件 → 永远不做
- ❌ 过度设计未来才需要的能力 → 永远不做
1.3 架构铁律
| 约束 | 原因 |
|---|---|
| 模块间只通过事件总线和 trait 通信 | 保证模块可独立拆分为微服务 |
所有数据表必须含 tenant_id |
多租户是核心能力,不可事后补 |
| 使用 UUID v7 作为主键 | 时间排序 + 唯一性,分布式友好 |
| 软删除,不硬删除 | ERP 数据不可丢失,审计追溯需要 |
所有 API 使用 /api/v1/ 前缀 |
版本化是 SaaS 产品的基本要求 |
2. 工作风格
2.1 按计划推进
- 严格按 Phase 顺序执行 — Phase 2 依赖 Phase 1 的基础设施
- 每个 Task 完成后立即提交 — 不积压,保持可追溯
- 先测试后实现 — TDD 流程:写失败测试 → 实现 → 通过 → 提交
2.2 分步编写文档(强制)
编写计划、设计文档、实施报告等长文档时,必须分步编写,禁止一次性输出全文:
- 先写大纲 — 确认文档结构和章节划分
- 逐章编写 — 每次只写 1-2 个章节,写完确认后继续下一章
- 最终整合 — 所有章节完成后合并为完整文档
原因: 上下文过长会导致输出截断或卡死。分步编写保证每步都能完整输出,且用户可以中途调整方向。
适用范围: 超过 200 行的文档、实施计划、设计规格、技术报告等。简短的 bugfix 说明、单页 wiki 更新不受此限制。
2.3 讨论记录
每次发散式讨论(brainstorming、方案探索、需求梳理、技术选型等)必须建立独立文档:
- 存放位置:
docs/discussions/YYYY-MM-DD-{主题简称}.md - 文档格式:
# {讨论主题} > 日期: YYYY-MM-DD | 参与者: ... ## 背景 为什么会有这次讨论 ## 讨论要点 - 要点 1 - 要点 2 ## 结论 / 待定 达成的共识或遗留问题 - 时机: 讨论结束后立即创建,不要积压。如果讨论横跨多个主题,拆分为多份文档。
- 用途: 作为后续实施的输入和决策追溯的依据,避免"之前讨论过但忘了结论"。
2.4 模块化思维
开发任何功能时先问:
- 它属于哪个模块? — 不确定就放到
erp-core共享层 - 它的接口是什么? — 先定义 trait,再实现
- 它需要发什么事件? — 跨模块通知必须走事件总线
- 其他模块怎么发现它? — 通过
ErpModuletrait 注册
2.5 闭环工作法(强制)
每次改动必须按顺序完成以下步骤,不允许跳过:
- 阅读 Wiki(强制起点) — 收到任何任务后,先读 wiki 再动手:
- 读取
wiki/index.md了解项目全貌和当前进度 - 根据任务涉及的范围,读取相关 wiki 页面(
wiki/infrastructure.md、wiki/testing.md、wiki/wasm-plugin.md等) - wiki 中包含实际的环境配置(数据库连接、端口、登录凭据、启动方式),不看 wiki 就无法正确验证
- 违反此步骤 = 盲目工作,浪费时间去猜环境配置,产出不可信
- 读取
- 现状确认(强制) — 动手之前,先检查代码里已经有什么:
- 用 Grep/Glob/Read 工具搜索相关文件,确认哪些能力已存在
- 明确列出"已有"和"缺失",不允许凭印象断言缺失
- 如果不确定现有实现状态,停下来问用户,不要编造
- 违反此步骤 = 所有后续工作可能脱离实际,白费力气
- 理解需求 — 确认改动的目标模块和影响范围
- 最小实现 — 只改必要的代码,保持模块边界
- 验证通过 — 必须全部通过才可继续:
cargo check— 编译无错误cargo test --workspace— 所有测试通过(有相关测试时)- 功能验证 — 启动后端 + 前端服务,在浏览器中实际操作验证改动生效(涉及 API 或 UI 时)
pnpm build— 前端生产构建通过(涉及前端时)
- 提交 — 验证通过后按 §5 规范提交
- 文档同步 — 更新相关文档(如果涉及架构、接口、模块变化)
- 推送到仓库 — 提交后立即
git push,确保远程仓库同步
铁律:
- 步骤 0 阅读 Wiki 是绝对起点 — 不读 wiki 就开干 = 连环境配置都不知道,所有验证步骤都是空谈。
- 步骤 1 现状确认是强制起点 — 不检查就开干 = 脱离实际,所有产出不可信。
- 步骤 4 功能验证必须实际操作 — 只看编译通过不算验证,必须启动服务、在浏览器中确认功能正常。
- 步骤 7 推送是强制环节,不推送就等于没完成。不允许"等一下再推"。
3. 实现规则
3.1 错误处理
- 跨 crate 边界:使用
thiserror定义类型化错误,转换为AppError - crate 内部:可以使用
anyhow,但永远不跨越 crate 边界 - 数据库错误:通过
From<sea_orm::DbErr>自动转换为AppError - 验证错误:包含字段级详情,方便 UI 渲染
3.2 数据库操作
- 所有 SeaORM Entity 必须包含:
id,tenant_id,created_at,updated_at,created_by,updated_by,deleted_at,version - 查询时始终带
tenant_id过滤(中间件自动注入) - 更新时检查
version字段实现乐观锁 - 删除使用软删除(设置
deleted_at)
3.3 API 设计
- 所有端点使用
/api/v1/前缀 - 响应统一使用
ApiResponse<T>包装 - 分页使用
Pagination+PaginatedResponse<T> - utoipa 自动生成 OpenAPI 文档
- 租户 ID 从 JWT 中间件注入,不在 API 路径中传递(管理员接口除外)
3.4 事件总线
- 模块间通信只能通过
EventBus - 事件必须持久化到
domain_events表(outbox 模式) - 事件处理失败记录到 dead-letter 存储
- 事件类型命名:
{模块}.{动作}如user.created,workflow.task.completed - 铁律:每个事件必须有至少一个消费者,否则功能不算完成。 新增事件发布时必须同步实现消费者和对应测试。详见
docs/discussions/2026-04-28-architecture-retrospective.md§4。
3.5 Rust 代码规范
// 命名:snake_case (函数/变量), PascalCase (类型/trait), SCREAMING_SNAKE (常量)
// 模块公开接口通过 lib.rs 统一导出
// 每个 public 函数和 trait 必须有文档注释
// 异步函数返回 Result 时使用 AppResult<T> 类型别名
// 数据库操作使用 SeaORM 的 Entity + Model + Relation 模式
3.6 TypeScript / React 代码规范
// 避免 any,优先 unknown + 类型守卫
// 函数组件 + hooks
// 复杂状态收敛到 Zustand store
// API 调用封装到独立的 service 层,不在组件中直接 fetch
// 使用 Ant Design 组件,不自行实现已有组件
// 国际化文案使用 i18n key,不硬编码中文
4. 测试与验证
4.1 测试要求
| 测试类型 | 覆盖目标 | 工具 |
|---|---|---|
| 单元测试 | 每个 service 函数 | #[cfg(test)] + tokio::test |
| 集成测试 | API 端点 → 数据库 | Testcontainers + 真实 PostgreSQL |
| 多租户测试 | 数据隔离验证 | 独立测试 crate |
| E2E 测试 | 前端关键流程 | Playwright |
| 插件测试 | 动态表 CRUD + 租户隔离 | Testcontainers |
4.2 验证命令
# Rust 编译检查
cargo check
# Rust 全量测试
cargo test --workspace
# 后端服务启动
cd crates/erp-server && cargo run
# Docker 环境
cd docker && docker compose up -d
# 桌面端开发
cd apps/desktop && pnpm tauri dev
# 数据库迁移检查
docker exec erp-postgres psql -U erp -c "\dt"
4.3 Phase 完成标准
每个 Phase 完成时必须满足:
cargo check全 workspace 通过cargo test全部通过- PostgreSQL 服务正常运行,迁移自动执行
- 所有迁移可正/反向执行
- API 端点可通过 Swagger UI 测试
- 桌面端可正常启动并展示对应 UI
- 所有代码已提交
5. 提交规范
<type>(<scope>): <description>
类型:
feat— 新功能fix— 修复问题refactor— 重构docs— 文档更新test— 测试相关chore— 杂项(构建、配置等)perf— 性能优化
Scope 对应 crate 或模块名:
| scope | 范围 |
|---|---|
core |
erp-core |
auth |
erp-auth |
workflow |
erp-workflow |
message |
erp-message |
config |
erp-config |
server |
erp-server |
health |
erp-health |
ai |
erp-ai |
dialysis |
erp-dialysis |
plugin |
erp-plugin / erp-plugin-prototype / erp-plugin-test-sample |
assessment |
erp-plugin-assessment |
crm |
erp-plugin-crm |
inventory |
erp-plugin-inventory |
web |
Web 前端 |
ui |
React 组件 |
db |
数据库迁移 |
docker |
Docker 配置 |
示例:
feat(auth): 添加用户管理 CRUD
feat(core): 实现事件总线和模块注册
fix(server): 修复数据库连接池配置
refactor(auth): 拆分 RBAC 和 ABAC 权限模型
chore(docker): 添加 PostgreSQL 健康检查
6. 反模式警告
- ❌ 不要不看 wiki 就开干 — wiki 包含环境配置、数据库连接、启动方式、已知问题,不看就做等于盲猜,浪费时间且产出不可信
- ❌ 不要在业务 crate 之间创建直接依赖 — 只通过事件和 trait 通信
- ❌ 不要跳过多租户中间件 — 所有数据操作必须带
tenant_id过滤 - ❌ 不要硬编码配置值 — 使用 config.toml + 环境变量
- ❌ 不要跳过迁移直接建表 — 所有 schema 变更通过 SeaORM Migration
- ❌ 不要在前端组件中直接调用 HTTP — 封装到 service 层
- ❌ 不要使用
anyhow跨越 crate 边界 — 内部可用,对外必须转AppError - ❌ 不要假设只有单租户 — 从第一天就按多租户设计
- ❌ 不要提前实现远期功能 — 严格按 Phase 计划推进
- ❌ 不要忽略
version字段 — 所有更新操作必须检查乐观锁 - ❌ 不要在动态表 SQL 中拼接用户输入 — 使用
sanitize_identifier防注入 - ❌ 不要在插件 crate 中直接依赖 erp-auth — 权限注册用 raw SQL,保持模块边界
- ❌ 不要在 plugin.toml 中使用与实体名不一致的权限码 —
permissions[].code前缀必须与schema.entities[].name完全一致(如实体customer_tag→ 权限码customer_tag.list/customer_tag.manage,不能写成tag.manage),否则页面 403 - ❌ 不要漏掉实体的
.list权限 — 每个实体必须同时声明.list和.manage,缺少.list导致列表页 403 - ❌ 不要跳过验证直接提交 — 编译/测试/功能验证必须全部通过
- ❌ 不要提交后忘记推送 — 不推送等于没完成,远程仓库必须同步
- ❌ 不要忘记更新文档 — 涉及架构、接口、模块变化时必须同步更新相关文档
- ❌ 不要一次性输出长文档 — 超过 200 行的文档必须分步编写(先大纲 → 逐章 → 整合),否则会因上下文过长卡死
- ❌ 不要忽略讨论记录 — 每次发散式讨论结束后必须建立文档到
docs/discussions/,不要口头确认后就忘
场景化指令
- 当遇到新增模块 → 实现
ErpModuletrait,在erp-server注册 - 当遇到跨模块通信 → 定义事件类型,通过
EventBus发布/订阅 - 当遇到数据查询 → 确保包含
tenant_id过滤,检查软删除条件 - 当遇到新增 API → 添加 utoipa 注解,确保 OpenAPI 文档同步
- 当遇到新增表 → 创建 SeaORM migration + Entity,包含所有标准字段
- 当遇到新增页面 → 使用 Ant Design 组件,i18n key 引用文案
- 当遇到新增业务模块插件 → 参考
wiki/wasm-plugin.md的插件制作完整流程和.claude/skills/plugin-development/SKILL.md,创建 cdylib crate + 实现 Guest trait + 编译为 WASM Component。权限码必须与实体名一致(每个实体声明.list+.manage)
7. 详细参考(wiki)
以下内容已从本文件迁移到 wiki,需要时查阅:
| 主题 | wiki 页面 |
|---|---|
| 目录结构、crate 依赖、技术栈 | wiki/architecture.md §2 |
| 模块开发规范、ErpModule trait、迁移规范 | wiki/architecture.md §3 |
| 安全注意事项(认证/多租户/通用) | wiki/architecture.md §4 |
| UI 布局规范 | wiki/frontend.md §2 |
| 常用命令(Rust/前端/数据库/WASM) | wiki/infrastructure.md §3 |
| 设计文档索引 | wiki/index.md |
| 开发进度、模块状态 | wiki/index.md 关键数字 |
| 环境配置、连接信息、登录凭据 | wiki/infrastructure.md §2 |