feat(health): 添加 erp-health 健康管理模块骨架
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

新建 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:
iven
2026-04-23 19:59:22 +08:00
parent 5ac8e18d74
commit ca50d32f6e
61 changed files with 6853 additions and 1208 deletions

View File

@@ -0,0 +1,710 @@
# 健康管理系统 — erp-health 模块设计规格
> **文档版本**: 1.0
> **日期**: 2026-04-23
> **状态**: 已确认
> **范围**: V1 — 患者管理 + 健康数据 + 预约排班 + 随访管理 + 咨询管理
---
## 1. 项目背景
### 1.1 产品定位
构建一个面向体检中心/医疗机构的**综合型健康管理平台**,以体检中心为数据源,汇集不同情况的患者,提供全生命周期的健康管理服务。
本系统从 ERP 平台底座分叉独立,作为 **Health Management System (HMS)** 产品演进。ERP 底座提供身份权限、工作流、消息通知、系统配置等基础能力,`erp-health` 作为原生 Rust 模块承载所有医疗业务逻辑。
### 1.2 系统架构
```
📱 患者端(微信小程序) ──┐
├──→ 🔀 API 网关 ──→ 🖥️ ERP 后端HMS
👨‍⚕️ 医护端(小程序/H5 ──┘ │ │
│ ├── erp-auth用户/角色/权限)
│ ├── erp-workflow工作流引擎
│ ├── erp-message消息通知
│ ├── erp-config字典/配置)
│ └── erp-health健康管理★ 新增
└──→ 💾 PostgreSQL + Redis
```
**关键决策:**
- ERP 只负责 **PC 管理后台**功能
- 小程序(患者端/医护端)作为**独立系统**开发
- 数据共享通过 **API 网关**实现
- 健康管理使用**原生 Rust 模块**(非 WASM 插件),获得完整的数据库访问和自定义 API 能力
### 1.3 为什么不用 WASM 插件
| 限制 | 影响 |
|------|------|
| 实体上限 20 个 | 综合健康平台轻松超过 |
| JSONB 存储 | 医疗数据需要强类型、索引、关联 |
| 无自定义 API | 趋势分析、统计报表需要专用端点 |
| 无文件上传 | 化验单、体检报告无法存储 |
| WASM 沙箱限制 | 无法引入加密、AI、外部 API |
原生模块遵循现有模式(如 erp-auth、erp-workflow。**注意:**`ErpModule` trait 没有 `register_routes` 方法。模块通过固有方法 `public_routes()``protected_routes()` 暴露路由,在 `erp-server``main.rs` 中通过 `.nest("/api/v1/health", HealthModule::protected_routes())` 集成。通过 EventBus 通信,未来可平滑拆分为独立微服务。
---
## 2. V1 功能范围
| 模块 | 功能 | 页面数 |
|------|------|--------|
| ① 患者与医护管理 | 患者档案、家庭成员、医护档案、患者标签 | 3 |
| ② 健康数据管理 | 体检记录、日常监测、化验报告、趋势分析 | 3 |
| ③ 预约与排班 | 预约管理、医生排班、日历视图 | 2 |
| ④ 随访管理 | 随访任务、随访记录台账 | 2 |
| ⑤ 咨询管理 | 会话管理、对话记录查看/导出 | 2 |
| ⑥ 医护管理 | 医护人员列表 | 1 |
| **合计** | | **13** |
**V2 预留:** 积分商城、数据统计中心、内容管理增强。
---
## 3. 实体模型
### 3.1 设计原则
- 患者和医护的**账号**走 `erp-auth``users` 表,`erp-health` 只存医疗业务扩展字段
- 通过 `user_id` 外键关联 `users`
- 所有表含 `tenant_id`(多租户隔离)、`id`UUIDv7`created_at``updated_at``created_by``updated_by``deleted_at``version`
- 多对多关系使用中间表
### 3.2 实体定义
#### ① 患者与医护管理
**patient — 患者档案**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | UUIDv7 |
| tenant_id | UUID NOT NULL | 租户 ID |
| user_id | UUID FK → users | 关联 erp-auth 账号 |
| name | VARCHAR(100) | 姓名 |
| gender | VARCHAR(10) | 性别 (male/female/other) |
| birth_date | DATE | 出生日期 |
| blood_type | VARCHAR(10) | 血型 (A/B/AB/O/RH-/RH+) |
| id_number | VARCHAR(20) | 身份证号 |
| allergy_history | TEXT | 过敏史 |
| medical_history_summary | TEXT | 病史摘要 |
| emergency_contact_name | VARCHAR(100) | 紧急联系人姓名 |
| emergency_contact_phone | VARCHAR(20) | 紧急联系人电话 |
| status | VARCHAR(20) | 状态 (active/inactive/deceased) |
| verification_status | VARCHAR(20) | 实名认证 (pending/verified/rejected) |
| source | VARCHAR(100) | 来源(体检中心名称) |
| notes | TEXT | 备注 |
| created_at, updated_at, created_by, updated_by, deleted_at, version | — | 标准字段 |
索引:`(tenant_id, name)`, `(tenant_id, status)`, `(tenant_id, id_number) UNIQUE WHERE deleted_at IS NULL`
**patient_family_member — 家庭成员**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | 患者关联 |
| name | VARCHAR(100) | 姓名 |
| relationship | VARCHAR(50) | 关系(父亲/母亲/配偶/子女等) |
| phone | VARCHAR(20) | 电话 |
| birth_date | DATE | 出生日期 |
| notes | TEXT | 备注 |
| 标准 ERP 字段 | — | |
**doctor_profile — 医护档案**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| user_id | UUID FK → users | 关联 erp-auth 账号 |
| department | VARCHAR(100) | 科室 |
| title | VARCHAR(50) | 职称(主任医师/副主任医师/主治医师等) |
| specialty | VARCHAR(200) | 专长 |
| license_number | VARCHAR(50) | 执业证号 |
| bio | TEXT | 简介 |
| online_status | VARCHAR(20) | 在线状态 (online/offline/busy) |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, patient_id)`
**patient_tag — 患者标签**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| name | VARCHAR(50) | 标签名 |
| color | VARCHAR(20) | 颜色值 |
| description | TEXT | 描述 |
| is_system | BOOLEAN | 系统标签(不可删除) |
| 标准 ERP 字段 | — | |
索引:`UNIQUE (tenant_id, name) WHERE deleted_at IS NULL`
**patient_tag_relation — 患者-标签关联**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| tag_id | UUID FK → patient_tag | |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |
| created_by | UUID | |
| updated_by | UUID | |
| deleted_at | TIMESTAMPTZ | 软删除 |
索引:`(tenant_id, patient_id)`, `(tenant_id, tag_id)`
**patient_doctor_relation — 医患关系**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| doctor_id | UUID FK → doctor_profile | |
| relationship_type | VARCHAR(20) | 类型 (primary/consulting) |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |
| created_by | UUID | |
| updated_by | UUID | |
| deleted_at | TIMESTAMPTZ | 软删除 |
索引:`(tenant_id, patient_id)`, `(tenant_id, doctor_id)`
#### ② 健康数据管理
**health_record — 体检/就诊记录**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| record_type | VARCHAR(20) | 类型 (checkup/outpatient/inpatient) |
| record_date | DATE | 记录日期 |
| source | VARCHAR(200) | 来源(体检中心/医院名称) |
| overall_assessment | TEXT | 总体评估 |
| report_file_url | VARCHAR(500) | 报告文件 URL |
| notes | TEXT | 备注 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, patient_id, record_date DESC)`
**vital_signs — 日常监测数据**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| record_date | DATE | 记录日期 |
| systolic_bp_morning | INTEGER | 晨起收缩压 |
| diastolic_bp_morning | INTEGER | 晨起舒张压 |
| systolic_bp_evening | INTEGER | 晚间收缩压 |
| diastolic_bp_evening | INTEGER | 晚间舒张压 |
| heart_rate | INTEGER | 心率 |
| weight | DECIMAL(5,1) | 体重 (kg) |
| blood_sugar | DECIMAL(5,1) | 血糖 (mmol/L) |
| water_intake_ml | INTEGER | 饮水量 (ml) |
| urine_output_ml | INTEGER | 尿量 (ml) |
| notes | TEXT | 备注 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, patient_id, record_date DESC)`
**lab_report — 化验报告**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| report_date | DATE | 报告日期 |
| report_type | VARCHAR(50) | 报告类型(肾功能/血常规/尿常规等) |
| indicators | JSONB | 指标数据 [{name, value, unit, ref_range, is_abnormal}] |
| image_urls | JSONB | 图片 URLs [url1, url2, ...] |
| doctor_interpretation | TEXT | 医生解读 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, patient_id, report_date DESC)`, GIN on `indicators`, `(tenant_id, report_type)`
**health_trend — 健康趋势报告**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| period_start | DATE | 周期开始 |
| period_end | DATE | 周期结束 |
| indicator_summary | JSONB | 指标摘要 |
| abnormal_items | JSONB | 异常项 |
| generation_type | VARCHAR(20) | 生成方式 (auto/manual) |
| report_file_url | VARCHAR(500) | 报告文件 URL |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, patient_id, period_start DESC)`
#### ③ 预约排班
**appointment — 预约记录**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| doctor_id | UUID FK → doctor_profile | |
| appointment_type | VARCHAR(20) | 类型 (dialysis/recheck/outpatient) |
| appointment_date | DATE | 预约日期 |
| start_time | TIME | 开始时间 |
| end_time | TIME | 结束时间 |
| status | VARCHAR(20) | 状态 (pending/confirmed/cancelled/completed/no_show) |
| cancel_reason | TEXT | 取消原因 |
| notes | TEXT | 备注 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, appointment_date, status)`, `(tenant_id, doctor_id, appointment_date)`
**doctor_schedule — 医生排班**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| doctor_id | UUID FK → doctor_profile | |
| schedule_date | DATE | 排班日期 |
| period_type | VARCHAR(20) | 时段 (am/pm/night/full_day) |
| start_time | TIME | 开始时间 |
| end_time | TIME | 结束时间 |
| max_appointments | INTEGER | 最大预约数 |
| current_appointments | INTEGER | 已预约数(默认 0 |
| status | VARCHAR(20) | 状态 (enabled/disabled) |
| 标准 ERP 字段 | — |
索引:`(tenant_id, doctor_id, schedule_date)`, `UNIQUE (tenant_id, doctor_id, schedule_date, period_type) WHERE deleted_at IS NULL`
**预约并发控制:** 创建预约时使用原子 CAS 操作 `UPDATE doctor_schedule SET current_appointments = current_appointments + 1 WHERE id = $1 AND current_appointments < max_appointments RETURNING *`,防止超额预约。
#### ④ 随访管理
**follow_up_task — 随访任务**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| assigned_to | UUID FK → users | 负责医护 |
| follow_up_type | VARCHAR(20) | 类型 (phone/face_to_face/online) |
| planned_date | DATE | 计划日期 |
| status | VARCHAR(20) | 状态 (pending/in_progress/completed/overdue/cancelled) |
| content_template | TEXT | 随访内容模板 |
| related_appointment_id | UUID FK → appointment | 关联预约 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, assigned_to, status)`, `(tenant_id, planned_date, status)`
**follow_up_record — 随访记录**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| task_id | UUID FK → follow_up_task | |
| executed_by | UUID FK → users | 执行医护 |
| executed_date | DATE | 执行日期 |
| result | VARCHAR(20) | 结果 (followed_up/unreachable/refused/other) |
| patient_condition | TEXT | 患者状况 |
| medical_advice | TEXT | 医嘱建议 |
| next_follow_up_date | DATE | 下次随访日期 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, task_id)`, `(tenant_id, executed_date)`
#### ⑤ 咨询管理
**consultation_session — 咨询会话**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| patient_id | UUID FK → patient | |
| doctor_id | UUID FK → doctor_profile | |
| type | VARCHAR(20) | 类型 (customer_service/doctor) |
| status | VARCHAR(20) | 状态 (waiting/active/closed) |
| last_message_at | TIMESTAMPTZ | 最后消息时间 |
| unread_count_patient | INTEGER | 患者未读数 |
| unread_count_doctor | INTEGER | 医生未读数 |
| 标准 ERP 字段 | — | |
索引:`(tenant_id, doctor_id, status)`, `(tenant_id, patient_id, status)`
**consultation_message — 咨询消息**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | UUID PK | |
| tenant_id | UUID NOT NULL | |
| session_id | UUID FK → consultation_session | |
| sender_id | UUID | 发送者 ID |
| sender_role | VARCHAR(20) | 角色 (patient/doctor/system) |
| content_type | VARCHAR(20) | 类型 (text/image/voice/file) |
| content | TEXT | 内容 |
| is_read | BOOLEAN | 已读状态(默认 false |
| created_at | TIMESTAMPTZ | 发送时间 |
| updated_at | TIMESTAMPTZ | |
| created_by | UUID | |
| updated_by | UUID | |
| deleted_at | TIMESTAMPTZ | 软删除(内容审核用) |
| version | INT NOT NULL DEFAULT 1 | 乐观锁 |
索引:`(tenant_id, session_id, created_at)`
**数据增长策略:**`created_at` 按月分区PostgreSQL table partitioning超过 1 年的已关闭会话消息归档到冷存储。
**说明:**
- `patient.user_id` 允许 NULL — 患者可先创建档案(如体检中心导入),后续再绑定 erp-auth 账号
- `consultation_message.sender_id` 引用 `users.id` — 统一使用 erp-auth 用户体系标识发送者
---
## 3.3 状态机定义
### appointment.status 转换
```
pending ──→ confirmed ──→ completed
│ │
│ └──→ no_show预约时间过后系统自动或前台手动触发
└──→ cancelled任意时刻可取消需填 cancel_reason
```
### follow_up_task.status 转换
```
pending ──→ in_progress ──→ completed
│ │
└──→ cancelled └──→ overdue系统定时任务planned_date 已过且仍 pending 自动标记)
```
### consultation_session.status 转换
```
waiting ──→ active第一条消息发送时自动触发──→ closed手动关闭或超时自动关闭
```
### patient.status 转换
```
active ──→ inactive手动停用
active ──→ deceased标记死亡不可逆
inactive ──→ active重新激活
```
### patient.verification_status 转换
```
pending ──→ verified实名认证通过
pending ──→ rejected认证被拒
rejected ──→ pending重新提交认证
```
---
## 4. API 设计
所有端点前缀: `/api/v1/health/`
### 4.1 患者管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/patients` | 患者列表(分页、搜索、标签筛选) |
| POST | `/patients` | 创建患者 |
| GET | `/patients/:id` | 患者详情 |
| PUT | `/patients/:id` | 更新患者 |
| DELETE | `/patients/:id` | 软删除 |
| POST | `/patients/:id/tags` | 管理标签(批量设置) |
| GET | `/patients/:id/health-summary` | 健康摘要 |
| GET | `/patients/:id/family-members` | 家庭成员列表 |
| POST | `/patients/:id/family-members` | 新增家庭成员 |
| PUT | `/patients/:id/family-members/:fid` | 更新家庭成员 |
| DELETE | `/patients/:id/family-members/:fid` | 删除家庭成员 |
| POST | `/patients/:id/doctors` | 分配主治医生 |
| DELETE | `/patients/:id/doctors/:did` | 移除医患关系 |
### 4.2 健康数据
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/patients/:id/vital-signs` | 日常监测列表 |
| POST | `/patients/:id/vital-signs` | 新增监测数据 |
| GET | `/patients/:id/lab-reports` | 化验报告列表 |
| POST | `/patients/:id/lab-reports` | 新增化验报告 |
| GET | `/patients/:id/health-records` | 体检/就诊记录 |
| POST | `/patients/:id/health-records` | 新增记录 |
| GET | `/patients/:id/trends` | 趋势报告 |
| POST | `/patients/:id/trends/generate` | 生成趋势报告 |
| GET | `/patients/:id/trends/:indicator` | 单指标时序数据 |
| PUT | `/patients/:id/vital-signs/:vid` | 更新监测数据 |
| DELETE | `/patients/:id/vital-signs/:vid` | 删除监测数据 |
| PUT | `/patients/:id/lab-reports/:rid` | 更新化验报告 |
| DELETE | `/patients/:id/lab-reports/:rid` | 删除化验报告 |
| PUT | `/patients/:id/health-records/:rid` | 更新体检记录 |
| DELETE | `/patients/:id/health-records/:rid` | 删除体检记录 |
### 4.3 预约排班
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/appointments` | 预约列表 |
| POST | `/appointments` | 创建预约 |
| PUT | `/appointments/:id/status` | 更新状态 |
| GET | `/doctor-schedules` | 排班列表 |
| POST | `/doctor-schedules` | 创建排班 |
| PUT | `/doctor-schedules/:id` | 更新排班 |
| GET | `/doctor-schedules/calendar` | 日历视图 |
### 4.4 随访管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/follow-up-tasks` | 任务列表 |
| POST | `/follow-up-tasks` | 创建任务 |
| PUT | `/follow-up-tasks/:id` | 更新任务 |
| DELETE | `/follow-up-tasks/:id` | 删除任务 |
| POST | `/follow-up-tasks/:id/records` | 填写随访记录 |
| GET | `/follow-up-records` | 随访台账 |
### 4.5 咨询管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/consultation-sessions` | 会话列表 |
| GET | `/consultation-sessions/:id/messages` | 消息记录 |
| PUT | `/consultation-sessions/:id/close` | 关闭会话 |
| POST | `/consultation-messages` | 写入消息API 网关用) |
| GET | `/consultation-sessions/export` | 导出 |
### 4.6 医护管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/doctors` | 医护列表 |
| POST | `/doctors` | 创建医护档案 |
| GET | `/doctors/:id` | 医护详情 |
| PUT | `/doctors/:id` | 更新医护档案 |
| DELETE | `/doctors/:id` | 软删除医护档案 |
---
## 5. 前端页面设计
文件位置: `apps/web/src/pages/health/`
### 5.1 页面清单
| # | 页面 | 文件名 | 类型 |
|---|------|--------|------|
| 1 | 患者列表 | PatientList.tsx | 表格+搜索+标签筛选+导出 |
| 2 | 患者详情 | PatientDetail.tsx | Tab布局基本信息/健康趋势/化验报告/就诊记录/随访记录 |
| 3 | 标签管理 | PatientTagManage.tsx | CRUD+颜色+批量打标 |
| 4 | 日常监测 | VitalSignsList.tsx | 按患者+日期+ECharts趋势折线图 |
| 5 | 化验报告 | LabReportList.tsx | 列表+图片预览+指标详情+解读 |
| 6 | 体检记录 | HealthRecordList.tsx | 类型筛选+报告文件查看/上传 |
| 7 | 预约管理 | AppointmentList.tsx | 列表/日历切换+状态流转 |
| 8 | 排班管理 | DoctorSchedule.tsx | 周/月日历+排班模板 |
| 9 | 随访任务 | FollowUpTaskList.tsx | 任务CRUD+分配+关联工作流 |
| 10 | 随访台账 | FollowUpRecordList.tsx | 按患者/医护/日期筛选+导出 |
| 11 | 会话管理 | ConsultationList.tsx | 列表+未回复统计 |
| 12 | 对话记录 | ConsultationDetail.tsx | 聊天气泡+图片/语音查看+导出 |
| 13 | 医护列表 | DoctorList.tsx | 列表+科室筛选+在线状态 |
### 5.2 技术要点
- **ECharts 趋势图** — 血压/体重/血糖曲线图,按日期范围展示
- **文件上传/预览** — 化验单图片、体检报告 PDF需新增基础能力
- **日历组件** — Ant Design Calendar 用于排班和预约视图
- **聊天 UI** — 消息气泡展示(只读,非实时聊天)
- **导出** — 随访台账、咨询记录导出为 Excel
---
## 6. 事件集成
### 6.1 发布事件
| 事件类型 | 触发时机 | 载荷 |
|----------|----------|------|
| `patient.created` | 创建患者 | `{patient_id, name, tenant_id}` |
| `patient.updated` | 更新患者信息 | `{patient_id, changed_fields}` |
| `appointment.created` | 创建预约 | `{appointment_id, patient_id, doctor_id, date}` |
| `appointment.confirmed` | 确认预约 | `{appointment_id}` |
| `appointment.cancelled` | 取消预约 | `{appointment_id, cancel_reason}` |
| `appointment.completed` | 完成就诊 | `{appointment_id}` |
| `follow_up.created` | 创建随访任务 | `{task_id, patient_id, assigned_to, planned_date}` |
| `follow_up.completed` | 完成随访 | `{task_id, record_id, result}` |
| `lab_report.uploaded` | 上传化验报告 | `{report_id, patient_id, report_type, abnormal_count}` |
| `consultation.opened` | 开启咨询 | `{session_id, patient_id, doctor_id}` |
| `consultation.closed` | 关闭咨询 | `{session_id}` |
| `patient.deceased` | 患者死亡标记 | `{patient_id}` |
| `patient.verified` | 实名认证通过 | `{patient_id, id_number}` |
| `follow_up.overdue` | 随访任务逾期 | `{task_id, patient_id, planned_date}` |
| `doctor.online_status_changed` | 医护在线状态变更 | `{doctor_id, old_status, new_status}` |
**随访记录自动创建后续任务:**`follow_up_record.next_follow_up_date` 不为空时,服务层自动创建新的 `follow_up_task`planned_date = next_follow_up_dateassigned_to 沿用当前医护)。
### 6.2 订阅事件
| 事件类型 | 处理逻辑 |
|----------|----------|
| `workflow.task.completed` | 工作流任务完成时更新随访任务状态 |
| `message.sent` | 消息发送时联动咨询会话的 last_message_at |
---
## 7. 模块结构
```
crates/erp-health/
├── Cargo.toml
├── src/
│ ├── lib.rs ← ErpModule trait + public_routes() / protected_routes()
│ ├── error.rs ← HealthError → AppError
│ ├── state.rs ← HealthState (共享状态)
│ ├── entity/ ← SeaORM Entity
│ │ ├── mod.rs
│ │ ├── patient.rs
│ │ ├── patient_family_member.rs
│ │ ├── patient_tag.rs
│ │ ├── patient_tag_relation.rs
│ │ ├── patient_doctor_relation.rs
│ │ ├── doctor_profile.rs
│ │ ├── health_record.rs
│ │ ├── vital_signs.rs
│ │ ├── lab_report.rs
│ │ ├── health_trend.rs
│ │ ├── appointment.rs
│ │ ├── doctor_schedule.rs
│ │ ├── follow_up_task.rs
│ │ ├── follow_up_record.rs
│ │ ├── consultation_session.rs
│ │ └── consultation_message.rs
│ ├── service/ ← 业务逻辑
│ │ ├── mod.rs
│ │ ├── patient_service.rs
│ │ ├── health_data_service.rs
│ │ ├── appointment_service.rs
│ │ ├── follow_up_service.rs
│ │ └── consultation_service.rs
│ ├── handler/ ← Axum 路由
│ │ ├── mod.rs
│ │ ├── patient_handler.rs
│ │ ├── health_data_handler.rs
│ │ ├── appointment_handler.rs
│ │ ├── follow_up_handler.rs
│ │ └── consultation_handler.rs
│ ├── dto/ ← 请求/响应结构体
│ │ ├── mod.rs
│ │ ├── patient_dto.rs
│ │ ├── health_data_dto.rs
│ │ ├── appointment_dto.rs
│ │ ├── follow_up_dto.rs
│ │ └── consultation_dto.rs
│ └── event.rs ← 事件定义和处理器
```
---
## 8. 权限定义
### 8.1 权限码
| 权限码 | 名称 | 说明 |
|--------|------|------|
| `health.patient.list` | 查看患者列表 | 查看和搜索患者列表、详情 |
| `health.patient.manage` | 管理患者 | 创建、编辑、删除患者 |
| `health.health-data.list` | 查看健康数据 | 查看体检记录、监测数据、化验报告 |
| `health.health-data.manage` | 管理健康数据 | 录入、编辑、删除健康数据 |
| `health.appointment.list` | 查看预约 | 查看预约列表和排班 |
| `health.appointment.manage` | 管理预约 | 创建、确认、取消预约 |
| `health.follow-up.list` | 查看随访 | 查看随访任务和记录 |
| `health.follow-up.manage` | 管理随访 | 创建、分配、完成随访任务 |
| `health.consultation.list` | 查看咨询 | 查看咨询会话和消息记录 |
| `health.consultation.manage` | 管理咨询 | 关闭会话、导出记录 |
| `health.doctor.list` | 查看医护 | 查看医护列表和详情 |
| `health.doctor.manage` | 管理医护 | 创建、编辑医护档案、排班 |
### 8.2 数据范围
| 实体 | 支持的数据范围级别 | 说明 |
|------|-------------------|------|
| patient | self, department, department_tree, all | 医生只能看自己负责的患者或本科室患者 |
| follow_up_task | self, department, department_tree, all | 医护只能看分配给自己的随访任务 |
| appointment | self, department, department_tree, all | 按科室隔离预约数据 |
### 8.3 角色模板
| 角色 | 权限 |
|------|------|
| health_admin | 全部 health.* 权限 |
| doctor | health.patient.list, health.health-data.*, health.appointment.list, health.follow-up.*, health.consultation.list, health.doctor.list |
| nurse | health.patient.list, health.health-data.*, health.follow-up.*, health.appointment.list |
| receptionist | health.patient.*, health.appointment.*, health.doctor.list |
---
## 9. 能力扩展
V1 需要新增以下基础能力(在 erp-core 或独立模块中):
1. **文件上传服务** — 文件存储(本地/OSS、URL 生成、图片缩略图
2. **趋势分析** — 时序数据聚合、异常检测逻辑
3. **报告批注** — 医生对化验报告的解读/批注能力
4. **导出增强** — 健康数据导出为 Excel/PDF
---
## 10. 实施步骤
### Phase 1: 项目初始化
- 拷贝 ERP 到 hms
- 验证编译和构建
### Phase 2: erp-health 骨架
- 创建 crate 结构
- 实现 ErpModule trait + `public_routes()` / `protected_routes()` 固有方法
- 注册到 workspace
### Phase 3: 数据库迁移
- 16 张表14 业务实体 + 2 关联表)的迁移文件
- 索引创建、唯一约束
### Phase 4: 业务逻辑(按域迭代)
- ① 患者与医护管理
- ② 健康数据管理
- ③ 预约排班
- ④ 随访管理
- ⑤ 咨询管理
### Phase 5: 前端页面
- 13 个自定义 React 页面
- 路由注册和侧边栏菜单
### Phase 6: 集成测试
- API 端点测试
- 多租户隔离验证
- 端到端功能验证