@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. **逐章编写** — 每次只写 1-2 个章节,写完确认后继续下一章 3. **最终整合** — 所有章节完成后合并为完整文档 **原因:** 上下文过长会导致输出截断或卡死。分步编写保证每步都能完整输出,且用户可以中途调整方向。 **适用范围:** 超过 200 行的文档、实施计划、设计规格、技术报告等。简短的 bugfix 说明、单页 wiki 更新不受此限制。 ### 2.3 讨论记录 每次发散式讨论(brainstorming、方案探索、需求梳理、技术选型等)**必须建立独立文档**: - **存放位置:** `docs/discussions/YYYY-MM-DD-{主题简称}.md` - **文档格式:** ```markdown # {讨论主题} > 日期: YYYY-MM-DD | 参与者: ... ## 背景 为什么会有这次讨论 ## 讨论要点 - 要点 1 - 要点 2 ## 结论 / 待定 达成的共识或遗留问题 ``` - **时机:** 讨论结束后立即创建,不要积压。如果讨论横跨多个主题,拆分为多份文档。 - **用途:** 作为后续实施的输入和决策追溯的依据,避免"之前讨论过但忘了结论"。 ### 2.4 模块化思维 开发任何功能时先问: 1. **它属于哪个模块?** — 不确定就放到 `erp-core` 共享层 2. **它的接口是什么?** — 先定义 trait,再实现 3. **它需要发什么事件?** — 跨模块通知必须走事件总线 4. **其他模块怎么发现它?** — 通过 `ErpModule` trait 注册 ### 2.5 闭环工作法(强制) 每次改动**必须**按顺序完成以下步骤,不允许跳过: 0. **阅读 Wiki(强制起点)** — 收到任何任务后,**先读 wiki 再动手**: - 读取 `wiki/index.md` 了解项目全貌和当前进度 - 根据任务涉及的范围,读取相关 wiki 页面(`wiki/infrastructure.md`、`wiki/testing.md`、`wiki/wasm-plugin.md` 等) - wiki 中包含实际的环境配置(数据库连接、端口、登录凭据、启动方式),不看 wiki 就无法正确验证 - **违反此步骤 = 盲目工作,浪费时间去猜环境配置,产出不可信** 1. **现状确认(强制)** — 动手之前,先检查代码里已经有什么: - 用 Grep/Glob/Read 工具搜索相关文件,确认哪些能力已存在 - 明确列出"已有"和"缺失",不允许凭印象断言缺失 - 如果不确定现有实现状态,停下来问用户,不要编造 - 违反此步骤 = 所有后续工作可能脱离实际,白费力气 2. **理解需求** — 确认改动的目标模块和影响范围 3. **最小实现** — 只改必要的代码,保持模块边界 4. **验证通过** — 必须全部通过才可继续: - `cargo check` — 编译无错误 - `cargo test --workspace` — 所有测试通过(有相关测试时) - 功能验证 — 启动后端 + 前端服务,在浏览器中实际操作验证改动生效(涉及 API 或 UI 时) - `pnpm build` — 前端生产构建通过(涉及前端时) 5. **提交** — 验证通过后按 §5 规范提交 6. **文档同步** — 更新相关文档(如果涉及架构、接口、模块变化) 7. **推送到仓库** — 提交后立即 `git push`,确保远程仓库同步 **铁律:** - **步骤 0 阅读 Wiki 是绝对起点** — 不读 wiki 就开干 = 连环境配置都不知道,所有验证步骤都是空谈。 - **步骤 1 现状确认是强制起点** — 不检查就开干 = 脱离实际,所有产出不可信。 - **步骤 4 功能验证必须实际操作** — 只看编译通过不算验证,必须启动服务、在浏览器中确认功能正常。 - **步骤 7 推送是强制环节**,不推送就等于没完成。不允许"等一下再推"。 --- ## 3. 实现规则 ### 3.1 错误处理 - **跨 crate 边界**:使用 `thiserror` 定义类型化错误,转换为 `AppError` - **crate 内部**:可以使用 `anyhow`,但**永远不**跨越 crate 边界 - **数据库错误**:通过 `From` 自动转换为 `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` 包装 - 分页使用 `Pagination` + `PaginatedResponse` - 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 代码规范 ```rust // 命名:snake_case (函数/变量), PascalCase (类型/trait), SCREAMING_SNAKE (常量) // 模块公开接口通过 lib.rs 统一导出 // 每个 public 函数和 trait 必须有文档注释 // 异步函数返回 Result 时使用 AppResult 类型别名 // 数据库操作使用 SeaORM 的 Entity + Model + Relation 模式 ``` ### 3.6 TypeScript / React 代码规范 ```typescript // 避免 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 验证命令 ```bash # 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. 提交规范 ``` (): ``` **类型:** - `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/`,不要口头确认后就忘 ### 场景化指令 - 当遇到**新增模块** → 实现 `ErpModule` trait,在 `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 |