docs: 全局文档梳理归档 — 删除过期文件 + 归档 V1/早期设计 + wiki 数据校正 + CLAUDE.md 规则优化
**根目录清理:** - 删除 CLAUDE-1.md(ZCLAW 旧项目配置,HMS 已完全脱离) - 移动 DESIGN.md → docs/archive/(ERP 旧设计系统) - 删除 plans/ 98 个临时会话计划文件 **归档重组:** - V1 审计(12 文件)→ docs/archive/audits-v1/ - 早期 CRM/插件迭代设计(13 文件)→ docs/archive/superpowers-early/ - 已完成/已取代设计(28 文件)→ docs/archive/superpowers-completed/ - 早期讨论/测试报告 → docs/archive/discussions-early/ + test-reports-early/ - QA 重复文件清理(3 个旧版 result 文件) **wiki 数据校正:** - 迁移数 137→145,源文件 599→649,提交数 720→800+ - 小程序文件 124→163,Web 前端 297→332 - 后端测试 999→943(实际统计),权限码 75+→128 - 文档索引新增归档目录说明 **CLAUDE.md 规则优化:** - §2.5 闭环工作法:提交+文档+推送三合一 + wiki 更新触发条件 - §2.6 Feature DoD:新增文档一致性检查项 - §6 反模式:新增 wiki 更新滞后/推送不及时警告
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
# HMS 功能审计 — Phase 0: 基线快照
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 全系统
|
||||
|
||||
## 环境信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| Git HEAD | `84fafb0bc5bf3d742f4136c78288a3d06678fc4d` |
|
||||
| 最新提交 | `fix(web+health): 修复咨询轮询 temp ID 400 + 健康数据统计 500` |
|
||||
| 总提交数 | 409 |
|
||||
|
||||
## 编译状态
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| `cargo check --workspace` | **通过**(有警告) |
|
||||
| 编译警告总数 | 40 个 |
|
||||
| 受影响 crate | erp-core(1), erp-plugin(11), erp-health(11), erp-ai(6), erp-dialysis(1), erp-workflow(2), erp-server(1) |
|
||||
|
||||
### 警告分类
|
||||
|
||||
**未使用字段(9 处)**:
|
||||
| 文件 | 字段 | 状态 |
|
||||
|------|------|------|
|
||||
| erp-plugin `host.rs` | `chk`(×2), `tenant_id`, `user_id` | #[allow(dead_code)] 已抑制 |
|
||||
| erp-health 多处 | `message`, `usage`, `input_tokens`, `output_tokens`, `check_result`, `total` | 编译器警告 |
|
||||
| erp-server `analytics.rs` | `timestamp` | 编译器警告 |
|
||||
| erp-health | `RefRow` struct 从未构造 | 编译器警告 |
|
||||
|
||||
**未使用导入(18 处)**:分布在 erp-health(7)、erp-plugin(3)、erp-ai(2)、erp-workflow(2)、erp-dialysis(1)、erp-core(1)。
|
||||
|
||||
**未使用变量(7 处)**:`user_id`, `total`, `tenant_id`, `plugin_resp`, `plugin_id`, `final_nodes`, `entity`。
|
||||
|
||||
## 测试状态
|
||||
|
||||
### 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试总数 | **772 个函数** |
|
||||
| 通过 | **753(97.5%)** |
|
||||
| 失败 | **9(1.2%)** |
|
||||
| 跳过 | 0 |
|
||||
|
||||
### 失败测试(全部因 `blind_indexes` 表缺失)
|
||||
|
||||
| 测试 | 文件 |
|
||||
|------|------|
|
||||
| `test_create_patient` | `health_patient_tests.rs` |
|
||||
| `test_patient_pii_encrypted` | `health_patient_tests.rs` |
|
||||
| `test_dialysis_create_without_patient_returns_error` | `health_dialysis_tests.rs` |
|
||||
| `test_cross_tenant_data_integrity` | `health_pii_encryption_tests.rs` |
|
||||
| `test_patient_detail_returns_decrypted_fields` | `health_pii_encryption_tests.rs` |
|
||||
| `test_patient_hmac_search_by_phone` | `health_pii_encryption_tests.rs` |
|
||||
| `test_patient_list_hides_tier1_fields` | `health_pii_encryption_tests.rs` |
|
||||
| `test_patient_tier1_fields_encrypted_in_db` | `health_pii_encryption_tests.rs` |
|
||||
| `test_tenant_isolation_encrypted_patient` | `health_pii_encryption_tests.rs` |
|
||||
|
||||
**根因**:测试数据库缺少 `blind_indexes` 表(迁移未执行),非代码逻辑错误。
|
||||
|
||||
### 模块测试分布
|
||||
|
||||
| Crate | 单元测试 | 集成测试 | 总计 | 通过率 |
|
||||
|-------|---------|---------|------|--------|
|
||||
| erp-core | 74 | — | 74 | 100% |
|
||||
| erp-auth | 41 | 3 | 44 | 100% |
|
||||
| erp-config | 78 | — | 78 | 100% |
|
||||
| erp-workflow | 63 | 4 | 67 | 100% |
|
||||
| erp-message | 72 | — | 72 | 100% |
|
||||
| erp-health | 159 | 144 | 303 | 97% |
|
||||
| erp-ai | 36 | — | 36 | 100% |
|
||||
| erp-dialysis | 10 | 15 | 25 | 93% |
|
||||
| erp-plugin | 78 | 2 | 80 | 100% |
|
||||
| erp-server | — | 153 | 153 | 94% |
|
||||
| **合计** | **611** | **153** | **772** | **97.5%** |
|
||||
|
||||
## 路由统计
|
||||
|
||||
| 模块 | 路由数 |
|
||||
|------|--------|
|
||||
| erp-health | 169 |
|
||||
| erp-plugin | 38 |
|
||||
| erp-config | 26 |
|
||||
| erp-auth | 33(含 4 个公开路由) |
|
||||
| erp-workflow | 17 |
|
||||
| erp-ai | 12 |
|
||||
| erp-dialysis | 12 |
|
||||
| erp-message | 13 |
|
||||
| erp-server | 8(含 4 个公开路由) |
|
||||
| **合计** | **328**(8 个公开 + 320 个受保护) |
|
||||
|
||||
## 代码规模
|
||||
|
||||
| 维度 | 值 |
|
||||
|------|-----|
|
||||
| Rust 源文件 | 462 个 |
|
||||
| Rust 代码行数 | ~77,000 行 |
|
||||
| Web 前端文件 | 163 个 |
|
||||
| 小程序文件 | 125 个 |
|
||||
| 数据库迁移 | 96 个 |
|
||||
| Rust crate | 18 个 |
|
||||
|
||||
## 基线结论
|
||||
|
||||
1. **系统整体健康**:编译通过,97.5% 测试通过
|
||||
2. **单一阻塞问题**:9 个测试因 `blind_indexes` 表缺失失败,需执行迁移
|
||||
3. **技术债务低**:仅 40 个编译警告(多为未使用导入/变量),无 `unimplemented!` 或 `todo!` 宏
|
||||
4. **规模庞大**:233 个后端路由、40 个前端页面,审计工作量较大
|
||||
@@ -1,252 +0,0 @@
|
||||
# HMS 功能审计 — Phase 1: 功能清单与路由映射
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 后端 + Web + 小程序三端
|
||||
|
||||
## 总览
|
||||
|
||||
| 维度 | 数量 |
|
||||
|------|------|
|
||||
| 后端路由 | 328 个(8 公开 + 320 受保护) |
|
||||
| Web 前端 API 调用 | 235 个 |
|
||||
| 小程序 API 调用 | 76 个 |
|
||||
| Web 页面路由 | 38 个 |
|
||||
| 小程序页面 | 40 个(31 患者 + 9 医护) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 模块路由分布
|
||||
|
||||
| 模块 | 后端路由 | Web API | 小程序 API | 状态 |
|
||||
|------|---------|---------|-----------|------|
|
||||
| erp-auth | 33 | 32 | 3 | 分叉正常(Web=管理端,MP=微信登录) |
|
||||
| erp-health | 169 | 124 | 57 | 覆盖广泛 |
|
||||
| erp-ai | 12 | 8 | 2 | Web 为主 |
|
||||
| erp-dialysis | 12 | 6 | 0 | **MP 缺失** |
|
||||
| erp-config | 26 | 26 | 0 | Web 专属(管理功能) |
|
||||
| erp-workflow | 17 | 14 | 0 | Web 专属(管理功能) |
|
||||
| erp-message | 13 | 8 | 0 | Web 专属 |
|
||||
| erp-plugin | 38 | 35 | 0 | Web 专属 |
|
||||
| erp-server | 8 | 1 | 1 | SSE + 健康检查 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 三端对齐矩阵 — 健康模块(核心业务)
|
||||
|
||||
### 2.1 患者管理
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET /health/patients` | ✓ list | ✓ listPatients + getPatients | ALIGNED |
|
||||
| `POST /health/patients` | ✓ create | ✓ createPatient | ALIGNED |
|
||||
| `GET /health/patients/{id}` | ✓ get | ✓ getPatient | ALIGNED |
|
||||
| `PUT /health/patients/{id}` | ✓ update | ✓ updatePatient | ALIGNED |
|
||||
| `DELETE /health/patients/{id}` | ✓ delete | — | WEB-ONLY(预期) |
|
||||
| `POST /health/patients/{id}/tags` | ✓ manageTags | — | WEB-ONLY(预期) |
|
||||
| `GET /health/patient-tags` | ✓ listTags | ✓ listPatientTags | ALIGNED |
|
||||
| `POST/PUT/DELETE /health/patient-tags` | ✓ CRUD | — | WEB-ONLY(预期) |
|
||||
| `GET /health/patients/{id}/health-summary` | — | ✓ getHealthSummary | **MP-ONLY** |
|
||||
| `GET /health/patients/{id}/family-members` | ✓ list | — | WEB-ONLY |
|
||||
| `POST/PUT/DELETE .../family-members` | ✓ CRUD | ✓ (pages) | MP 有独立页面 |
|
||||
| `POST .../doctors` (assign) | ✓ | — | WEB-ONLY |
|
||||
| `DELETE .../doctors/{did}` | ✓ | — | WEB-ONLY |
|
||||
|
||||
### 2.2 健康数据
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET/POST/PUT/DELETE .../vital-signs` | ✓ 全 CRUD | ✓ 仅 create(inputVitalSign) | 差异正常 |
|
||||
| `GET /health/vital-signs/today` | — | ✓ getTodaySummary | **MP-ONLY** |
|
||||
| `GET /health/vital-signs/trend` | — | ✓ getTrend | **MP-ONLY** |
|
||||
| `GET .../trends` | ✓ listTrends | — | WEB-ONLY |
|
||||
| `GET .../trends/{indicator}` | ✓ timeseries | — | WEB-ONLY |
|
||||
| `POST .../trends/generate` | ✓ | — | **ORPHAN**(未见前端调用) |
|
||||
| `GET/POST .../lab-reports` | ✓ 全 CRUD | ✓ list + get | MP 只读 |
|
||||
| `PUT .../lab-reports/{id}/review` | ✓ review | ✓ review(医护端) | ALIGNED |
|
||||
| `GET/POST/PUT/DELETE .../health-records` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
| `GET/POST/PUT/DELETE .../diagnoses` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
| `GET/POST/PUT/DELETE .../daily-monitoring` | ✓ 全 CRUD | ✓ create + list | MP 仅创建+列表 |
|
||||
| `GET/POST/PUT/DELETE .../medications` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
| `GET/POST/PUT/DELETE .../medication-reminders` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
|
||||
### 2.3 预约管理
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET /health/appointments` | ✓ list | ✓ list + doctor list | ALIGNED |
|
||||
| `POST /health/appointments` | ✓ create | ✓ create | ALIGNED |
|
||||
| `GET /health/appointments/{id}` | ✓ get | ✓ get | ALIGNED |
|
||||
| `PUT .../status` | ✓ updateStatus | ✓ cancelAppointment | ALIGNED |
|
||||
| `GET/POST/PUT /health/doctor-schedules` | ✓ | ✓ get + calendar | Web 有管理 CRUD |
|
||||
| `GET .../calendar` | ✓ calendar | ✓ calendarView | ALIGNED |
|
||||
| `GET /health/doctors` | ✓ list | ✓ list | ALIGNED |
|
||||
| `POST/PUT/DELETE /health/doctors` | ✓ CRUD | — | WEB-ONLY(预期) |
|
||||
|
||||
### 2.4 随访管理
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET/POST/PUT/DELETE .../follow-up-tasks` | ✓ 全 CRUD | ✓ list + get | Web 有管理,MP 只读 |
|
||||
| `POST .../batch-create/assign/complete` | ✓ 批量操作 | — | WEB-ONLY |
|
||||
| `POST .../records` (create) | ✓ | ✓ submit + doctor create | ALIGNED |
|
||||
| `GET .../follow-up-records` | ✓ list | ✓ list | ALIGNED |
|
||||
| `GET/POST/PUT/DELETE .../follow-up-templates` | ✓ 全 CRUD | — | WEB-ONLY |
|
||||
|
||||
### 2.5 咨询管理
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET/POST .../consultation-sessions` | ✓ list + create | ✓ list + doctor list | ALIGNED |
|
||||
| `GET .../{id}` | ✓ get | ✓ get | ALIGNED |
|
||||
| `GET .../{id}/messages` | ✓ list | ✓ list + doctor list | ALIGNED |
|
||||
| `POST .../consultation-messages` | ✓ create | ✓ send + doctor send | ALIGNED |
|
||||
| `PUT .../{id}/close` | ✓ close | ✓ doctor close | ALIGNED |
|
||||
| `PUT .../{id}/read` | ✓ markRead | ✓ markRead | ALIGNED |
|
||||
| `GET .../export` | ✓ export | — | WEB-ONLY(预期) |
|
||||
| `GET .../doctor/dashboard` | — | ✓ doctor dashboard | **MP-ONLY** |
|
||||
|
||||
### 2.6 内容管理(文章)
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET /health/articles` | ✓ list(全状态) | ✓ list(仅 published) | 角色分叉(正常) |
|
||||
| `GET /health/articles/{id}` | ✓ get | ✓ getDetail | ALIGNED |
|
||||
| `POST/PUT/DELETE /health/articles` | ✓ 全 CRUD | — | WEB-ONLY(预期) |
|
||||
| `POST .../submit/approve/reject/unpublish` | ✓ 审核流程 | — | WEB-ONLY(预期) |
|
||||
| `POST .../view` | ✓ view | — | WEB-ONLY |
|
||||
| `GET .../revisions` | ✓ revisions | — | WEB-ONLY |
|
||||
| `GET/POST/PUT/DELETE .../article-categories` | ✓ CRUD | ✓ list | MP 只读 |
|
||||
| `GET/POST/PUT/DELETE .../article-tags` | ✓ CRUD | — | WEB-ONLY |
|
||||
|
||||
### 2.7 积分商城
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET /health/points/account` | — | ✓ getAccount | **MP-ONLY** |
|
||||
| `POST /health/points/checkin` | — | ✓ dailyCheckin | **MP-ONLY** |
|
||||
| `GET /health/points/checkin/status` | — | ✓ getCheckinStatus | **MP-ONLY** |
|
||||
| `GET .../products` | ✓ admin + patient | ✓ listProducts | ALIGNED |
|
||||
| `POST .../exchange` | — | ✓ exchangeProduct | **MP-ONLY** |
|
||||
| `GET .../orders` | ✓ admin list | ✓ listMyOrders | 角色分叉(正常) |
|
||||
| `GET .../transactions` | — | ✓ listMyTransactions | **MP-ONLY** |
|
||||
| `POST .../verify` | ✓ verifyOrder | — | WEB-ONLY |
|
||||
| `GET .../offline-events` | ✓ admin + patient | ✓ list | ALIGNED |
|
||||
| `POST .../offline-events/{id}/register` | — | ✓ registerEvent | **MP-ONLY** |
|
||||
| `GET/POST/PUT/DELETE .../admin/points/rules` | ✓ CRUD | — | WEB-ONLY(预期) |
|
||||
| `GET/POST/PUT/DELETE .../admin/points/products` | ✓ CRUD | — | WEB-ONLY(预期) |
|
||||
| `GET .../admin/points/orders` | ✓ list | — | WEB-ONLY(预期) |
|
||||
| `GET .../admin/points/statistics` | ✓ | — | WEB-ONLY(预期) |
|
||||
| `GET .../admin/offline-events` | ✓ admin CRUD | — | WEB-ONLY(预期) |
|
||||
|
||||
### 2.8 告警系统
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET /health/alerts` | ✓ list | ✓ list + doctor list | ALIGNED |
|
||||
| `PUT .../acknowledge/dismiss/resolve` | ✓ | ✓ doctor acknowledge/dismiss/resolve | ALIGNED |
|
||||
| `GET/POST/PUT .../alert-rules` | ✓ 全 CRUD | — | WEB-ONLY(预期) |
|
||||
| `GET/POST .../critical-alerts` | ✓ list/get/acknowledge | — | **WEB-ONLY** |
|
||||
| `GET/POST/PUT/DELETE .../critical-value-thresholds` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
|
||||
### 2.9 设备与数据采集
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET .../devices` | ✓ list | — | WEB-ONLY |
|
||||
| `DELETE .../devices/{id}` | ✓ unbind | — | WEB-ONLY |
|
||||
| `POST .../device-readings/batch` | ✓ batchCreate | ✓ uploadReadings | ALIGNED |
|
||||
| `GET .../device-readings` | ✓ query | ✓ query | ALIGNED |
|
||||
| `GET .../device-readings/hourly` | ✓ hourly | ✓ queryHourly | ALIGNED |
|
||||
|
||||
### 2.10 AI 分析
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `POST /ai/analyze/*` (4 个 SSE) | — | — | **ORPHAN**(管理端未接入) |
|
||||
| `GET /ai/analysis/history` | ✓ list | ✓ list | ALIGNED |
|
||||
| `GET /ai/analysis/{id}` | ✓ get | ✓ getDetail | ALIGNED |
|
||||
| `GET/POST .../prompts` | ✓ CRUD | — | WEB-ONLY(预期) |
|
||||
| `POST .../prompts/{id}/activate/rollback` | ✓ | — | WEB-ONLY |
|
||||
| `GET .../usage/overview + by-type` | ✓ | — | WEB-ONLY |
|
||||
|
||||
### 2.11 透析管理
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET .../dialysis-records` | ✓ list | — | **WEB-ONLY** |
|
||||
| `POST/PUT/DELETE .../dialysis-records` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
| `PUT .../dialysis-records/{id}/review` | ✓ review | — | **WEB-ONLY** |
|
||||
| `GET/POST/PUT/DELETE .../dialysis-prescriptions` | ✓ 全 CRUD | — | **WEB-ONLY** |
|
||||
| `GET .../admin/statistics/dialysis` | ✓ | — | **WEB-ONLY** |
|
||||
|
||||
### 2.12 知情同意
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET .../consents` | ✓ list | — | **WEB-ONLY** |
|
||||
| `POST .../consents` (grant) | ✓ | — | **WEB-ONLY** |
|
||||
| `PUT .../consents/{id}/revoke` | ✓ | — | **WEB-ONLY** |
|
||||
|
||||
### 2.13 统计仪表盘
|
||||
|
||||
| 后端路由 | Web | 小程序 | 状态 |
|
||||
|----------|-----|--------|------|
|
||||
| `GET .../admin/statistics/*` (9 个) | ✓ 全部 | ✓ 3 个(doctor 端) | Web 完整,MP 部分覆盖 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 差距模式摘要
|
||||
|
||||
### 3.1 预期分叉(管理员 vs 患者角色)
|
||||
|
||||
以下差异是**正常的角色分叉**,Web 面向管理员,小程序面向患者/医护:
|
||||
|
||||
- 用户/角色/组织管理 → 仅 Web
|
||||
- 系统配置(字典/菜单/设置/主题/语言/编号) → 仅 Web
|
||||
- 工作流引擎 → 仅 Web
|
||||
- 消息管理 → 仅 Web
|
||||
- 插件系统 → 仅 Web
|
||||
- 微信登录 → 仅小程序
|
||||
- 积分签到/兑换 → 仅小程序
|
||||
- BLE 设备同步 → 仅小程序
|
||||
- 每日摘要/趋势 → 仅小程序
|
||||
- 医生仪表盘 → 仅小程序(医护端)
|
||||
|
||||
### 3.2 需关注的差距
|
||||
|
||||
| 差距 | 影响范围 | 优先级 |
|
||||
|------|---------|--------|
|
||||
| 透析管理 — 小程序完全无入口 | 患者无法在移动端查看透析记录 | P1 |
|
||||
| 知情同意 — 小程序无入口 | 患者无法在移动端管理同意书 | P1 |
|
||||
| AI 分析 SSE 端点 — 两端都无管理 UI 调用 | AI 功能可能仅通过直接 API 测试使用 | P2 |
|
||||
| 药物管理 — 小程序有页面但无 API 调用对应 | MP 药物页面可能使用其他 API 或硬编码 | P2 |
|
||||
| 趋势生成 `POST .../trends/generate` — 无前端调用 | 后台功能可能仅通过定时任务触发 | P3 |
|
||||
| 危急值告警/阈值管理 — 小程序无入口 | 仅 Web 管理端可操作 | P3 |
|
||||
| 健康记录 CRUD — 小程序无入口 | 患者移动端无法查看健康档案 | P2 |
|
||||
| 诊断记录 CRUD — 小程序无入口 | 患者移动端无法查看诊断 | P2 |
|
||||
|
||||
### 3.3 后端孤立路由(无任何前端调用者)
|
||||
|
||||
| 路由 | 说明 |
|
||||
|------|------|
|
||||
| `POST /health/patients/{id}/trends/generate` | 趋势报告生成,可能为内部任务 |
|
||||
| `POST /ai/analyze/*` (4 个 SSE) | AI 分析接口,可能通过 API 工具直接调用 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 功能完成度评估
|
||||
|
||||
| 功能域 | 后端 | Web | 小程序 | 整体完成度 |
|
||||
|--------|------|-----|--------|-----------|
|
||||
| 患者管理 | 100% | 100% | 85%(无删除) | 95% |
|
||||
| 医生/排班 | 100% | 100% | 40%(只读) | 80% |
|
||||
| 健康数据 | 100% | 100% | 60%(体征+化验+监测) | 87% |
|
||||
| 预约管理 | 100% | 100% | 90% | 97% |
|
||||
| 随访管理 | 100% | 100% | 70%(医护端完善) | 90% |
|
||||
| 咨询管理 | 100% | 100% | 95% | 98% |
|
||||
| 内容管理 | 100% | 100% | 50%(只读列表) | 83% |
|
||||
| 积分商城 | 100% | 80%(管理端) | 100%(患者端) | 93% |
|
||||
| 告警系统 | 100% | 100% | 60%(仅查看/处理) | 87% |
|
||||
| AI 分析 | 100% | 70%(无 SSE 调用) | 30%(仅历史查看) | 67% |
|
||||
| 透析管理 | 100% | 100% | 0% | 67% |
|
||||
| 知情同意 | 100% | 100% | 0% | 67% |
|
||||
| 统计仪表盘 | 100% | 100% | 30%(医护端部分) | 77% |
|
||||
@@ -1,191 +0,0 @@
|
||||
# HMS 功能审计 — Phase 2: 后端完整性审计
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 后端 Rust 代码
|
||||
|
||||
## 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| Handler 函数 | 169 个(23 个文件) |
|
||||
| Service 函数 | 180 个(26 个文件) |
|
||||
| Entity | 45 个(46 个文件含 mod.rs) |
|
||||
| 路由 | 121 个(erp-health) |
|
||||
| 编译警告 | 40 个 |
|
||||
| 死代码抑制 | 4 处 |
|
||||
| TODO 注释 | 4 处 |
|
||||
| 生产代码 unwrap() | 10 处 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 死代码分析
|
||||
|
||||
### 1.1 `#[allow(dead_code)]` 抑制(4 处)
|
||||
|
||||
| 文件 | 行号 | 抑制内容 | 是否有调用者 | 判定 |
|
||||
|------|------|---------|------------|------|
|
||||
| `erp-auth/src/service/wechat_service.rs` | 43 | `unionid` 字段 | 微信 API 返回但不使用 | 保留合理(未来可能用于 UnionID 登录) |
|
||||
| `erp-server/src/middleware/rate_limit.rs` | 27 | `RateLimitConfig` 整个 struct | 当前使用环境变量配置 | 保留合理(预留结构化配置) |
|
||||
| `erp-plugin/src/host.rs` | 42 | `tenant_id` 字段 | HostState 中的 tenant_id | **建议清理**:已在 data_service 中改为函数参数传递 |
|
||||
| `erp-plugin/src/host.rs` | 44 | `user_id` 字段 | HostState 中的 user_id | **建议清理**:同上 |
|
||||
|
||||
### 1.2 编译器死代码警告(9 处未抑制)
|
||||
|
||||
| 文件 | 警告内容 | 严重性 |
|
||||
|------|---------|--------|
|
||||
| erp-health 多处 | `message`, `usage`, `input_tokens`, `output_tokens` 字段 | LOW — DTO 响应字段,前端可能使用 |
|
||||
| erp-health | `RefRow` struct 从未构造 | MEDIUM — 可能是重构残留 |
|
||||
| erp-health | `check_result`, `total` 字段 | LOW — 可能用于序列化 |
|
||||
| erp-server | `AnalyticsEvent.timestamp` 字段 | LOW — 预留字段 |
|
||||
|
||||
### 1.3 未使用导入(18 处)
|
||||
|
||||
分布在 6 个 crate 中,建议运行 `cargo fix` 自动清理:
|
||||
```bash
|
||||
cargo fix --lib -p erp-health --allow-dirty
|
||||
cargo fix --lib -p erp-plugin --allow-dirty
|
||||
cargo fix --lib -p erp-ai --allow-dirty
|
||||
```
|
||||
|
||||
### 1.4 TODO 注释(4 处)
|
||||
|
||||
| 文件 | 行号 | 内容 | 优先级 |
|
||||
|------|------|------|--------|
|
||||
| `erp-health/src/event.rs` | 50 | PATIENT_VERIFIED/PATIENT_DECEASED 未实现 | KNOWN |
|
||||
| `erp-auth/src/handler/wechat_handler.rs` | 45 | 多租户微信登录租户解析策略 | P2 |
|
||||
| `erp-auth/src/handler/wechat_handler.rs` | 76 | 同上 | P2 |
|
||||
| `erp-plugin/src/data_service.rs` | 1073 | 未来版本添加 Redis 缓存层 | P3 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 调用链完整性
|
||||
|
||||
### 2.1 Handler → Service 覆盖率
|
||||
|
||||
| Handler 文件 | Handler 函数数 | 对应 Service | 覆盖率 |
|
||||
|-------------|--------------|-------------|--------|
|
||||
| patient_handler | 17 | patient_service | 100% |
|
||||
| health_data_handler | 18 | health_data_service + trend_service | 100% |
|
||||
| points_handler | 28 | points_service + stats_service | 100% |
|
||||
| stats_handler | 9 | stats_service | 100% |
|
||||
| follow_up_handler | 10 | follow_up_service | 100% |
|
||||
| article_handler | 11 | article_service | 100% |
|
||||
| consultation_handler | 9 | consultation_service | 100% |
|
||||
| doctor_handler | 5 | doctor_service | 100% |
|
||||
| appointment_handler | 8 | appointment_service | 100% |
|
||||
| follow_up_template_handler | 5 | follow_up_template_service | 100% |
|
||||
| medication_record_handler | 5 | medication_record_service | 100% |
|
||||
| medication_reminder_handler | 4 | medication_reminder_service | 100% |
|
||||
| daily_monitoring_handler | 5 | daily_monitoring_service | 100% |
|
||||
| diagnosis_handler | 4 | diagnosis_service | 100% |
|
||||
| device_reading_handler | 3 | device_reading_service | 100% |
|
||||
| device_handler | 2 | device_service | 100% |
|
||||
| consent_handler | 3 | consent_service | 100% |
|
||||
| alert_handler | 4 | alert_service | 100% |
|
||||
| alert_rule_handler | 4 | alert_rule_service | 100% |
|
||||
| critical_alert_handler | 3 | critical_alert_service | 100% |
|
||||
| critical_value_threshold_handler | 4 | critical_value_threshold_service | 100% |
|
||||
| article_category_handler | 4 | article_category_service | 100% |
|
||||
| article_tag_handler | 4 | article_tag_service | 100% |
|
||||
|
||||
**结论:Handler → Service 覆盖率 100%。每个 handler 都有对应的 service 实现。**
|
||||
|
||||
### 2.2 Service → Entity 覆盖率
|
||||
|
||||
| Entity | 对应 Service | 状态 |
|
||||
|--------|-------------|------|
|
||||
| patient | patient_service | ✓ |
|
||||
| patient_family_member | patient_service | ✓ |
|
||||
| patient_tag | patient_service | ✓ |
|
||||
| patient_tag_relation | patient_service | ✓ |
|
||||
| patient_doctor_relation | patient_service | ✓ |
|
||||
| patient_devices | device_service | ✓ |
|
||||
| blind_index | patient_service(PII 加密) | ✓ |
|
||||
| consent | consent_service | ✓ |
|
||||
| doctor_profile | doctor_service | ✓ |
|
||||
| doctor_schedule | doctor_service | ✓ |
|
||||
| health_record | health_data_service | ✓ |
|
||||
| vital_signs | health_data_service | ✓ |
|
||||
| vital_signs_hourly | health_data_service | ✓ |
|
||||
| lab_report | health_data_service | ✓ |
|
||||
| health_trend | trend_service | ✓ |
|
||||
| diagnosis | diagnosis_service | ✓ |
|
||||
| medication_record | medication_record_service | ✓ |
|
||||
| medication_reminder | medication_reminder_service | ✓ |
|
||||
| device_readings | device_reading_service | ✓ |
|
||||
| appointment | appointment_service | ✓ |
|
||||
| follow_up_task | follow_up_service | ✓ |
|
||||
| follow_up_record | follow_up_service | ✓ |
|
||||
| follow_up_template | follow_up_template_service | ✓ |
|
||||
| follow_up_template_field | follow_up_template_service | ✓ |
|
||||
| consultation_session | consultation_service | ✓ |
|
||||
| consultation_message | consultation_service | ✓ |
|
||||
| article | article_service | ✓ |
|
||||
| article_category | article_category_service | ✓ |
|
||||
| article_tag | article_tag_service | ✓ |
|
||||
| article_article_tag | article_service | ✓ |
|
||||
| article_revision | article_service | ✓ |
|
||||
| alerts | alert_service | ✓ |
|
||||
| alert_rules | alert_rule_service | ✓ |
|
||||
| critical_alert | critical_alert_service | ✓ |
|
||||
| critical_alert_response | critical_alert_service | ✓ |
|
||||
| critical_value_threshold | critical_value_threshold_service | ✓ |
|
||||
| points_account | points_service | ✓ |
|
||||
| points_rule | points_service | ✓ |
|
||||
| points_product | points_service | ✓ |
|
||||
| points_order | points_service | ✓ |
|
||||
| points_transaction | points_service | ✓ |
|
||||
| points_checkin | points_service | ✓ |
|
||||
| offline_event | points_service | ✓ |
|
||||
| offline_event_registration | points_service | ✓ |
|
||||
| daily_monitoring | daily_monitoring_service | ✓ |
|
||||
|
||||
**结论:Entity → Service 覆盖率 100%。45 个实体全部有对应的 service 操作。**
|
||||
|
||||
---
|
||||
|
||||
## 3. unwrap() 调用审计
|
||||
|
||||
### 生产代码中的 unwrap()(10 处)
|
||||
|
||||
| 模式 | 次数 | 安全性 |
|
||||
|------|------|--------|
|
||||
| `active.version.unwrap() + 1` | 9 处 | **安全** — version 从 DB 查询获取,SeaORM ActiveModel 保证非 None |
|
||||
| `existing.unwrap().into()` | 1 处 | **需审查** — device_reading_service.rs:192 |
|
||||
|
||||
device_reading_service.rs:192 上下文:在 `update()` 函数中,`existing` 来自 `find_by_id` 查询。如果调用 `update()` 时记录不存在,会 panic。**建议**:改用 `ok_or(AppError::NotFound)` 模式。
|
||||
|
||||
### 测试代码中的 unwrap()(20+ 处)
|
||||
|
||||
全部在 `#[cfg(test)]` 块中,仅用于测试断言,无安全风险。
|
||||
|
||||
---
|
||||
|
||||
## 4. ErpModule Trait 覆盖率
|
||||
|
||||
| 模块 | on_startup | on_tenant_created | on_tenant_deleted | permissions | health_check | 等级 |
|
||||
|------|-----------|-------------------|-------------------|-------------|-------------|------|
|
||||
| erp-health | **5 个后台任务 + 事件监听** | **种子数据** | **软删除** | **56 个** | 默认 | **FULL** |
|
||||
| erp-ai | 默认 | 默认 | 默认 | 6 个 | 默认 | PARTIAL |
|
||||
| erp-dialysis | 默认 | 默认 | 默认 | 5 个 | 默认 | PARTIAL |
|
||||
| erp-auth | 默认 | 默认 | 默认 | 默认 | 默认 | MINIMAL |
|
||||
| erp-config | 默认 | 默认 | 默认 | 默认 | 默认 | MINIMAL |
|
||||
| erp-workflow | 默认 | 默认 | 默认 | 默认 | 默认 | MINIMAL |
|
||||
| erp-message | 默认 | 默认 | 默认 | 默认 | 默认 | MINIMAL |
|
||||
| erp-plugin | 默认 | 默认 | 默认 | 默认 | 默认 | MINIMAL |
|
||||
|
||||
**说明**:
|
||||
- MINIMAL 不一定代表缺陷 — auth/config/workflow/message 的权限通过中间件和 JWT claims 管控,不需要声明 PermissionDescriptor
|
||||
- 只有业务模块(health/ai/dialysis)需要声明细粒度权限码,因为它们的路由使用 `require_permission` 中间件
|
||||
|
||||
---
|
||||
|
||||
## 5. 后端完整性评分
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 所有实体/服务/处理器 完整 |
|
||||
| 调用链连通性 | 100% | 处理器→服务→实体 全部连通 |
|
||||
| 死代码率 | 2% | 4 处抑制 + 9 处警告 / 462 文件 |
|
||||
| unwrap() 风险 | 98% | 仅 1 处 device_reading_service 可能 panic |
|
||||
| Trait 实现完整度 | 37.5% | 3/8 模块有实质实现(其余使用默认值) |
|
||||
| TODO 债务 | LOW | 4 处,均为已知的 P2/P3 预留项 |
|
||||
@@ -1,214 +0,0 @@
|
||||
# HMS 功能审计 — Phase 3: 事件系统审计
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 全系统事件总线
|
||||
|
||||
## 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 事件类型常量定义 | 25 个(event.rs) |
|
||||
| 事件发布调用 | 44 处 |
|
||||
| 事件消费者 | 14 个(11 个 tokio::spawn 任务) |
|
||||
| 已知未实现事件 | 2 个(PATIENT_VERIFIED / PATIENT_DECEASED,标记 KNOWN) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 事件发布方清单
|
||||
|
||||
### erp-health 模块(30 处发布)
|
||||
|
||||
| 事件类型 | 发布者 Service | 函数 | Payload 字段 |
|
||||
|----------|---------------|------|-------------|
|
||||
| `patient.created` | patient_service.rs | create_patient | patient_id, name, phone |
|
||||
| `patient.updated` | patient_service.rs | update_patient | patient_id, updated_fields |
|
||||
| `appointment.created` | appointment_service.rs | create_appointment | appointment_id, patient_id, doctor_id, scheduled_at |
|
||||
| `appointment.{status}` | appointment_service.rs | update_appointment_status | appointment_id, patient_id, doctor_id, status |
|
||||
| `appointment.reminder` | appointment_service.rs | send_reminders | appointment_id, patient_id, doctor_id, scheduled_at |
|
||||
| `follow_up.created` | follow_up_service.rs | create_task | task_id, patient_id, assigned_to |
|
||||
| `follow_up.batch_created` | follow_up_service.rs | batch_create_tasks | count, task_ids |
|
||||
| `follow_up.completed` | follow_up_service.rs | batch_complete_tasks | task_ids, completed_count |
|
||||
| `follow_up.assigned` | follow_up_service.rs | batch_assign_tasks | task_ids, assigned_to |
|
||||
| `follow_up.overdue` | follow_up_service.rs | check_overdue_tasks | task_id, assigned_to, patient_id |
|
||||
| `consultation.opened` | consultation_service.rs | create_session | session_id, patient_id, doctor_id |
|
||||
| `consultation.closed` | consultation_service.rs | close_session | session_id, patient_id, doctor_id |
|
||||
| `consultation.new_message` | consultation_service.rs | create_message | session_id, sender_id, sender_type |
|
||||
| `device.readings.synced` | device_reading_service.rs | sync_readings | patient_id, device_type, reading_count |
|
||||
| `vital_signs.created` | health_data_service.rs | create_vital_signs | patient_id, record_id, indicators |
|
||||
| `lab_report.uploaded` | health_data_service.rs | create_lab_report | patient_id, report_id, indicator_count |
|
||||
| `lab_report.reviewed` | health_data_service.rs | review_lab_report | report_id, reviewer_id, status |
|
||||
| `health_data.critical_alert` | health_data_service.rs | create_vital_signs | patient_id, alert_type, metric_name, metric_value, threshold_value |
|
||||
| `daily_monitoring.created` | daily_monitoring_service.rs | create_daily_monitoring | patient_id, monitoring_id, record_date |
|
||||
| `alert.triggered` | alert_engine.rs | evaluate_rules | patient_id, severity, rule_name, alert_id |
|
||||
| `article.published` | article_service.rs | publish_article | article_id, title, author_id |
|
||||
| `article.rejected` | article_service.rs | reject_article | article_id, reviewer_id, reason |
|
||||
| `consent.granted` | consent_service.rs | grant_consent | patient_id, consent_type |
|
||||
| `consent.revoked` | consent_service.rs | revoke_consent | patient_id, consent_type, reason |
|
||||
| `points.earned` | points_service.rs | daily_checkin | patient_id, points, balance |
|
||||
| `points.exchanged` | points_service.rs | exchange_product | patient_id, product_id, order_id, points |
|
||||
| `points.expired` | points_service.rs | expire_points | count, expired_points_total |
|
||||
| `doctor.online_status_changed` | doctor_service.rs | update_online_status | doctor_id, old_status, new_status |
|
||||
|
||||
### erp-ai 模块(2 处发布)
|
||||
|
||||
| 事件类型 | 发布者 | 函数 |
|
||||
|----------|--------|------|
|
||||
| `ai.analysis.failed` | handler/mod.rs | stream_lab_report 等 |
|
||||
| `ai.analysis.completed` | handler/mod.rs | stream_lab_report 等 |
|
||||
|
||||
### erp-dialysis 模块(1 处发布)
|
||||
|
||||
| 事件类型 | 发布者 | 函数 |
|
||||
|----------|--------|------|
|
||||
| `dialysis.record.created` | dialysis_service.rs | create_record |
|
||||
|
||||
### erp-auth 模块(1 处发布)
|
||||
|
||||
| 事件类型 | 发布者 | 函数 |
|
||||
|----------|--------|------|
|
||||
| `user.login` | auth_service.rs | login |
|
||||
|
||||
### erp-workflow 模块(2+ 处发布)
|
||||
|
||||
| 事件类型 | 发布者 | 函数 |
|
||||
|----------|--------|------|
|
||||
| `workflow.instance.*` | instance_service.rs | start_instance 等 |
|
||||
| `workflow.task.timeout` | module.rs | 后台超时检测 |
|
||||
|
||||
### erp-plugin 模块(3 处发布)
|
||||
|
||||
| 事件类型 | 发布者 | 函数 |
|
||||
|----------|--------|------|
|
||||
| `plugin.data.*` | data_service.rs | CRUD 操作 |
|
||||
| `plugin.trigger.*` | data_service.rs | 通知触发 |
|
||||
| 动态事件 | engine.rs | 插件自定义事件 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 事件消费方清单
|
||||
|
||||
### erp-health 消费者(11 个 tokio::spawn 任务)
|
||||
|
||||
| # | 消费者名称 | 订阅前缀 | 处理事件 | 业务动作 |
|
||||
|---|-----------|---------|---------|---------|
|
||||
| 1 | workflow_task_consumer | `workflow.task.` | `workflow.task.completed` | 更新随访任务状态为 completed |
|
||||
| 2 | message_consumer | `message.` | `message.sent` | 日志记录(预留扩展) |
|
||||
| 3 | device_reading_consumer | `device.readings.` | `device.readings.synced` | 触发告警引擎评估 |
|
||||
| 4 | alert_notifier | `alert.` | `alert.triggered` | 发送应用内告警通知 |
|
||||
| 5 | patient_welcome | `patient.` | `patient.created` | 发送欢迎消息 |
|
||||
| 6 | appointment_notifier | `appointment.` | `appointment.confirmed` | 发送预约确认通知 |
|
||||
| 7 | appointment_cancel_handler | `appointment.` | `appointment.cancelled` | 日志记录(号源释放) |
|
||||
| 8 | follow_up_escalator | `follow_up.` | `follow_up.overdue` | 发送逾期升级通知 |
|
||||
| 9 | critical_alert_consumer | `health_data.` | `health_data.critical_alert` | 创建危急值告警记录 |
|
||||
| 10 | ai_analysis_notifier | `ai.` | `ai.analysis.completed` | 通知关联医生 |
|
||||
| 11 | dialysis_notifier | `ai.` | `dialysis.record.created` | 日志记录 |
|
||||
| 12 | consent_notifier | `consent.` | `consent.granted` | 发送授予通知 |
|
||||
| 13 | consent_revoked_notifier | `consent.` | `consent.revoked` | 发送撤回通知给医护 |
|
||||
|
||||
### 其他模块消费者
|
||||
|
||||
| 模块 | 消费者 | 订阅范围 |
|
||||
|------|--------|---------|
|
||||
| erp-message | SSE handler | 全部事件(转发给前端) |
|
||||
| erp-plugin | notification handler | `plugin.trigger.` |
|
||||
| erp-plugin | engine | 按插件 manifest 的 pattern |
|
||||
|
||||
---
|
||||
|
||||
## 3. 事件常量 vs 发布方 vs 消费方矩阵
|
||||
|
||||
| 事件常量 | 事件类型 | 发布方 | 消费方 | 状态 |
|
||||
|----------|---------|--------|--------|------|
|
||||
| APPOINTMENT_CREATED | `appointment.created` | ✓ appointment_service | ✓ appointment_notifier(前缀匹配) | **ALIVE** |
|
||||
| ALERT_TRIGGERED | `alert.triggered` | ✓ alert_engine | ✓ alert_notifier | **ALIVE** |
|
||||
| CONSENT_GRANTED | `consent.granted` | ✓ consent_service | ✓ consent_notifier | **ALIVE** |
|
||||
| CONSENT_REVOKED | `consent.revoked` | ✓ consent_service | ✓ consent_revoked_notifier | **ALIVE** |
|
||||
| ARTICLE_PUBLISHED | `article.published` | ✓ article_service | — | **NO CONSUMER** |
|
||||
| ARTICLE_REJECTED | `article.rejected` | ✓ article_service | — | **NO CONSUMER** |
|
||||
| CONSULTATION_OPENED | `consultation.opened` | ✓ consultation_service | — | **NO CONSUMER** |
|
||||
| CONSULTATION_CLOSED | `consultation.closed` | ✓ consultation_service | — | **NO CONSUMER** |
|
||||
| CONSULTATION_NEW_MESSAGE | `consultation.new_message` | ✓ consultation_service | — | **NO CONSUMER** |
|
||||
| DEVICE_READINGS_SYNCED | `device.readings.synced` | ✓ device_reading_service | ✓ device_reading_consumer | **ALIVE** |
|
||||
| DOCTOR_ONLINE_STATUS_CHANGED | `doctor.online_status_changed` | ✓ doctor_service | — | **NO CONSUMER** |
|
||||
| FOLLOW_UP_CREATED | `follow_up.created` | ✓ follow_up_service | — | **NO CONSUMER** |
|
||||
| FOLLOW_UP_COMPLETED | `follow_up.completed` | ✓ follow_up_service | — | **NO CONSUMER** |
|
||||
| FOLLOW_UP_OVERDUE | `follow_up.overdue` | ✓ follow_up_service | ✓ follow_up_escalator | **ALIVE** |
|
||||
| DAILY_MONITORING_CREATED | `daily_monitoring.created` | ✓ daily_monitoring_service | — | **NO CONSUMER** |
|
||||
| LAB_REPORT_UPLOADED | `lab_report.uploaded` | ✓ health_data_service | — | **NO CONSUMER** |
|
||||
| LAB_REPORT_REVIEWED | `lab_report.reviewed` | ✓ health_data_service | — | **NO CONSUMER** |
|
||||
| HEALTH_DATA_CRITICAL_ALERT | `health_data.critical_alert` | ✓ health_data_service | ✓ critical_alert_consumer | **ALIVE** |
|
||||
| PATIENT_CREATED | `patient.created` | ✓ patient_service | ✓ patient_welcome | **ALIVE** |
|
||||
| PATIENT_UPDATED | `patient.updated` | ✓ patient_service | — | **NO CONSUMER** |
|
||||
| PATIENT_VERIFIED | `patient.verified` | — | — | **KNOWN** 未实现 |
|
||||
| PATIENT_DECEASED | `patient.deceased` | — | — | **KNOWN** 未实现 |
|
||||
| POINTS_EXPIRED | `points.expired` | ✓ points_service | — | **NO CONSUMER** |
|
||||
| POINTS_EARNED | `points.earned` | ✓ points_service | — | **NO CONSUMER** |
|
||||
| POINTS_EXCHANGED | `points.exchanged` | ✓ points_service | — | **NO CONSUMER** |
|
||||
|
||||
---
|
||||
|
||||
## 4. 分析结论
|
||||
|
||||
### 4.1 活跃事件(有发布+有消费):11 个
|
||||
这是系统核心业务链路,全部正常:
|
||||
- `workflow.task.completed` → 随访自动完成
|
||||
- `device.readings.synced` → 告警评估
|
||||
- `alert.triggered` → 告警通知
|
||||
- `patient.created` → 欢迎消息
|
||||
- `appointment.confirmed/cancelled` → 预约通知
|
||||
- `follow_up.overdue` → 逾期升级
|
||||
- `health_data.critical_alert` → 危急值告警
|
||||
- `ai.analysis.completed` → 医生通知
|
||||
- `dialysis.record.created` → 日志
|
||||
- `consent.granted/revoked` → 知情同意通知
|
||||
|
||||
### 4.2 有发布无消费事件:14 个
|
||||
这些事件被发布到 EventBus 并持久化到 domain_events 表,但没有业务消费者。它们可能仅用于审计追踪或 SSE 前端推送。
|
||||
|
||||
**风险评估**:
|
||||
| 事件 | 风险 | 说明 |
|
||||
|------|------|------|
|
||||
| `patient.updated` | LOW | 信息性事件,更新操作已直接处理 |
|
||||
| `vital_signs.created` | LOW | 数据已写入 DB,无需后续触发 |
|
||||
| `lab_report.uploaded` | LOW | 同上 |
|
||||
| `lab_report.reviewed` | LOW | 同上 |
|
||||
| `follow_up.created` | LOW | 任务已创建,无需后续触发 |
|
||||
| `follow_up.completed` | LOW | 状态已更新,无需后续触发 |
|
||||
| `consultation.*` | LOW | 会话管理已在 service 内直接处理 |
|
||||
| `article.published/rejected` | LOW | 状态已更新,无需后续触发 |
|
||||
| `points.earned/exchanged/expired` | LOW | 积分操作已在 service 内完成 |
|
||||
| `daily_monitoring.created` | LOW | 数据已写入 |
|
||||
| `doctor.online_status_changed` | LOW | 状态已更新 |
|
||||
|
||||
### 4.3 已知未实现事件(KNOWN):2 个
|
||||
- `PATIENT_VERIFIED` — 患者认证流程未实现
|
||||
- `PATIENT_DECEASED` — 死亡记录流程未实现
|
||||
|
||||
### 4.4 潜在问题
|
||||
1. **message.sent 消费者仅为日志记录** — event.rs:119 的消费者仅做 tracing::info,实际业务逻辑(如更新 consultation last_message_at)已在 service 层直接处理,此消费者预留扩展但当前无实质作用。
|
||||
2. **SSE 全事件转发** — erp-message 的 SSE handler 监听所有事件,意味着所有事件(包括 points.earned 等低优先级事件)都会被推送到前端,可能导致通知噪音。
|
||||
|
||||
---
|
||||
|
||||
## 5. Payload Schema 一致性验证(抽样 5 个)
|
||||
|
||||
| 事件 | 发布方字段 | 消费方解析 | 一致性 |
|
||||
|------|-----------|-----------|--------|
|
||||
| `workflow.task.completed` | `task_id` | `task_id` → Uuid | ✅ |
|
||||
| `device.readings.synced` | `patient_id` | `patient_id` → Uuid | ✅ |
|
||||
| `health_data.critical_alert` | `patient_id, alert_type, metric_name, metric_value, threshold_value` | 全部解析 | ✅ |
|
||||
| `follow_up.overdue` | `task_id, assigned_to` | 全部解析 | ✅ |
|
||||
| `appointment.confirmed` | `doctor_id, patient_id` | 全部解析 | ✅ |
|
||||
|
||||
**结论**:所有抽样的关键事件 payload schema 发布方与消费方完全一致。所有消费者都使用幂等检查(`is_event_processed`),防止重复处理。
|
||||
|
||||
---
|
||||
|
||||
## 6. 事件系统评分
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 事件定义完整性 | 95% | 25/27 常量有发布者(2 个 KNOWN 未实现) |
|
||||
| 消费者覆盖率 | 44% | 11/25 事件有活跃消费者 |
|
||||
| Payload 一致性 | 100% | 抽样 5 个全部一致 |
|
||||
| 幂等性保证 | 100% | 所有消费者使用 `is_event_processed` 检查 |
|
||||
| 死信处理 | 100% | 消费失败自动进入 dead_letter_event 表 |
|
||||
@@ -1,334 +0,0 @@
|
||||
# HMS 功能审计 — Phase 4: 参数传递与配置审计
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: DTO 覆盖率、配置参数、权限码、数据模型映射
|
||||
|
||||
## 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| DTO 结构体 | 105 个(17 文件) |
|
||||
| 配置参数 | 30 个字段(12 配置节) |
|
||||
| 未使用配置字段 | 6 个(均在 AiConfig) |
|
||||
| 声明权限码 | 50 个(3 模块) |
|
||||
| Handler 实际使用权限码 | ~106 个 |
|
||||
| 前端 AuthButton 引用 | 13 个唯一码 |
|
||||
| 小程序未映射字段 | 6 个(vital_signs) |
|
||||
|
||||
---
|
||||
|
||||
## 1. DTO 覆盖率检查
|
||||
|
||||
### 1.1 DTO 分布统计
|
||||
|
||||
| 分类 | 数量 | 说明 |
|
||||
|------|------|------|
|
||||
| Create(Create*Req) | 27 | 创建请求 |
|
||||
| Update(Update*Req) | 19 | 更新/审核/撤销/核销请求 |
|
||||
| Query(*Query/*Params) | 8 | 列表查询参数 |
|
||||
| Response(*Resp/*Response) | 38 | 响应结构体 |
|
||||
| Other(批量操作/辅助) | 13 | BatchReq、ExchangeReq、数据点等 |
|
||||
| **总计** | **105** | 17 个文件 |
|
||||
|
||||
### 1.2 按 Handler 的 DTO 覆盖矩阵
|
||||
|
||||
| Handler | Create DTO | Update DTO | Query DTO | Response DTO | 状态 |
|
||||
|---------|-----------|-----------|----------|-------------|------|
|
||||
| patient_handler | CreatePatientReq | UpdatePatientReq | PatientListQuery | PatientResp | ✓ |
|
||||
| doctor_handler | CreateDoctorReq | UpdateDoctorReq | DoctorListQuery | DoctorResp | ✓ |
|
||||
| appointment_handler | CreateAppointmentReq | UpdateAppointmentStatusReq | AppointmentListQuery/CalendarQuery | AppointmentResp/ScheduleResp | ✓ |
|
||||
| consultation_handler | CreateSessionReq/CreateMessageReq | — | SessionQuery | SessionResp/MessageResp | ✓ |
|
||||
| follow_up_handler | CreateFollowUpTaskReq/BatchCreateTasksReq | UpdateFollowUpTaskReq | FollowUpTaskListQuery/FollowUpRecordListQuery | FollowUpTaskResp/FollowUpRecordResp | ✓ |
|
||||
| follow_up_template_handler | CreateFollowUpTemplateReq | UpdateFollowUpTemplateReq | FollowUpTemplateListQuery | FollowUpTemplateResp | ✓ |
|
||||
| health_data_handler | CreateVitalSignsReq/CreateLabReportReq/CreateHealthRecordReq | UpdateVitalSignsReq/UpdateLabReportReq/UpdateHealthRecordReq/ReviewLabReportReq | MiniTrendQueryParams | VitalSignsResp/LabReportResp/HealthRecordResp/TrendResp | ✓ |
|
||||
| daily_monitoring_handler | CreateDailyMonitoringReq | UpdateDailyMonitoringReq | — | DailyMonitoringResp | ✓ |
|
||||
| diagnosis_handler | CreateDiagnosisReq | UpdateDiagnosisReq | — | DiagnosisResp | ✓ |
|
||||
| medication_record_handler | CreateMedicationRecordReq | UpdateMedicationRecordReq | — | MedicationRecordResp | ✓ |
|
||||
| medication_reminder_handler | CreateMedicationReminderReq | UpdateMedicationReminderReq | — | MedicationReminderResp | ✓ |
|
||||
| alert_handler | CreateAlertRuleRequest | UpdateAlertRuleRequest/AcknowledgeAlertRequest | — | AlertRuleResponse/AlertResponse | ✓ |
|
||||
| consent_handler | CreateConsentReq | RevokeConsentReq | — | ConsentResp | ✓ |
|
||||
| points_handler | CreatePointsRuleReq/CreatePointsProductReq/CreateOfflineEventReq | UpdatePointsRuleReq/UpdatePointsProductReq/UpdateOfflineEventWithVersion/VerifyOrderReq | — | PointsRuleResp/PointsProductResp/PointsOrderResp/PointsAccountResp | ✓ |
|
||||
| article_handler | CreateArticleReq/CreateCategoryReq/CreateTagReq | UpdateArticleReq/UpdateCategoryReq/UpdateTagReq/ReviewArticleReq | ArticleListParams | ArticleResp/CategoryResp/TagResp/ArticleRevisionResp | ✓ |
|
||||
| stats_handler | — | — | — | DashboardStatsResp/PatientStatisticsResp/...(9 个) | ✓ |
|
||||
|
||||
**结论:所有 23 个 handler 都有完整的 DTO 覆盖。每个写入端点有 Create/Update DTO,列表端点有 Query DTO,所有端点有 Response DTO。**
|
||||
|
||||
### 1.3 DTO 传递链完整性(抽样验证 5 个)
|
||||
|
||||
| 端点 | Handler 输入 DTO | Service 函数签名 | 一致性 |
|
||||
|------|-----------------|-----------------|--------|
|
||||
| `POST /patients` | CreatePatientReq | `create_patient(req: CreatePatientReq)` | ✅ |
|
||||
| `POST /appointments` | CreateAppointmentReq | `create_appointment(req: CreateAppointmentReq)` | ✅ |
|
||||
| `POST /vital-signs` | CreateVitalSignsReq | `create_vital_signs(patient_id, req: CreateVitalSignsReq)` | ✅ |
|
||||
| `POST /follow-up/tasks` | CreateFollowUpTaskReq | `create_task(req: CreateFollowUpTaskReq)` | ✅ |
|
||||
| `POST /consultation-sessions` | CreateSessionReq | `create_session(req: CreateSessionReq)` | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 2. 配置参数使用率
|
||||
|
||||
### 2.1 配置结构总览
|
||||
|
||||
AppConfig 包含 12 个子配置节、30 个字段:
|
||||
|
||||
| 配置节 | 字段数 | 用途 |
|
||||
|--------|-------|------|
|
||||
| ServerConfig | 3 | 服务端口、指标端口 |
|
||||
| DatabaseConfig | 3 | 连接字符串、连接池大小 |
|
||||
| RedisConfig | 1 | 连接字符串 |
|
||||
| JwtConfig | 3 | 密钥、Token TTL |
|
||||
| AuthConfig | 1 | 超级管理员密码 |
|
||||
| LogConfig | 1 | 日志级别 |
|
||||
| CorsConfig | 1 | 允许的 Origin |
|
||||
| WechatConfig | 3 | 微信小程序 AppID/Secret/开发模式 |
|
||||
| HealthConfig | 2 | AES/HMAC 密钥(PII 加密) |
|
||||
| CryptoConfig | 1 | KEK 主密钥 |
|
||||
| AiConfig | 8 | AI 提供商配置 |
|
||||
| StorageConfig | 2 | 文件上传目录/大小限制 |
|
||||
|
||||
### 2.2 字段使用情况
|
||||
|
||||
| 配置节 | 字段 | 使用次数 | 状态 |
|
||||
|--------|------|---------|------|
|
||||
| ServerConfig | `host` | 1 | ✓ 正常 |
|
||||
| ServerConfig | `port` | 1 | ✓ 正常 |
|
||||
| ServerConfig | `metrics_port` | 2 | ✓ 正常 |
|
||||
| DatabaseConfig | `url` | 5 | ✓ 正常 |
|
||||
| DatabaseConfig | `max_connections` | 1 | ✓ 正常 |
|
||||
| DatabaseConfig | `min_connections` | 1 | ✓ 正常 |
|
||||
| RedisConfig | `url` | 2 | ✓ 正常 |
|
||||
| JwtConfig | `secret` | 5 | ✓ 正常 |
|
||||
| JwtConfig | `access_token_ttl` | 1 | ✓ 正常 |
|
||||
| JwtConfig | `refresh_token_ttl` | 1 | ✓ 正常 |
|
||||
| AuthConfig | `super_admin_password` | 1 | ✓ 正常 |
|
||||
| LogConfig | `level` | 1 | ✓ 正常 |
|
||||
| CorsConfig | `allowed_origins` | 1 | ✓ 正常 |
|
||||
| WechatConfig | `appid` | 2 | ✓ 正常 |
|
||||
| WechatConfig | `secret` | 3 | ✓ 正常 |
|
||||
| WechatConfig | `dev_mode` | 2 | ✓ 正常 |
|
||||
| HealthConfig | `aes_key` | 2 | ✓ 正常(启动校验 + CryptoService 初始化) |
|
||||
| HealthConfig | `hmac_key` | 2 | ✓ 正常(启动校验 + HMAC 盲索引) |
|
||||
| CryptoConfig | `kek` | 2 | ✓ 正常(KEK/DEK 密钥体系) |
|
||||
| AiConfig | `api_key` | 1 | ✓ 正常 |
|
||||
| AiConfig | `base_url` | 1 | ✓ 正常 |
|
||||
| **AiConfig** | **`default_provider`** | **0** | **未使用** |
|
||||
| **AiConfig** | **`model`** | **0** | **未使用** |
|
||||
| **AiConfig** | **`max_tokens`** | **0** | **未使用** |
|
||||
| **AiConfig** | **`temperature`** | **0** | **未使用** |
|
||||
| **AiConfig** | **`cache_ttl_seconds`** | **0** | **未使用** |
|
||||
| **AiConfig** | **`rate_limit_patient_daily`** | **0** | **未使用** |
|
||||
| StorageConfig | `upload_dir` | 3 | ✓ 正常 |
|
||||
| StorageConfig | `max_file_size` | 1 | ✓ 正常 |
|
||||
|
||||
### 2.3 未使用字段分析
|
||||
|
||||
6 个未使用字段全部集中在 `AiConfig`:
|
||||
|
||||
| 字段 | 原设计意图 | 当前替代机制 |
|
||||
|------|-----------|------------|
|
||||
| `default_provider` | 全局默认 AI 提供商 | 硬编码使用 Claude |
|
||||
| `model` | 全局默认模型 | 每个 Prompt 模板通过 `model_config` JSONB 字段配置 |
|
||||
| `max_tokens` | 全局默认最大 Token 数 | 同上,从 `model_config.max_tokens` 读取,默认 2048 |
|
||||
| `temperature` | 全局默认温度 | 同上,从 `model_config.temperature` 读取,默认 0.3 |
|
||||
| `cache_ttl_seconds` | AI 结果缓存时间 | 未实现缓存层(每次为独立 SSE 流) |
|
||||
| `rate_limit_patient_daily` | 每患者每日 AI 调用限制 | 未实现限流 |
|
||||
|
||||
**影响评估**:LOW。这些字段是预留的全局默认值,实际业务已通过更灵活的 per-prompt `model_config` 实现相同能力。但 `cache_ttl_seconds` 和 `rate_limit_patient_daily` 代表缺失的功能(缓存和限流),属于 P3 待实现。
|
||||
|
||||
---
|
||||
|
||||
## 3. 权限码审计
|
||||
|
||||
### 3.1 权限码声明总览
|
||||
|
||||
| 模块 | 声明数 | 权限码前缀 |
|
||||
|------|--------|-----------|
|
||||
| erp-health | 39 | `health.*` |
|
||||
| erp-ai | 6 | `ai.*` |
|
||||
| erp-dialysis | 5 | `health.dialysis*` |
|
||||
| **合计** | **50** | |
|
||||
|
||||
其余 5 个模块(auth/config/workflow/message/plugin)**未声明任何 PermissionDescriptor**,但 handler 中使用了约 56 个权限码。
|
||||
|
||||
### 3.2 声明 vs 使用覆盖矩阵
|
||||
|
||||
#### erp-health(39 声明 → 全部使用)
|
||||
|
||||
| 权限码 | Handler 使用 | 前端 AuthButton |
|
||||
|--------|-------------|----------------|
|
||||
| health.patient.list | ✓ | — |
|
||||
| health.patient.manage | ✓ | ✓ PatientList/PatientDetail/PatientTagManage |
|
||||
| health.health-data.list | ✓ | — |
|
||||
| health.health-data.manage | ✓ | ✓ VitalSignsTab/LabReportsTab/HealthRecordsTab/DailyMonitoringTab |
|
||||
| health.appointment.list | ✓ | — |
|
||||
| health.appointment.manage | ✓ | ✓ AppointmentList |
|
||||
| health.follow-up.list | ✓ | — |
|
||||
| health.follow-up.manage | ✓ | ✓ FollowUpTaskList |
|
||||
| health.consultation.list | ✓ | — |
|
||||
| health.consultation.manage | ✓ | ✓ ConsultationList/ConsultationDetail |
|
||||
| health.doctor.list | ✓ | — |
|
||||
| health.doctor.manage | ✓ | ✓ DoctorList/DoctorSchedule |
|
||||
| health.articles.list | ✓ | — |
|
||||
| health.articles.manage | ✓ | ✓ ArticleManageList/ArticleEditor/ArticleCategoryManage/ArticleTagManage |
|
||||
| health.articles.review | ✓ | ✓ ArticleManageList |
|
||||
| health.points.list | ✓ | — |
|
||||
| health.points.manage | ✓ | ✓ PointsRuleList/PointsProductList/PointsOrderList/OfflineEventList |
|
||||
| health.device-readings.list | ✓ | — |
|
||||
| health.device-readings.manage | ✓ | — |
|
||||
| health.devices.list | ✓ | — |
|
||||
| health.devices.manage | ✓ | — |
|
||||
| health.alerts.list | ✓ | — |
|
||||
| health.alerts.manage | ✓ | ⚠️ 前端拼写错误(见 §3.4) |
|
||||
| health.alert-rules.list | ✓ | — |
|
||||
| health.alert-rules.manage | ✓ | — |
|
||||
| health.critical-alerts.list | ✓ | — |
|
||||
| health.critical-alerts.manage | ✓ | — |
|
||||
| health.critical-value-thresholds.list | ✓ | — |
|
||||
| health.critical-value-thresholds.manage | ✓ | — |
|
||||
| health.follow-up-templates.list | ✓ | — |
|
||||
| health.follow-up-templates.manage | ✓ | — |
|
||||
| health.daily-monitoring.list | ✓ | — |
|
||||
| health.daily-monitoring.manage | ✓ | — |
|
||||
| health.consent.list | ✓ | — |
|
||||
| health.consent.manage | ✓ | — |
|
||||
| health.medication-records.list | ✓ | — |
|
||||
| health.medication-records.manage | ✓ | — |
|
||||
| health.medication-reminders.list | ✓ | — |
|
||||
| health.medication-reminders.manage | ✓ | — |
|
||||
|
||||
#### erp-dialysis(5 声明 → 全部使用)
|
||||
|
||||
| 权限码 | Handler 使用 | 前端 AuthButton |
|
||||
|--------|-------------|----------------|
|
||||
| health.dialysis.list | ✓ | — |
|
||||
| health.dialysis.manage | ✓ | ✓ DialysisManageList |
|
||||
| health.dialysis-prescription.list | ✓ | — |
|
||||
| health.dialysis-prescription.manage | ✓ | — |
|
||||
| health.dialysis.stats | ✓ | — |
|
||||
|
||||
#### erp-ai(6 声明 → 5 使用)
|
||||
|
||||
| 权限码 | Handler 使用 | 前端 AuthButton |
|
||||
|--------|-------------|----------------|
|
||||
| ai.analysis.list | ✓ | — |
|
||||
| ai.analysis.manage | ✓ | — |
|
||||
| ai.prompt.list | ✓ | ✓ AiPromptList |
|
||||
| ai.prompt.manage | ✓ | ✓ AiPromptList |
|
||||
| ai.usage.list | ✓ | — |
|
||||
| **ai.provider.manage** | **—** | **已声明但无 Handler 调用** |
|
||||
|
||||
### 3.3 未声明权限码(基础模块)
|
||||
|
||||
以下 5 个模块的 handler 使用了 `require_permission` 但未实现 `permissions()` 方法,导致权限码不会通过 `sync_module_permissions` 自动注册到数据库:
|
||||
|
||||
| 模块 | 未声明权限数 | 权限码示例 |
|
||||
|------|------------|-----------|
|
||||
| erp-auth | 23 | user.list, role.create, organization.update, department.delete, position.list |
|
||||
| erp-config | 18 | dictionary.list, menu.update, setting.read, numbering.generate, theme.update |
|
||||
| erp-workflow | 8 | workflow.list, workflow.start, workflow.approve, workflow.delegate |
|
||||
| erp-message | 5 | message.list, message.send, message.template.create |
|
||||
| erp-plugin | 2 | plugin.admin, plugin.list |
|
||||
| **合计** | **56** | |
|
||||
|
||||
**影响评估**:这些权限码通过种子数据(super_admin 角色)手动注册到数据库,而非通过 `ErpModule::permissions()` 自动注册。功能上可以正常工作,但:
|
||||
- 新增权限需要手动 SQL 插入,容易遗漏
|
||||
- 与 health/ai/dialysis 模块的自动注册机制不一致
|
||||
- 建议在后续迭代中为这 5 个模块补充 `permissions()` 实现
|
||||
|
||||
### 3.4 前端权限码拼写错误(BUG)
|
||||
|
||||
| 文件 | 前端代码 | 后端声明 | 差异 |
|
||||
|------|---------|---------|------|
|
||||
| [AlertList.tsx:240](apps/web/src/pages/health/AlertList.tsx#L240) | `health.alert.manage` | `health.alerts.manage` | 缺少 `s` |
|
||||
|
||||
**影响**:AlertList 页面的"管理"按钮(确认/处置告警)永远不显示,因为 `health.alert.manage` 权限码不存在于系统。用户虽然可以通过 API 直接操作,但 UI 层面无法触发管理动作。
|
||||
|
||||
**修复**:将 `health.alert.manage` 改为 `health.alerts.manage`(复数形式)。
|
||||
|
||||
### 3.5 前端路由级权限控制
|
||||
|
||||
Web 前端通过 `PrivateRoute` 组件做路由守卫,当前仅检查:
|
||||
1. 是否已认证(`isAuthenticated`)
|
||||
2. `/users`、`/roles`、`/organizations` 路径需要 `auth.*` 前缀权限
|
||||
|
||||
健康模块的路由**没有前端路由级权限守卫**,依赖后端 API 返回 403 拒绝未授权请求。这在 SPA 架构中是可接受的做法,但用户可能看到空白页面而非友好的"无权限"提示。
|
||||
|
||||
---
|
||||
|
||||
## 4. 小程序数据模型映射验证
|
||||
|
||||
### 4.1 后端 vital_signs Entity 字段(24 个,含标准字段)
|
||||
|
||||
| # | 字段名 | 类型 | 小程序映射 |
|
||||
|---|--------|------|-----------|
|
||||
| 1 | id | Uuid | — |
|
||||
| 2 | tenant_id | Uuid | — |
|
||||
| 3 | patient_id | Uuid | — |
|
||||
| 4 | record_date | NaiveDate | ✓ |
|
||||
| 5 | systolic_bp_morning | Option\<i32\> | ✓ blood_pressure → extra.systolic |
|
||||
| 6 | diastolic_bp_morning | Option\<i32\> | ✓ blood_pressure → extra.diastolic |
|
||||
| 7 | **systolic_bp_evening** | Option\<i32\> | **未映射** |
|
||||
| 8 | **diastolic_bp_evening** | Option\<i32\> | **未映射** |
|
||||
| 9 | heart_rate | Option\<i32\> | ✓ heart_rate |
|
||||
| 10 | weight | Option\<Decimal\> | ✓ weight |
|
||||
| 11 | blood_sugar | Option\<Decimal\> | ✓ blood_sugar |
|
||||
| 12 | **body_temperature** | Option\<Decimal\> | **未映射** |
|
||||
| 13 | **spo2** | Option\<i32\> | **未映射** |
|
||||
| 14 | **blood_sugar_type** | Option\<String\> | **未映射** |
|
||||
| 15 | water_intake_ml | Option\<i32\> | ✓ water_intake |
|
||||
| 16 | urine_output_ml | Option\<i32\> | ✓ urine_output |
|
||||
| 17 | notes | Option\<String\> | ✓ |
|
||||
| 18 | source | String | —(后端默认 "manual") |
|
||||
| 19-24 | 标准字段 | — | — |
|
||||
|
||||
### 4.2 小程序 indicator_type 映射表
|
||||
|
||||
来源:[health.ts:30-59](apps/miniprogram/src/services/health.ts#L30-L59)
|
||||
|
||||
| indicator_type | 映射字段 | 转换逻辑 |
|
||||
|---------------|---------|---------|
|
||||
| `blood_pressure` | systolic_bp_morning + diastolic_bp_morning | extra.systolic / extra.diastolic |
|
||||
| `heart_rate` | heart_rate | Math.round(value) |
|
||||
| `weight` | weight | value |
|
||||
| `blood_sugar` | blood_sugar | value |
|
||||
| `water_intake` | water_intake_ml | Math.round(value) |
|
||||
| `urine_output` | urine_output_ml | Math.round(value) |
|
||||
| *(default)* | — | 丢弃(不发送) |
|
||||
|
||||
### 4.3 差异清单
|
||||
|
||||
#### A. 后端有但小程序未映射(6 个业务字段)
|
||||
|
||||
| 字段 | 严重性 | 说明 |
|
||||
|------|--------|------|
|
||||
| **systolic_bp_evening** | **HIGH** | 后端趋势服务和危急值检测均支持晚间血压,但小程序所有血压数据一律写入 `*_morning` 字段,晚间数据丢失 |
|
||||
| **diastolic_bp_evening** | **HIGH** | 同上 |
|
||||
| **body_temperature** | MEDIUM | 后端 entity 和 DTO 均支持体温录入,但小程序无 `body_temperature` indicator_type |
|
||||
| **spo2** | MEDIUM | 后端支持血氧饱和度,小程序无对应类型 |
|
||||
| **blood_sugar_type** | LOW | 后端支持区分空腹/餐后/随机/OGTT 血糖类型,小程序仅传数值不传类型 |
|
||||
| **source** | LOW | 后端默认 "manual",小程序未区分来源标识 |
|
||||
|
||||
#### B. 小程序映射了但后端不存在的字段
|
||||
|
||||
**无此类差异。** 所有小程序映射的目标字段在后端 CreateVitalSignsReq 中均存在。
|
||||
|
||||
### 4.4 根因分析
|
||||
|
||||
小程序 `inputVitalSign()` 的 `indicator_type` 模型设计为"每种体征一个类型",但血压实际上需要区分时段(晨间/晚间)。当前 `blood_pressure` 类型固定映射到 `*_morning` 字段,没有逻辑可以写入 `*_evening` 字段。
|
||||
|
||||
后端设计是完善的——趋势服务([trend_service.rs](crates/erp-health/src/service/trend_service.rs))支持 `systolic_bp_evening`/`diastolic_bp_evening` 指标查询,危急值检测([health_data_service.rs](crates/erp-health/src/service/health_data_service.rs))使用 `morning.or(evening)` 做回退检查。
|
||||
|
||||
**修复建议**:新增 `blood_pressure_evening` indicator_type,或修改 UI 让用户选择时段。
|
||||
|
||||
---
|
||||
|
||||
## 5. 评分
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| DTO 覆盖率 | 100% | 105 个 DTO 完整覆盖所有 handler,传递链一致 |
|
||||
| 配置参数使用率 | 80% | 24/30 字段活跃使用,6 个 AiConfig 字段预留未接入 |
|
||||
| 权限码声明覆盖率 | 47% | 仅 50/106 个使用的权限码通过 PermissionDescriptor 声明 |
|
||||
| 权限码一致性 | 98% | 1 处前端拼写错误(alert vs alerts) |
|
||||
| 前端权限按钮覆盖 | 26% | 13/50 声明码有 AuthButton,其余依赖 API 403 |
|
||||
| 数据模型映射完整性 | 63% | 6/16 业务字段未映射(2 个 HIGH + 2 个 MEDIUM) |
|
||||
| **综合评分** | **69%** | DTO 和配置健壮,权限码和数据模型映射有差距 |
|
||||
@@ -1,243 +0,0 @@
|
||||
# HMS 功能审计 — Phase 5: 五种差距模式识别
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 全系统差距模式分析
|
||||
|
||||
## 总览
|
||||
|
||||
基于 Phase 0-4 的审计数据,系统性检测五种常见差距模式。
|
||||
|
||||
---
|
||||
|
||||
## 1. 模式 1:"写了没接" — 后端路由无前端调用者
|
||||
|
||||
### 检测方法
|
||||
|
||||
集合差运算:`backend_routes - web_api_calls - mp_api_calls`
|
||||
|
||||
### 发现
|
||||
|
||||
| 路由 | 模块 | 说明 | 严重性 |
|
||||
|------|------|------|--------|
|
||||
| `POST /health/patients/{id}/trends/generate` | erp-health | 趋势报告生成,无前端调用。可能为后台定时任务触发 | LOW |
|
||||
| `POST /ai/analyze/vital-signs` | erp-ai | SSE 端点,Web 和小程序均无 UI 调用 | MEDIUM |
|
||||
| `POST /ai/analyze/lab-report` | erp-ai | 同上 | MEDIUM |
|
||||
| `POST /ai/analyze/health-trend` | erp-ai | 同上 | MEDIUM |
|
||||
| `POST /ai/analyze/health-summary` | erp-ai | 同上 | MEDIUM |
|
||||
|
||||
**AI 分析 SSE 端点说明**:4 个 AI 分析端点通过 SSE 流式返回结果。前端未调用是因为 AI 分析功能可能仅通过 API 工具(如 Swagger UI)直接测试。这些端点的后端实现完整(含权限检查 `ai.analysis.manage`),但缺少前端管理界面的触发入口。
|
||||
|
||||
**趋势生成说明**:`POST .../trends/generate` 可能设计为后台任务调用(如定时生成趋势报告),而非前端直接触发。
|
||||
|
||||
### 统计
|
||||
|
||||
| 类别 | 数量 |
|
||||
|------|------|
|
||||
| 孤立后端路由(无任何前端调用) | 5 |
|
||||
| 其中 AI SSE 端点 | 4 |
|
||||
| 其中后台任务路由 | 1 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 模式 2:"接了没传" — 前端调用但参数传递不完整
|
||||
|
||||
### 检测方法
|
||||
|
||||
搜索前端 API 调用中空参数传递、缺失 header、字段遗漏。
|
||||
|
||||
### 发现
|
||||
|
||||
#### 2.1 小程序 vital_signs 字段遗漏(6 个)
|
||||
|
||||
已在 Phase 4 §4.3 详细分析。核心问题:`indicator_type` 模型无法传递晚间血压、体温、血氧等字段。
|
||||
|
||||
#### 2.2 小程序 X-Patient-Id / X-Tenant-Id Header
|
||||
|
||||
小程序通过 `request.ts` 拦截器统一注入 `X-Patient-Id` 和 `X-Tenant-Id` header。经检查:
|
||||
- 所有患者端 API 调用均通过 `request.ts` 封装,header 注入正常
|
||||
- 医护端 API 调用使用独立的 `doctorRequest` 实例,header 注入机制一致
|
||||
|
||||
#### 2.3 后端 DTO 字段前端未传递(低风险)
|
||||
|
||||
| 端点 | 后端 DTO 字段 | 前端行为 | 风险 |
|
||||
|------|-------------|---------|------|
|
||||
| `POST /vital-signs` | blood_sugar_type | 小程序不传,后端默认 NULL | LOW |
|
||||
| `POST /vital-signs` | source | 小程序不传,后端默认 "manual" | LOW |
|
||||
| `POST /device-readings/batch` | raw_data | 前端传递完整 JSON | ✓ |
|
||||
|
||||
### 统计
|
||||
|
||||
| 类别 | 数量 |
|
||||
|------|------|
|
||||
| 前端字段遗漏(HIGH) | 2(晚间血压) |
|
||||
| 前端字段遗漏(MEDIUM) | 2(体温、血氧) |
|
||||
| 前端字段遗漏(LOW) | 2(血糖类型、来源) |
|
||||
| Header 注入问题 | 0 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 模式 3:"传了没存" — 数据传递但未完整持久化
|
||||
|
||||
### 检测方法
|
||||
|
||||
追踪 4 条关键写入路径:小程序体征录入、Web 创建预约、BLE 设备同步、每日签到。
|
||||
|
||||
### 路径 1:小程序体征录入
|
||||
|
||||
```
|
||||
inputVitalSign() → POST /health/patients/{id}/vital-signs
|
||||
→ health_data_handler::create_vital_signs()
|
||||
→ health_data_service::create_vital_signs()
|
||||
→ INSERT vital_signs
|
||||
```
|
||||
|
||||
**结果**:✅ 晨间血压/心率/体重/血糖/饮水量/尿量正常写入。❌ 晚间血压/体温/血氧未写入(前端未传)。
|
||||
|
||||
### 路径 2:Web 创建预约
|
||||
|
||||
```
|
||||
AppointmentList → POST /health/appointments
|
||||
→ appointment_handler::create_appointment()
|
||||
→ appointment_service::create_appointment()
|
||||
→ CAS 原子检查 slot_available → INSERT appointment
|
||||
```
|
||||
|
||||
**结果**:✅ 完整。含 CAS 并发控制、乐观锁、事务保证。
|
||||
|
||||
### 路径 3:BLE 设备同步
|
||||
|
||||
```
|
||||
BLE scan → uploadReadings() → POST /health/patients/{id}/device-readings/batch
|
||||
→ device_reading_handler::batch_create()
|
||||
→ device_reading_service::sync_readings()
|
||||
→ INSERT device_readings + UPSERT vital_signs_hourly
|
||||
```
|
||||
|
||||
**结果**:✅ 完整。批量插入 + 小时聚合 + 事件发布。
|
||||
|
||||
### 路径 4:每日签到
|
||||
|
||||
```
|
||||
checkin() → POST /health/points/checkin
|
||||
→ points_handler::daily_checkin()
|
||||
→ points_service::daily_checkin()
|
||||
→ INSERT/UPDATE points_checkin + INSERT points_transaction
|
||||
```
|
||||
|
||||
**结果**:✅ 完整。幂等检查(同日不可重复签到)+ 事务保证 + 事件发布。
|
||||
|
||||
### 统计
|
||||
|
||||
| 路径 | 结果 |
|
||||
|------|------|
|
||||
| 体征录入 | 部分数据丢失(晚间血压等) |
|
||||
| 创建预约 | ✅ 完整 |
|
||||
| BLE 同步 | ✅ 完整 |
|
||||
| 每日签到 | ✅ 完整 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 模式 4:"存了没用" — 数据持久化但未被消费
|
||||
|
||||
### 检测方法
|
||||
|
||||
检查写入了但从未被读取或展示的数据。
|
||||
|
||||
### 发现
|
||||
|
||||
#### 4.1 事件无消费者(14 个)
|
||||
|
||||
已在 Phase 3 §4.2 详细分析。14 个事件被发布到 EventBus 但没有业务消费者:
|
||||
- `patient.updated` — 信息性事件,更新已直接处理
|
||||
- `consultation.*`(3 个) — 会话管理已在 service 内直接处理
|
||||
- `article.published/rejected` — 状态已更新
|
||||
- `points.earned/exchanged/expired`(3 个) — 积分操作已完成
|
||||
- 其余 5 个同类型
|
||||
|
||||
**评估**:这些事件通过 SSE 推送到前端,用于实时通知,不完全是"没用"。但没有专门的后端消费者处理后续业务逻辑。
|
||||
|
||||
#### 4.2 数据库字段未读取
|
||||
|
||||
| 字段 | 表 | 写入场景 | 读取场景 | 状态 |
|
||||
|------|-----|---------|---------|------|
|
||||
| raw_data | device_readings | BLE 同步时写入 | 无专门查询 | MEDIUM |
|
||||
| revoke_reason | consent | 撤回知情同意时写入 | 列表查询返回 | ✓ 已用 |
|
||||
| model_config | ai_prompt | 创建/更新 Prompt 时写入 | 分析时读取 model/temperature/max_tokens | ✓ 已用 |
|
||||
| article_revision | article_revision | 文章编辑时写入 | GET .../revisions 端点返回 | ✓ 已用 |
|
||||
|
||||
**raw_data 字段说明**:`device_readings.raw_data` 存储 BLE 设备原始 JSONB 数据。当前无专门的读取/分析页面。价值在于数据溯源和异常排查,但在 UI 层面无入口。
|
||||
|
||||
#### 4.3 entity 字段从未构造
|
||||
|
||||
Phase 2 发现 `RefRow` struct 从未构造(编译器警告),属于重构残留。
|
||||
|
||||
### 统计
|
||||
|
||||
| 类别 | 数量 | 影响 |
|
||||
|------|------|------|
|
||||
| 无消费者事件 | 14 | LOW(SSE 推送 + 审计追踪仍有价值) |
|
||||
| 未读取数据库字段 | 1(raw_data) | MEDIUM(数据溯源需要但无 UI 入口) |
|
||||
| 死代码 struct | 1(RefRow) | LOW(编译器警告,重构残留) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 模式 5:"双系统不同步" — Web 与小程序实现差异
|
||||
|
||||
### 检测方法
|
||||
|
||||
对比共享功能在 Web 和小程序的实现差异,区分"角色分叉(正常)"和"实现缺陷"。
|
||||
|
||||
### 5.1 角色分叉(正常差异)
|
||||
|
||||
| 功能 | Web(管理端) | 小程序(患者/医护端) | 差异类型 |
|
||||
|------|-------------|-------------------|---------|
|
||||
| 积分管理 | 规则 CRUD + 商品管理 + 订单核销 | 签到 + 兑换 + 查看订单 | 角色分叉 ✓ |
|
||||
| 文章管理 | CMS 全流程(创建/编辑/审核/发布) | 只读列表(仅 published) | 角色分叉 ✓ |
|
||||
| 告警管理 | 规则配置 + 仪表盘 + 处置 | 查看自身告警 + 处理 | 角色分叉 ✓ |
|
||||
| 设备管理 | 列表 + 解绑 | BLE 扫描 + 同步 | 平台差异 ✓ |
|
||||
| 预约管理 | 全 CRUD + 排班配置 | 创建/查看/取消预约 | 角色分叉 ✓ |
|
||||
| 医生管理 | 档案 CRUD + 排班管理 | 查看排班日历 | 角色分叉 ✓ |
|
||||
|
||||
### 5.2 实现缺陷(需修复)
|
||||
|
||||
| 功能 | Web | 小程序 | 差异类型 | 优先级 |
|
||||
|------|-----|--------|---------|--------|
|
||||
| 体征录入 | 结构化表单(所有字段独立输入) | indicator_type 映射(丢失晚间血压/体温/血氧) | **数据模型差异** | P1 |
|
||||
| 咨询消息 | 列表 + 详情 + 导出 | 列表 + 详情(无导出) | 功能缺失 | P3 |
|
||||
| 透析管理 | 完整 CRUD + 审阅 | **无任何入口** | **功能缺失** | P1 |
|
||||
| 知情同意 | 完整 CRUD | **无任何入口** | **功能缺失** | P1 |
|
||||
| 健康记录 | 完整 CRUD | **无任何入口** | 功能缺失 | P2 |
|
||||
| 诊断记录 | 完整 CRUD | **无任何入口** | 功能缺失 | P2 |
|
||||
| 药物管理 | 完整 CRUD | 有页面但 API 调用不明确 | 待验证 | P2 |
|
||||
| 权限码拼写 | `health.alerts.manage`(正确) | —(前端用 `health.alert.manage`) | **拼写错误** | P1 |
|
||||
|
||||
### 5.3 透析管理 — 最大差距
|
||||
|
||||
透析是唯一一个**后端完整实现但小程序完全空白**的功能域:
|
||||
- 后端:12 个路由(记录 CRUD + 处方 CRUD + 审阅 + 统计)
|
||||
- Web:完整管理界面(DialysisManageList)
|
||||
- 小程序:0 个 API 调用、0 个页面
|
||||
|
||||
**影响**:透析患者无法在移动端查看自己的透析记录和处方,医护无法在小程序端录入透析数据。
|
||||
|
||||
### 统计
|
||||
|
||||
| 差异类型 | 数量 |
|
||||
|---------|------|
|
||||
| 角色分叉(正常) | 6 |
|
||||
| 数据模型差异 | 1 |
|
||||
| 功能缺失(P1) | 3(透析/知情同意/体征映射) |
|
||||
| 功能缺失(P2) | 3(健康记录/诊断/药物) |
|
||||
| 功能缺失(P3) | 1(咨询导出) |
|
||||
| 拼写错误 | 1 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 差距模式汇总评分
|
||||
|
||||
| 模式 | 严重程度 | 发现数 | 评分 |
|
||||
|------|---------|--------|------|
|
||||
| 模式 1: 写了没接 | LOW-MEDIUM | 5 | 95%(仅 AI SSE 和趋势生成孤立) |
|
||||
| 模式 2: 接了没传 | HIGH-LOW | 6 | 75%(晚间血压数据丢失是关键缺陷) |
|
||||
| 模式 3: 传了没存 | LOW | 1 | 95%(仅体征部分字段未传导致未存) |
|
||||
| 模式 4: 存了没用 | LOW | 16 | 85%(大部分事件有 SSE 消费者,raw_data 待利用) |
|
||||
| 模式 5: 双系统不同步 | HIGH-LOW | 9 | 65%(透析/知情同意完全缺失,体征映射丢失) |
|
||||
@@ -1,227 +0,0 @@
|
||||
# HMS 功能审计 — Phase 6: 错误处理与降级审计
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 错误处理、降级策略、日志完整性
|
||||
|
||||
## 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| AppError 变体 | 8 个 |
|
||||
| 领域错误变体 | HealthError 26 + AiError 11 |
|
||||
| Handler 错误映射机制 | 统一 IntoResponse trait |
|
||||
| 生产 unwrap() | 18 处(13 低风险 + 2 中风险 + 3 无害) |
|
||||
| 审计日志 | 140 处(33 文件)+ SHA256 哈希链 |
|
||||
| 运行时 tracing 日志 | 11 处(health service 层) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 错误处理覆盖率
|
||||
|
||||
### 1.1 AppError 枚举(8 变体)
|
||||
|
||||
| 变体 | HTTP 状态码 | 触发场景 |
|
||||
|------|-----------|---------|
|
||||
| `NotFound(String)` | 404 | 资源不存在 |
|
||||
| `Validation(String)` | 400 | 输入验证失败 |
|
||||
| `Unauthorized` | 401 | 未认证 |
|
||||
| `Forbidden(String)` | 403 | 无权限 |
|
||||
| `Conflict(String)` | 409 | 唯一约束冲突 |
|
||||
| `VersionMismatch` | 409 | 乐观锁版本不匹配 |
|
||||
| `TooManyRequests` | 429 | 速率限制 |
|
||||
| `Internal(String)` | 500 | 内部错误(消息对外隐藏) |
|
||||
|
||||
### 1.2 领域错误映射
|
||||
|
||||
**HealthError → AppError**(26 变体):
|
||||
|
||||
| 映射规则 | 变体示例 | 目标 AppError |
|
||||
|---------|---------|-------------|
|
||||
| 资源不存在 | PatientNotFound, DoctorNotFound, ScheduleNotFound | NotFound |
|
||||
| 状态转换无效 | InvalidStatusTransition, AppointmentAlreadyCancelled | Validation |
|
||||
| 容量限制 | ScheduleFull | Validation |
|
||||
| 乐观锁 | VersionMismatch | VersionMismatch (409) |
|
||||
| 数据库错误 | DbError | Internal |
|
||||
| 加密错误 | EncryptionError, DecryptionError | Internal |
|
||||
|
||||
**AiError → AppError**(11 变体):
|
||||
|
||||
| 映射规则 | 变体示例 | 目标 AppError |
|
||||
|---------|---------|-------------|
|
||||
| 资源不存在 | AnalysisNotFound, PromptNotFound | NotFound |
|
||||
| 验证失败 | Validation, SanitizationError, TemplateError | Validation |
|
||||
| 提供商不可用 | ProviderUnavailable | Internal |
|
||||
| 提供商错误 | ProviderError | Internal |
|
||||
| 速率限制 | RateLimitExceeded | TooManyRequests (429) |
|
||||
|
||||
### 1.3 Handler 错误传播模式
|
||||
|
||||
所有 handler 函数统一使用 `?` 运算符自动传播错误:
|
||||
|
||||
```
|
||||
Handler → require_permission()? → service.do_work()? → Ok(Json(ApiResponse::ok(result)))
|
||||
```
|
||||
|
||||
无需手动 match/map,`From<DomainError> for AppError` 自动转换。SeaORM 的 `DbErr` 也有智能映射(RecordNotFound → 404, duplicate key → 409)。
|
||||
|
||||
### 1.4 PII 加密错误处理
|
||||
|
||||
所有加密/解密失败通过 `AppError::Internal` 返回。关键设计:
|
||||
- 错误细节仅通过 `tracing::error!` 记录到日志
|
||||
- HTTP 响应体统一替换为 `"内部错误"`,不泄露加密实现细节
|
||||
- 防止通过错误消息推断加密方案
|
||||
|
||||
### 1.5 SSE 端点错误处理
|
||||
|
||||
**预流阶段**(连接建立前):
|
||||
- 权限/验证/数据获取全部通过 `?` 传播
|
||||
- AI Provider 不可用时返回标准 JSON 错误响应(500),**不会挂起连接**
|
||||
|
||||
**流中阶段**(连接建立后):
|
||||
- Provider 断连:发送 SSE `error` 事件 → 标记分析记录为 `failed` → 发布 `ai.analysis.failed` 事件 → 优雅终止流
|
||||
- 序列化错误:使用 `unwrap_or_default()` 避免 panic
|
||||
|
||||
**结论:SSE 端点不会挂起。** Provider 不可用时有完整的错误传播和清理机制。
|
||||
|
||||
### 1.6 生产代码 unwrap() 风险
|
||||
|
||||
| 类别 | 数量 | 风险 | 建议 |
|
||||
|------|------|------|------|
|
||||
| `active.version.unwrap() + 1` | 13 | LOW | 改用 `expect("version from DB must be set")` |
|
||||
| `PluginHost::db.unwrap()` | 1 | **MEDIUM** | 改用 `ok_or(AppError::Internal(...))` |
|
||||
| 信号量 `acquire().unwrap()` | 1 | **MEDIUM** | 改用 `map_err` 处理关闭场景 |
|
||||
| Response builder `.unwrap()` | 3 | LOW | 可接受,硬编码值不会失败 |
|
||||
| `unwrap_or`/`unwrap_or_default` | ~20 | NONE | 安全模式 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 降级策略评估
|
||||
|
||||
### 2.1 Redis 不可用
|
||||
|
||||
| 场景 | 降级策略 | 评估 |
|
||||
|------|---------|------|
|
||||
| IP 限流 | **Fail-Open**:Redis 不可达时限流被旁路,请求正常放行 | ✓ 安全(宁可放过不可误杀) |
|
||||
| 账户锁定 | **可配置**:`ERP__RATE_LIMIT__FAIL_CLOSE` 环境变量控制,默认 Fail-Open,生产可切 Fail-Close | ✓ 灵活 |
|
||||
| 微信会话存储 | **优雅降级**:`AuthState.redis` 为 `Option<redis::Client>`,缺失时降级 | ✓ 安全 |
|
||||
| 断路器 | 30 秒冷却期,避免重复连接失败阻塞请求 | ✓ 成熟 |
|
||||
|
||||
**风险点**:`AppState.redis` 是 `redis::Client`(非 `Option`),仅 `AuthState` 用 `Option` 包裹。但没有全局 Redis 健康检查端点。
|
||||
|
||||
### 2.2 AI Provider (Claude) 不可用
|
||||
|
||||
| 阶段 | 行为 | 评估 |
|
||||
|------|------|------|
|
||||
| 预流(连接前) | 返回 500 JSON 错误,不挂起 | ✓ |
|
||||
| 流中(连接后) | 发送 SSE error 事件 → 标记失败 → 发布事件 → 终止流 | ✓ |
|
||||
| 分析记录 | 状态更新为 `failed`,保留错误信息 | ✓ |
|
||||
|
||||
**结论:AI Provider 不可用不会导致连接挂起。** 有完整的错误传播和清理机制。
|
||||
|
||||
### 2.3 EventBus 满载
|
||||
|
||||
| 层级 | 机制 | 评估 |
|
||||
|------|------|------|
|
||||
| 内存层 | broadcast channel 容量 1024,慢消费者收到 `Lagged` 错误 | ✓ |
|
||||
| 持久化层 | `domain_events` 表 outbox 模式,事件不丢失 | ✓ |
|
||||
| 兜底轮询 | 30 秒间隔扫描 `pending` 事件 | ✓ |
|
||||
| 最大重试 | 5 次,超限进入 `dead_letter_events` 表 | ✓ |
|
||||
|
||||
**结论:事件不会丢失。** broadcast channel 满载时实时性受影响(最多 30 秒延迟),但 outbox relay 保证最终交付。
|
||||
|
||||
### 2.4 前端 SSE 重连
|
||||
|
||||
| 维度 | 实现 | 评估 |
|
||||
|------|------|------|
|
||||
| 重连机制 | 浏览器原生 EventSource 自动重连 | 基本可用 |
|
||||
| 指数退避 | 无(浏览器默认 ~3 秒固定间隔) | ⚠️ 缺失 |
|
||||
| 最大重连次数 | 无(原生无限重试) | ⚠️ 缺失 |
|
||||
| 连接状态 UI | `useAlertSSE` 暴露 `connected` 状态但未在界面显示 | ⚠️ 缺失 |
|
||||
| Keep-alive | 后端 `Sse::new().keep_alive(KeepAlive::default())` | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## 3. 日志完整性
|
||||
|
||||
### 3.1 运行时 tracing 日志密度
|
||||
|
||||
**erp-health/src/service/ 目录**(核心业务层):
|
||||
|
||||
| 日志级别 | 调用次数 | 分布文件 |
|
||||
|---------|---------|---------|
|
||||
| `tracing::info!` | 4 | points_service(1), seed(3) |
|
||||
| `tracing::warn!` | 6 | appointment(1), device_reading(1), health_data(3), points(1), seed(1) |
|
||||
| `tracing::error!` | 0 | — |
|
||||
| `tracing::debug!` | 1 | points(1) |
|
||||
| **总计** | **11** | **5/26 文件** |
|
||||
|
||||
**关键缺口**:
|
||||
|
||||
| 文件 | 行数 | tracing 调用 | 问题 |
|
||||
|------|------|-------------|------|
|
||||
| patient_service.rs | 949 | 0 | 患者创建/更新/删除无运行时日志 |
|
||||
| appointment_service.rs | 590 | 1 | 仅取消预约边缘场景有日志 |
|
||||
| consultation_service.rs | ~400 | 0 | 咨询会话/消息操作无运行时日志 |
|
||||
| follow_up_service.rs | ~500 | 0 | 随访任务操作无运行时日志 |
|
||||
|
||||
**对比**:基础设施层日志丰富:
|
||||
- `erp-core/events.rs`:EventBus 有完整的生命周期日志
|
||||
- `erp-server/rate_limit.rs`:限流有 warn/error 日志
|
||||
- `erp-server/outbox.rs`:Outbox relay 有完整的处理日志
|
||||
|
||||
### 3.2 审计日志覆盖
|
||||
|
||||
审计日志通过 `audit_service::record` 实现,全局 140 处调用分布在 33 个文件中。
|
||||
|
||||
**Health 模块审计覆盖**:
|
||||
|
||||
| 文件 | 审计调用数 | 覆盖操作 |
|
||||
|------|----------|---------|
|
||||
| patient_service.rs | 12 | 创建/更新/删除患者、标签管理、家庭成员 CRUD、医生分配 |
|
||||
| points_service.rs | 12 | 积分发放/兑换/过期 |
|
||||
| health_data_service.rs | 10 | 体征/化验/体检记录 CRUD + 审核 |
|
||||
| article_service.rs | 7 | 文章 CRUD |
|
||||
| follow_up_service.rs | 7 | 随访任务 CRUD |
|
||||
| appointment_service.rs | 4 | 预约创建/状态变更/排班管理 |
|
||||
| 其余 7 个文件 | 各 2-3 | 咨询/诊断/同意/医生/透析/设备/告警 |
|
||||
|
||||
**审计日志特性**:
|
||||
|
||||
| 特性 | 实现 |
|
||||
|------|------|
|
||||
| 结构化字段 | tenant_id, user_id, action, resource_type, resource_id, ip_address, user_agent |
|
||||
| 变更追踪 | 更新操作记录 old_value/new_value 快照 |
|
||||
| 哈希链 | SHA256 链式签名,可验证完整性 |
|
||||
| 请求来源 | task_local 自动注入 IP + User-Agent |
|
||||
| 写入模式 | Fire-and-forget(失败仅 warn,不影响业务) |
|
||||
|
||||
**审计日志风险点**:
|
||||
- Fire-and-forget 意味着写入失败是静默的,无重试或告警
|
||||
- 哈希链在并发写入时可能出现短暂链断裂(查询和写入非原子操作)
|
||||
|
||||
### 3.3 日志分层评估
|
||||
|
||||
| 层级 | tracing 日志 | 审计日志 | 评估 |
|
||||
|------|-------------|---------|------|
|
||||
| Handler 层 | — | — | 依赖 service 层 |
|
||||
| Service 层(health) | 11 处 | 70+ 处 | 运行时日志不足,审计日志完善 |
|
||||
| Service 层(infrastructure) | 丰富 | — | 日志密度高 |
|
||||
| EventBus | 完整 | — | 发布/消费全链路可追踪 |
|
||||
| 加密层 | — | — | 仅错误时 tracing::error |
|
||||
|
||||
---
|
||||
|
||||
## 4. 评分
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 错误变体覆盖 | 95% | 8 个 AppError + 37 个领域错误,覆盖全面 |
|
||||
| Handler 错误传播 | 100% | 统一 `?` + IntoResponse,无手动 match |
|
||||
| PII 错误安全 | 100% | Internal 错误消息对外隐藏,仅日志记录 |
|
||||
| SSE 挂起风险 | 100% | Provider 不可用不会挂起,有完整清理 |
|
||||
| unwrap() 安全性 | 95% | 2 处中等风险(PluginHost::db + 信号量) |
|
||||
| Redis 降级 | 90% | Fail-Open 断路器 + 可配置,缺健康检查端点 |
|
||||
| EventBus 降级 | 95% | Outbox 兜底 + 死信队列,事件不丢失 |
|
||||
| 前端重连 | 60% | 依赖原生 EventSource,缺指数退避和 UI 反馈 |
|
||||
| 运行时日志 | 30% | health service 仅 11 处 tracing,运维盲区大 |
|
||||
| 审计日志 | 95% | 140 处覆盖所有写操作 + 哈希链验证 |
|
||||
| **综合评分** | **76%** | 错误处理架构优秀,日志和前端重连有差距 |
|
||||
@@ -1,115 +0,0 @@
|
||||
# HMS 功能审计 — Phase 7: 测试覆盖率审计
|
||||
|
||||
> 日期: 2026-04-30 | 审计范围: 后端 + 前端测试分布与缺口
|
||||
|
||||
## 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试总数 | 772 个函数 |
|
||||
| 通过 | 753(97.5%) |
|
||||
| 失败 | 9(全部因 blind_indexes 表缺失) |
|
||||
| 跳过 | 0 |
|
||||
| 后端测试 | 767(611 单元 + 153 集成 + 3 多模块集成) |
|
||||
| Web 前端测试 | 5 单元(vitest)+ 5 E2E(playwright) |
|
||||
| 小程序测试 | 0 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 模块测试分布
|
||||
|
||||
### 1.1 后端 Rust 测试
|
||||
|
||||
| Crate | 单元测试 | 集成测试 | 总计 | 通过率 | 评估 |
|
||||
|-------|---------|---------|------|--------|------|
|
||||
| erp-core | 74 | — | 74 | 100% | 良好 |
|
||||
| erp-auth | 41 | 3 | 44 | 100% | 良好 |
|
||||
| erp-config | 78 | — | 78 | 100% | 良好 |
|
||||
| erp-workflow | 63 | 4 | 67 | 100% | 良好 |
|
||||
| erp-message | 72 | — | 72 | 100% | 中等(缺集成测试) |
|
||||
| erp-health | 159 | 144 | 303 | 97% | 良好 |
|
||||
| erp-ai | 36 | — | 36 | 100% | 中等(缺集成测试) |
|
||||
| erp-dialysis | 10 | 15 | 25 | 93% | 中等 |
|
||||
| erp-plugin | 78 | 2 | 80 | 100% | 良好 |
|
||||
| erp-server | — | 153 | 153 | 94% | 良好(API 集成测试) |
|
||||
| **合计** | **611** | **153** | **767** | **97.5%** | |
|
||||
|
||||
### 1.2 前端测试
|
||||
|
||||
| 层级 | 框架 | 数量 | 评估 |
|
||||
|------|------|------|------|
|
||||
| Web 单元测试 | vitest | 5 | **极低** |
|
||||
| Web E2E 测试 | playwright | 5 spec | **低** |
|
||||
| 小程序测试 | — | 0 | **无** |
|
||||
|
||||
### 1.3 测试 vs 代码规模比
|
||||
|
||||
| 维度 | 代码量 | 测试数 | 比率 |
|
||||
|------|--------|--------|------|
|
||||
| Rust 后端 | ~77k 行 | 767 | 1:100 |
|
||||
| Web 前端 | ~20k 行(163 文件) | 10 | 1:2000 |
|
||||
| 小程序 | ~15k 行(125 文件) | 0 | N/A |
|
||||
|
||||
---
|
||||
|
||||
## 2. 失败测试根因
|
||||
|
||||
9 个失败测试全部集中在 erp-health,原因一致:
|
||||
|
||||
| 测试文件 | 失败数 | 根因 |
|
||||
|---------|--------|------|
|
||||
| health_patient_tests.rs | 2 | `blind_indexes` 表不存在 |
|
||||
| health_dialysis_tests.rs | 1 | 同上 |
|
||||
| health_pii_encryption_tests.rs | 6 | 同上 |
|
||||
|
||||
**修复方案**:执行数据库迁移创建 `blind_indexes` 表。这是测试环境配置问题,非代码逻辑错误。
|
||||
|
||||
---
|
||||
|
||||
## 3. 测试缺口分析
|
||||
|
||||
### 3.1 关键缺口(按风险排序)
|
||||
|
||||
| 优先级 | 模块 | 缺口 | 影响 |
|
||||
|--------|------|------|------|
|
||||
| **P0** | erp-ai | 无集成测试(SSE 流 + 外部 API) | AI 功能仅通过手动测试验证,无法回归 |
|
||||
| **P1** | 小程序 | 完全无测试 | 40 个页面全靠手工验证 |
|
||||
| **P1** | erp-message | 无集成测试(SSE 推送) | SSE 连接/重连行为未测试 |
|
||||
| **P2** | Web 前端 | 仅 10 个测试 | 163 个文件的 API 调用/组件/路由无覆盖 |
|
||||
| **P2** | erp-dialysis | 93% 通过率 | 2 个测试失败待修复 |
|
||||
| **P3** | erp-config | 无集成测试 | 简单 CRUD,风险较低 |
|
||||
|
||||
### 3.2 测试类型覆盖矩阵
|
||||
|
||||
| 测试类型 | 后端 | Web 前端 | 小程序 |
|
||||
|---------|------|---------|--------|
|
||||
| 单元测试 | ✓ 丰富 | ⚠️ 极少 | ✗ 无 |
|
||||
| 集成测试 | ✓ health+plugin+server | ✗ 无 | ✗ 无 |
|
||||
| E2E 测试 | — | ⚠️ 5 spec | ✗ 无 |
|
||||
| 多租户隔离 | ✓ PII 加密测试 | ✗ 无 | ✗ 无 |
|
||||
| 并发测试 | ✓ 预约 CAS 测试 | ✗ 无 | ✗ 无 |
|
||||
| 安全测试 | ⚠️ 部分(权限/注入) | ✗ 无 | ✗ 无 |
|
||||
|
||||
### 3.3 测试覆盖良好的领域
|
||||
|
||||
| 领域 | 测试特点 |
|
||||
|------|---------|
|
||||
| 患者 CRUD | 完整的集成测试覆盖创建/更新/删除/列表 |
|
||||
| PII 加密 | 独立测试文件验证加密/解密/盲索引/跨租户隔离 |
|
||||
| 预约并发 | CAS 原子操作测试,验证乐观锁和排班满额 |
|
||||
| 工作流引擎 | BPMN 解析 + Token 驱动 + 任务分配测试 |
|
||||
| 权限 RBAC | 角色/权限/菜单关联测试 |
|
||||
| 插件系统 | WASM 运行时 + 动态表 CRUD + 租户隔离 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试覆盖率评分
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 后端单元测试 | 85% | 611 个,覆盖核心 service 逻辑 |
|
||||
| 后端集成测试 | 70% | 153 个 API 测试,但 AI/Message 模块缺失 |
|
||||
| 前端测试 | 5% | 仅 10 个测试覆盖 163 个文件 |
|
||||
| 小程序测试 | 0% | 完全空白 |
|
||||
| 测试稳定性 | 98% | 9 个失败因环境配置,非代码缺陷 |
|
||||
| **综合评分** | **65%** | 后端测试基础扎实,前端和 AI 是主要缺口 |
|
||||
@@ -1,363 +0,0 @@
|
||||
# HMS 功能审计报告
|
||||
|
||||
> 日期: 2026-04-30 | Git: `84fafb0` | 审计范围: 全系统(后端 + Web + 小程序)
|
||||
|
||||
## 执行摘要
|
||||
|
||||
| 维度 | 结果 |
|
||||
|------|------|
|
||||
| 总体完成度 | **83%** |
|
||||
| CRITICAL 发现 | **2 项**(小程序晚间血压丢失、告警权限码拼写错误) |
|
||||
| HIGH 发现 | **3 项**(透析/知情同意小程序缺失、前端日志严重不足) |
|
||||
| MEDIUM 发现 | **8 项** |
|
||||
| LOW 发现 | **12 项** |
|
||||
| 后端健康度 | 优秀(97.5% 测试通过,100% 调用链连通) |
|
||||
| 前端覆盖度 | 中等(Web 管理端完善,小程序关键功能缺失) |
|
||||
|
||||
### 审计范围
|
||||
|
||||
- 328 个后端路由(8 公开 + 320 受保护)
|
||||
- 235 个 Web 前端 API 调用
|
||||
- 76 个小程序 API 调用
|
||||
- 38 个 Web 页面路由
|
||||
- 40 个小程序页面
|
||||
- 45 个数据库 Entity
|
||||
- 25 个事件类型
|
||||
- 772 个测试函数
|
||||
|
||||
---
|
||||
|
||||
## 功能域评分
|
||||
|
||||
### 评分方法
|
||||
|
||||
每个功能域按 10 项审计清单打分(0-100%),加权求和:
|
||||
|
||||
| 检查项 | 权重 | 100% 标准 |
|
||||
|--------|------|----------|
|
||||
| 代码存在性 | 15% | Handler + Service + Entity 全部存在 |
|
||||
| 调用链连通性 | 15% | Handler→Service→DB 完整连通 |
|
||||
| 配置参数传递 | 5% | 所有配置字段被正确读取和使用 |
|
||||
| 降级策略 | 5% | 外部依赖不可用时有优雅降级 |
|
||||
| 错误处理 | 10% | 所有错误路径返回适当 HTTP 响应 |
|
||||
| 性能 | 5% | 列表端点有分页,查询有索引 |
|
||||
| 安全合规 | 15% | tenant_id 过滤 + 权限检查 + PII 保护 |
|
||||
| 兼容性 | 5% | Web 和小程序 API 契约一致 |
|
||||
| 日志完整性 | 10% | 关键操作有日志和审计记录 |
|
||||
| UX 一致性 | 15% | 两端展示数据一致,交互逻辑统一 |
|
||||
|
||||
### 1. 患者管理: 93%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 17 handler + 完整 service + 6 entity |
|
||||
| 调用链 | 100% | Handler→Service→Entity 全连通 |
|
||||
| 配置参数 | 100% | PII 加密密钥正确使用 |
|
||||
| 降级策略 | 90% | 加密失败有错误处理 |
|
||||
| 错误处理 | 100% | 26 种领域错误完整映射 |
|
||||
| 性能 | 90% | 分页查询 + HMAC 盲索引搜索 |
|
||||
| 安全合规 | 100% | tenant_id + RLS + PII AES-256-GCM + HMAC |
|
||||
| 兼容性 | 90% | Web/MP 基本对齐,MP 无删除(预期) |
|
||||
| 日志 | 95% | 12 处审计日志(含变更快照),运行时日志不足 |
|
||||
| UX 一致性 | 70% | 健康摘要仅 MP,家庭医生管理仅 Web |
|
||||
|
||||
### 2. 医生/排班: 88%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 5 handler + service + 2 entity |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 90% | 标准 |
|
||||
| 错误处理 | 100% | 领域错误映射 |
|
||||
| 性能 | 90% | 排班 CAS 并发控制 |
|
||||
| 安全合规 | 100% | tenant_id + 权限检查 |
|
||||
| 兼容性 | 60% | MP 仅只读列表,无管理操作 |
|
||||
| 日志 | 90% | 3 处审计日志 |
|
||||
| UX 一致性 | 60% | Web 有排班 CRUD,MP 仅查看日历 |
|
||||
|
||||
### 3. 健康数据: 85%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 18 handler + service + 7 entity |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | AES/HMAC 密钥正确使用 |
|
||||
| 降级策略 | 90% | 加密失败安全处理 |
|
||||
| 错误处理 | 100% | 完整错误映射 |
|
||||
| 性能 | 85% | 分页 + 趋势缓存,但趋势生成可能慢 |
|
||||
| 安全合规 | 100% | PII 加密 + 权限 + 危急值检测 |
|
||||
| 兼容性 | 70% | MP 丢失晚间血压/体温/血氧字段 |
|
||||
| 日志 | 80% | 10 处审计日志,运行时 tracing 不足 |
|
||||
| UX 一致性 | 60% | 小程序 indicator_type 模型限制数据录入 |
|
||||
|
||||
### 4. 预约管理: 95%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 8 handler + 完整 CRUD |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 95% | CAS 原子操作保证 |
|
||||
| 错误处理 | 100% | 排班满额/状态转换错误 |
|
||||
| 性能 | 100% | CAS 并发控制 + 分页 |
|
||||
| 安全合规 | 100% | tenant_id + 权限 |
|
||||
| 兼容性 | 90% | Web/MP 基本对齐 |
|
||||
| 日志 | 90% | 4 处审计 + 1 处 tracing |
|
||||
| UX 一致性 | 85% | Web 排班管理,MP 预约创建/查看 |
|
||||
|
||||
### 5. 随访管理: 88%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 10+5 handler + 模板管理 |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 90% | 事件驱动逾期检测 |
|
||||
| 错误处理 | 100% | 完整 |
|
||||
| 性能 | 90% | 批量操作支持 |
|
||||
| 安全合规 | 100% | 权限检查 |
|
||||
| 兼容性 | 80% | MP 仅列表+创建,无管理 |
|
||||
| 日志 | 90% | 7 处审计 |
|
||||
| UX 一致性 | 70% | Web 批量操作,MP 基础功能 |
|
||||
|
||||
### 6. 咨询管理: 94%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 9 handler |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 95% | 消息持久化保证 |
|
||||
| 错误处理 | 100% | 完整 |
|
||||
| 性能 | 90% | 分页消息列表 |
|
||||
| 安全合规 | 100% | 权限 + 数据隔离 |
|
||||
| 兼容性 | 95% | Web/MP 高度对齐 |
|
||||
| 日志 | 85% | 3 处审计 |
|
||||
| UX 一致性 | 90% | Web 有导出,MP 无(预期) |
|
||||
|
||||
### 7. 内容管理: 86%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 11+4+4 handler(文章+分类+标签) |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 文件上传配置 |
|
||||
| 降级策略 | 90% | 标准 |
|
||||
| 错误处理 | 100% | 审核流程错误 |
|
||||
| 性能 | 90% | 分页 |
|
||||
| 安全合规 | 100% | 审核权限分离 |
|
||||
| 兼容性 | 70% | MP 仅只读 published 文章 |
|
||||
| 日志 | 90% | 7 处审计 |
|
||||
| UX 一致性 | 65% | Web 完整 CMS,MP 仅文章列表 |
|
||||
|
||||
### 8. 积分商城: 90%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 28 handler(最复杂的 handler) |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 90% | 幂等签到检查 |
|
||||
| 错误处理 | 100% | 完整 |
|
||||
| 性能 | 90% | 分页 + 统计缓存 |
|
||||
| 安全合规 | 95% | 权限检查,但积分操作无事务日志 |
|
||||
| 兼容性 | 90% | Web 管理端 + MP 患者端对齐 |
|
||||
| 日志 | 85% | 12 处审计 |
|
||||
| UX 一致性 | 85% | 角色分叉正常 |
|
||||
|
||||
### 9. 告警系统: 87%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 4+4+3+4 handler |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 90% | 事件驱动评估 |
|
||||
| 错误处理 | 100% | 完整 |
|
||||
| 性能 | 90% | 分页 |
|
||||
| 安全合规 | 100% | 权限检查 |
|
||||
| 兼容性 | 70% | MP 仅查看+处理,无规则管理 |
|
||||
| 日志 | 85% | 审计日志 |
|
||||
| UX 一致性 | 60% | ⚠️ 前端 `health.alert.manage` 拼写错误导致按钮不显示 |
|
||||
|
||||
### 10. AI 分析: 70%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 12 handler + 3 entity |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 40% | 6/8 AiConfig 字段未使用 |
|
||||
| 降级策略 | 95% | SSE 不挂起,Provider 不可用优雅终止 |
|
||||
| 错误处理 | 100% | 预流/流中完整错误处理 |
|
||||
| 性能 | 80% | SSE 流式返回,但无缓存层 |
|
||||
| 安全合规 | 100% | 权限检查 + 输入消毒 |
|
||||
| 兼容性 | 50% | SSE 端点无前端 UI 调用 |
|
||||
| 日志 | 70% | 分析成功/失败有事件发布 |
|
||||
| UX 一致性 | 30% | 仅历史查看有 UI,分析触发无入口 |
|
||||
|
||||
### 11. 透析管理: 67%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 12 handler |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 90% | 标准 |
|
||||
| 错误处理 | 100% | 完整 |
|
||||
| 性能 | 90% | 分页 |
|
||||
| 安全合规 | 100% | 5 个权限码 |
|
||||
| 兼容性 | 0% | 小程序完全无入口 |
|
||||
| 日志 | 90% | 审计 + 事件 |
|
||||
| UX 一致性 | 0% | 小程序无透析功能 |
|
||||
|
||||
### 12. 统计仪表盘: 85%
|
||||
|
||||
| 检查项 | 评分 | 说明 |
|
||||
|--------|------|------|
|
||||
| 代码存在性 | 100% | 9 handler |
|
||||
| 调用链 | 100% | 全连通 |
|
||||
| 配置参数 | 100% | 正确 |
|
||||
| 降级策略 | 85% | 统计查询失败返回 500 |
|
||||
| 错误处理 | 90% | 标准 |
|
||||
| 性能 | 80% | 聚合查询可能慢 |
|
||||
| 安全合规 | 100% | 权限检查 |
|
||||
| 兼容性 | 60% | MP 仅 3 个医护端统计 |
|
||||
| 日志 | 80% | 标准 |
|
||||
| UX 一致性 | 70% | Web 完整仪表盘,MP 部分 |
|
||||
|
||||
---
|
||||
|
||||
## 发现清单(按严重程度排序)
|
||||
|
||||
### CRITICAL(2 项)
|
||||
|
||||
| # | 发现 | 模块 | 来源 |
|
||||
|---|------|------|------|
|
||||
| C1 | **小程序晚间血压数据永久丢失** — `inputVitalSign()` 的 `blood_pressure` 类型固定映射到 `*_morning` 字段,`systolic_bp_evening`/`diastolic_bp_evening` 从未写入。后端趋势服务和危急值检测支持晚间血压,但数据源缺失 | Phase 4 §4.3 | |
|
||||
| C2 | **告警管理按钮永远不显示** — 前端 AlertList.tsx 使用 `health.alert.manage`(单数),后端声明 `health.alerts.manage`(复数),权限码不匹配导致 AuthButton 永远隐藏 | Phase 4 §3.4 | |
|
||||
|
||||
### HIGH(3 项)
|
||||
|
||||
| # | 发现 | 模块 | 来源 |
|
||||
|---|------|------|------|
|
||||
| H1 | **透析管理小程序完全无入口** — 后端 12 个路由完整实现,Web 有管理界面,但小程序 0 个 API 调用、0 个页面。透析患者无法在移动端查看记录和处方 | Phase 1 §2.11, Phase 5 §5.2 | |
|
||||
| H2 | **知情同意小程序完全无入口** — 同上,后端完整但小程序无任何入口 | Phase 1 §2.12 | |
|
||||
| H3 | **Health service 层运行时日志极缺** — 26 个 service 文件仅 11 处 tracing 日志,patient_service(949 行)0 处日志,运维排查困难 | Phase 6 §3.1 | |
|
||||
|
||||
### MEDIUM(8 项)
|
||||
|
||||
| # | 发现 | 说明 | 来源 |
|
||||
|---|------|------|------|
|
||||
| M1 | 56 个基础模块权限码未通过 PermissionDescriptor 声明 | auth/config/workflow/message/plugin 的权限通过种子数据手动注册 | Phase 4 §3.3 |
|
||||
| M2 | 4 个 AI 分析 SSE 端点无前端 UI 调用 | 可能仅通过 API 工具直接测试 | Phase 5 §1 |
|
||||
| M3 | 小程序丢失体温/血氧数据 | body_temperature/spo2 无 indicator_type | Phase 4 §4.3 |
|
||||
| M4 | 前端 SSE 重连无指数退避 | 依赖浏览器原生 EventSource,固定 3 秒间隔 | Phase 6 §2.4 |
|
||||
| M5 | erp-ai 无集成测试 | SSE 流 + 外部 API 调用无法回归测试 | Phase 7 §3.1 |
|
||||
| M6 | Web 前端测试仅 10 个 | 163 个文件仅 5 单元 + 5 E2E | Phase 7 §1.2 |
|
||||
| M7 | 小程序完全无测试 | 40 个页面全靠手工验证 | Phase 7 §1.2 |
|
||||
| M8 | 健康记录/诊断记录小程序无入口 | 患者移动端无法查看 | Phase 1 §2.2 |
|
||||
|
||||
### LOW(12 项)
|
||||
|
||||
| # | 发现 | 说明 | 来源 |
|
||||
|---|------|------|------|
|
||||
| L1 | 14 个事件无业务消费者 | 通过 SSE 推送仍有价值,但无后端消费者 | Phase 3 §4.2 |
|
||||
| L2 | AiConfig 6 字段声明未使用 | 预留全局默认值,per-prompt 已覆盖 | Phase 4 §2.3 |
|
||||
| L3 | device_readings.raw_data 无读取入口 | 数据溯源需要但无 UI | Phase 5 §4.2 |
|
||||
| L4 | RefRow struct 从未构造 | 重构残留,编译器警告 | Phase 2 §1.2 |
|
||||
| L5 | 2 处 unwrap() 中等风险 | PluginHost::db + 信号量 acquire | Phase 6 §1.6 |
|
||||
| L6 | 审计日志 Fire-and-forget | 写入失败静默,无重试 | Phase 6 §3.2 |
|
||||
| L7 | health.dialysis.stats 权限无前端引用 | 透析统计页面可能无按钮 | Phase 4 §3.2 |
|
||||
| L8 | ai.provider.manage 声明但无 Handler | 预留提供商管理功能 | Phase 4 §3.2 |
|
||||
| L9 | 咨询消息导出仅 Web | 小程序无导出功能(预期) | Phase 1 §2.5 |
|
||||
| L10 | 趋势生成路由无前端调用 | 可能仅后台任务触发 | Phase 5 §1 |
|
||||
| L11 | 9 个测试因 blind_indexes 表失败 | 环境配置问题,非代码缺陷 | Phase 0 |
|
||||
| L12 | 40 个编译警告 | 多为未使用导入/变量 | Phase 0 |
|
||||
|
||||
---
|
||||
|
||||
## 差距模式分析
|
||||
|
||||
基于 Phase 5 的系统性检测,五种差距模式的发生频率和影响:
|
||||
|
||||
| 模式 | 发现数 | 最大影响 | 整体评估 |
|
||||
|------|--------|---------|---------|
|
||||
| 写了没接 | 5 | AI SSE 端点无 UI 入口 | 低风险(功能完整,缺触发入口) |
|
||||
| 接了没传 | 6 | 晚间血压数据丢失 | 高风险(数据永久丢失) |
|
||||
| 传了没存 | 0 | — | 无问题 |
|
||||
| 存了没用 | 16 | 14 事件无消费者 + raw_data 无入口 | 低风险(审计追踪仍有价值) |
|
||||
| 双系统不同步 | 9 | 透析/知情同意小程序完全缺失 | 高风险(核心功能移动端空白) |
|
||||
|
||||
**根因模式**:
|
||||
1. **后端先行,前端跟进不足** — 后端功能完整度高(100% 调用链),但小程序覆盖仅 60%
|
||||
2. **角色分叉设计正确但执行不彻底** — 透析/知情同意应覆盖小程序医护端但未实现
|
||||
3. **数据模型差异** — 小程序 indicator_type 简化模型与后端完整字段不匹配
|
||||
|
||||
---
|
||||
|
||||
## 测试覆盖率总结
|
||||
|
||||
| 层级 | 测试数 | 通过率 | 评估 |
|
||||
|------|--------|--------|------|
|
||||
| Rust 单元测试 | 611 | 100% | 良好 |
|
||||
| Rust 集成测试 | 153 | 94% | 良好 |
|
||||
| Web 前端 | 10 | N/A | **极低** |
|
||||
| 小程序 | 0 | N/A | **无测试** |
|
||||
| **合计** | **772** | **97.5%** | |
|
||||
|
||||
关键缺口:AI 模块无集成测试(SSE + 外部 API),小程序完全无测试。
|
||||
|
||||
---
|
||||
|
||||
## 建议与优先级排序
|
||||
|
||||
### P0 — 立即修复(1-2 天)
|
||||
|
||||
| # | 建议 | 影响 | 工作量 |
|
||||
|---|------|------|--------|
|
||||
| C2 | 修复前端权限码拼写:`health.alert.manage` → `health.alerts.manage` | 告警管理按钮恢复可用 | 5 分钟 |
|
||||
| C1-fix | 小程序新增 `blood_pressure_evening` indicator_type | 晚间血压数据可正常录入 | 2 小时 |
|
||||
|
||||
### P1 — 短期优化(1-2 周)
|
||||
|
||||
| # | 建议 | 影响 | 工作量 |
|
||||
|---|------|------|--------|
|
||||
| H1 | 实现小程序透析模块(患者查看记录 + 医护端录入) | 透析患者移动端可用 | 1 周 |
|
||||
| H2 | 实现小程序知情同意页面 | 患者可管理同意书 | 3 天 |
|
||||
| H3 | 补充 health service 层 tracing 日志 | 运维可追踪关键操作 | 2 天 |
|
||||
| M5 | 补充 erp-ai 集成测试 | AI SSE 流可回归测试 | 3 天 |
|
||||
|
||||
### P2 — 中期改进(1-2 月)
|
||||
|
||||
| # | 建议 | 影响 | 工作量 |
|
||||
|---|------|------|--------|
|
||||
| M1 | 为 auth/config/workflow/message/plugin 模块补充 PermissionDescriptor 声明 | 权限码自动注册,一致性提升 | 1 周 |
|
||||
| M3 | 小程序新增 body_temperature/spo2 indicator_type | 体温/血氧可录入 | 1 天 |
|
||||
| M8 | 实现小程序健康记录/诊断记录查看页面 | 患者可查看完整健康档案 | 1 周 |
|
||||
| M4/M6/M7 | 补充前端 SSE 指数退避 + 前端/小程序测试 | 系统健壮性提升 | 持续 |
|
||||
|
||||
### P3 — 长期规划
|
||||
|
||||
| # | 建议 | 影响 | 工作量 |
|
||||
|---|------|------|--------|
|
||||
| L1 | 为高价值事件添加消费者(如 consultation.opened 发送通知) | 事件驱动更完善 | 按需 |
|
||||
| L2 | 清理或接入 AiConfig 未使用字段 | 配置一致性 | 1 天 |
|
||||
| L5 | 替换 2 处中风险 unwrap() | 消除潜在 panic | 1 小时 |
|
||||
| L11 | 修复 blind_indexes 迁移使 9 个测试通过 | 测试通过率 100% | 1 小时 |
|
||||
|
||||
---
|
||||
|
||||
## 审计产出文件索引
|
||||
|
||||
| Phase | 文件 | 内容 |
|
||||
|-------|------|------|
|
||||
| 0 | [00-baseline-snapshot.md](docs/audits/00-baseline-snapshot.md) | 基线快照 |
|
||||
| 1 | [01-feature-inventory.md](docs/audits/01-feature-inventory.md) | 功能清单 + 三端映射矩阵 |
|
||||
| 2 | [02-backend-integrity.md](docs/audits/02-backend-integrity.md) | 死代码 + 调用链 + trait 覆盖率 |
|
||||
| 3 | [03-event-system.md](docs/audits/03-event-system.md) | 事件发布-消费矩阵 |
|
||||
| 4 | [04-parameter-config.md](docs/audits/04-parameter-config.md) | DTO + 配置 + 权限码 + 数据映射 |
|
||||
| 5 | [05-gap-patterns.md](docs/audits/05-gap-patterns.md) | 五种差距模式 |
|
||||
| 6 | [06-error-handling.md](docs/audits/06-error-handling.md) | 错误处理 + 降级 + 日志 |
|
||||
| 7 | [07-test-coverage.md](docs/audits/07-test-coverage.md) | 测试分布与缺口 |
|
||||
| 8 | [08-audit-report-2026-04-30.md](docs/audits/08-audit-report-2026-04-30.md) | 最终评分报告(本文档) |
|
||||
@@ -1,382 +0,0 @@
|
||||
# ERP 平台全面深度审计报告
|
||||
|
||||
> 审计日期: 2026-04-18
|
||||
> 审计方式: 前端功能链路 + API 接口测试 + 代码静态分析 + 安全渗透测试
|
||||
> 审计范围: 全部 5 个业务模块 + 2 个插件 + 前端 SPA + 安全与代码质量
|
||||
|
||||
---
|
||||
|
||||
## 一、审计总结
|
||||
|
||||
| 维度 | CRITICAL | HIGH | MEDIUM | LOW | 合计 |
|
||||
|------|----------|------|--------|-----|------|
|
||||
| 安全 | 2 | 4 | 3 | 2 | 11 |
|
||||
| 功能 | 1 | 3 | 3 | 1 | 8 |
|
||||
| 代码质量 | 0 | 1 | 3 | 6 | 10 |
|
||||
| **合计** | **3** | **8** | **9** | **9** | **29** |
|
||||
|
||||
---
|
||||
|
||||
## 二、CRITICAL — 必须立即修复
|
||||
|
||||
### C-01 Redis 凭据硬编码在配置文件中(泄露到 Git)
|
||||
|
||||
- **文件**: `crates/erp-server/config/default.toml` (line 11)
|
||||
- **现象**: `url = "redis://:redis_KBCYJk@129.204.154.246:6379"` 硬编码了远程 Redis 密码和 IP
|
||||
- **影响**: 凭据已提交到 Git 仓库,任何有代码访问权限的人都能获取 Redis 密码和服务器 IP
|
||||
- **修复**:
|
||||
1. 立即轮换 Redis 密码
|
||||
2. 将 `url` 改回 `__MUST_SET_VIA_ENV__` 占位符
|
||||
3. 使用环境变量 `ERP__REDIS__URL` 传递
|
||||
|
||||
### C-02 存储型 XSS — 用户输入未做 HTML 清理
|
||||
|
||||
- **文件**: `crates/erp-auth/src/service/user_service.rs` (创建/更新用户)
|
||||
- **现象**: 通过 `POST /api/v1/users` 可将 `<script>alert('xss')</script>` 和 `<img src=x onerror=alert(1)>` 直接存入数据库的 `display_name`、`email` 等字段
|
||||
- **影响**:
|
||||
- React JSX 自动转义避免了前端直接触发(当前安全)
|
||||
- 但原始 HTML 已存储在数据库中,在以下场景可触发:
|
||||
- 邮件模板渲染
|
||||
- PDF 导出
|
||||
- OpenAPI 文档中的 schema 示例
|
||||
- 未来使用非 React 渲染的任何场景
|
||||
- **验证**:
|
||||
```
|
||||
POST /api/v1/users {"display_name":"<img src=x onerror=alert(document.cookie)>"}
|
||||
→ 201 Created, 原始 HTML 直接入库
|
||||
```
|
||||
- **修复**: 后端入库前对所有用户可编辑字段 strip HTML tags 或 escape HTML entities
|
||||
|
||||
### C-03 首页工作台统计卡片永久 Loading
|
||||
|
||||
- **文件**: `apps/web/src/pages/Home.tsx`
|
||||
- **现象**: 4 个统计卡片(用户总数、角色数量、流程实例、未读消息)始终显示 loading 动画
|
||||
- **根因**: `useCountUp` 动画依赖数据加载,API 返回格式与前端预期不匹配
|
||||
- **影响**: 工作台页面无法展示核心统计数据,用户体验极差
|
||||
- **修复**: 修正统计 API 的数据格式,确保与 `StatisticCard` 组件预期一致
|
||||
|
||||
---
|
||||
|
||||
## 三、HIGH — 高优先级问题
|
||||
|
||||
### H-01 用户名唯一性约束未生效
|
||||
|
||||
- **文件**: `crates/erp-auth/src/service/user_service.rs` (创建用户)
|
||||
- **现象**: 用相同 `username` 创建两次用户均返回 `201 Created`
|
||||
- **影响**: 可能导致身份混淆、审计日志混乱
|
||||
- **修复**: 在创建用户前先查询 `username` 是否已存在(同 tenant_id + 未删除),或添加数据库唯一索引
|
||||
|
||||
### H-02 消息模板 API 返回空 body
|
||||
|
||||
- **文件**: `GET /api/v1/messages/templates`
|
||||
- **现象**: 返回空 HTTP body(非 JSON 格式),前端无法解析
|
||||
- **影响**: 消息中心"模板"tab 无法展示数据
|
||||
- **修复**: 修复空列表的序列化处理,确保返回 `{"success":true,"data":{"data":[],"total":0}}`
|
||||
|
||||
### H-03 主题 API 返回空 body
|
||||
|
||||
- **文件**: `GET /api/v1/config/theme`
|
||||
- **现象**: 返回空 body 而非 JSON
|
||||
- **影响**: 主题设置页面无法加载当前配置
|
||||
- **修复**: 为新租户初始化默认主题配置,或 API 返回默认值
|
||||
|
||||
### H-04 JWT Token 体积过大
|
||||
|
||||
- **文件**: `crates/erp-auth/src/service/token_service.rs`
|
||||
- **现象**: Access Token 包含 64 个权限字符串,JWT payload 约 2.5KB
|
||||
- **影响**:
|
||||
- 每次 HTTP 请求都要携带 2.5KB+ 的 Authorization header
|
||||
- 影响带宽和性能,尤其在高频 API 调用场景
|
||||
- 权限变更需要等 Token 过期才生效(最长 15 分钟)
|
||||
- **修复**:
|
||||
1. 方案 A:JWT 只存角色,权限在服务端 Redis 缓存实时查询
|
||||
2. 方案 B:使用权限位图/bitmask 压缩
|
||||
3. 方案 C:减少 JWT 中的权限列表,改为中间件实时校验
|
||||
|
||||
### H-05 字段长度无限制
|
||||
|
||||
- **文件**: `crates/erp-auth/src/dto.rs`
|
||||
- **现象**: `display_name` 可接受 500+ 字符(测试通过 500 个 'A'),无 max length 验证
|
||||
- **影响**: UI 布局破坏、潜在数据库性能问题
|
||||
- **修复**: 添加 `#[validate(length(max = 100))]` 等长度约束
|
||||
|
||||
---
|
||||
|
||||
## 四、MEDIUM — 中优先级问题
|
||||
|
||||
### M-01 菜单配置前端硬编码,未使用后端 API
|
||||
|
||||
- **文件**: `apps/web/src/components/AppLayout.tsx` 或路由配置
|
||||
- **现象**: 后端 `GET /api/v1/config/menus` 返回空数组,侧边栏菜单完全前端硬编码
|
||||
- **影响**: 菜单无法通过管理后台动态配置,插件菜单需要在代码中手动添加
|
||||
- **修复**: 实现前端从 API 动态加载菜单配置,或在后端初始化默认菜单数据
|
||||
|
||||
### M-02 时间戳未本地化显示
|
||||
|
||||
- **文件**: 消息中心、审计日志等列表页面
|
||||
- **现象**: 时间显示为原始 ISO 格式 `2026-04-14T13:10:59.516776Z`,用户不友好
|
||||
- **影响**: 用户体验差
|
||||
- **修复**: 使用 dayjs 格式化为本地时间,如 `2026-04-14 21:10:59`
|
||||
|
||||
### M-03 前端路由仅做认证守卫,无权限守卫
|
||||
|
||||
- **文件**: `apps/web/src/App.tsx`
|
||||
- **现象**: 路由只检查是否已登录(token 存在),不检查用户是否有权限访问特定页面
|
||||
- **影响**: 无权限用户可以通过直接输入 URL 访问任何页面(虽然 API 层会返回 403)
|
||||
- **修复**: 在路由守卫中增加权限校验,根据 JWT 中的 permissions 控制页面可见性
|
||||
|
||||
### M-04 消息响应包含内部 tenant_id
|
||||
|
||||
- **文件**: `crates/erp-message/src/handler/message_handler.rs`
|
||||
- **现象**: `GET /api/v1/messages` 返回每条消息的 `tenant_id` 字段
|
||||
- **影响**: 泄露内部多租户架构信息
|
||||
- **修复**: 在 DTO 层排除 `tenant_id` 字段
|
||||
|
||||
### M-05 搜索缺少防抖(Debounce)
|
||||
|
||||
- **文件**: `apps/web/src/pages/Users.tsx`, `apps/web/src/components/EntitySelect.tsx`
|
||||
- **现象**: 用户搜索输入框每次按键都触发 API 请求
|
||||
- **影响**: 高频请求冲击服务器,用户体验差
|
||||
- **修复**: 添加 300ms debounce(已有 `useDebouncedValue` Hook 但未使用)
|
||||
|
||||
### M-06 Organizations 页面 useCallback/useEffect 循环依赖
|
||||
|
||||
- **文件**: `apps/web/src/pages/Organizations.tsx`
|
||||
- **现象**: useCallback 依赖项导致 useEffect 无限循环渲染
|
||||
- **影响**: 性能问题、可能导致浏览器卡顿
|
||||
- **修复**: 重构 useCallback 依赖项,消除循环
|
||||
|
||||
### M-07 测试数据残留在生产数据库
|
||||
|
||||
- **现象**: 数据库中存在以下测试用户和数据:
|
||||
- `xss_user` — display_name 为 `<script>alert('xss')</script>`
|
||||
- `test_role_api` — 测试角色
|
||||
- `audit_test_user` — 审计测试用户
|
||||
- `testuser01` — 测试用户
|
||||
- `test_user_api` — API 测试用户
|
||||
- `Perf test` 消息 — 性能测试消息
|
||||
- `business_key: PERF-TEST-26311` 的待办任务
|
||||
- **影响**: 数据污染、潜在安全风险
|
||||
- **修复**: 清理所有测试数据,确保数据库只包含有意义的业务数据
|
||||
|
||||
### M-08 系统设置多个 tab 数据为空
|
||||
|
||||
- **现象**: 数据字典、编号规则、系统参数、语言管理等 tab 均无种子数据
|
||||
- **影响**: 系统看起来像空的,用户需要手动配置所有基础数据
|
||||
- **修复**: 在 `on_tenant_created` 中初始化默认字典(客户类型、行业、地区等)
|
||||
|
||||
### M-09 中文 API 响应编码异常
|
||||
|
||||
- **现象**: 部分中文内容在 API JSON 响应中显示为乱码(如 `\u7eef\u8364\u7cba` 而非"系统管理员")
|
||||
- **影响**: 可能是 curl 的显示问题,也可能是后端序列化配置问题
|
||||
- **修复**: 确认后端 JSON 序列化使用 `ensure_ascii: false` 等效配置
|
||||
|
||||
---
|
||||
|
||||
## 五、LOW — 低优先级 / 代码质量
|
||||
|
||||
### L-01 死代码 — graph 目录
|
||||
|
||||
- **文件**: `apps/web/src/pages/plugins/graph/` (6 个文件)
|
||||
- **现象**: 完全未使用的代码
|
||||
- **修复**: 删除或标记为实验性
|
||||
|
||||
### L-02 死代码 — 未使用的 Hooks
|
||||
|
||||
- **文件**: `apps/web/src/hooks/`
|
||||
- **现象**: `useDarkMode`, `useDebouncedValue`, `usePaginatedData`, `useApiRequest` 4 个 Hook 未被引用
|
||||
- **修复**: 清理或接入这些 Hook(特别是 useDebouncedValue 在搜索场景很有用)
|
||||
|
||||
### L-03 重复代码 — useCountUp
|
||||
|
||||
- **现象**: `useCountUp` 在 3 处重复定义
|
||||
- **修复**: 提取为共享 Hook
|
||||
|
||||
### L-04 暗色模式检测逻辑重复
|
||||
|
||||
- **现象**: `const isDark = token.colorBgContainer === '#111827'` 在 20+ 组件中重复
|
||||
- **修复**: 用已有的 `useDarkMode` Hook 替换
|
||||
|
||||
### L-05 i18n 已配置但未使用
|
||||
|
||||
- **现象**: i18next 已初始化且有 30 个翻译 key,但所有页面组件硬编码中文
|
||||
- **修复**: 逐步替换硬编码中文为 i18n key
|
||||
|
||||
### L-06 antd 废弃 API 警告
|
||||
|
||||
- **现象**: `Drawer` 的 `width` 属性、`Modal` 的 `destroyOnClose` 已废弃
|
||||
- **修复**: 升级到 antd 6 的新 API 用法
|
||||
|
||||
### L-07 ErrorBoundary 错误信息泄露
|
||||
|
||||
- **文件**: `apps/web/src/components/ErrorBoundary.tsx`
|
||||
- **现象**: 错误边界展示完整的错误堆栈给用户
|
||||
- **修复**: 生产环境只显示友好错误消息,堆栈信息仅记录到控制台
|
||||
|
||||
### L-08 Home 页面使用 dangerouslySetInnerHTML
|
||||
|
||||
- **文件**: `apps/web/src/pages/Home.tsx`
|
||||
- **现象**: 工作台页面使用 `dangerouslySetInnerHTML` 渲染内容
|
||||
- **影响**: 如果内容包含用户输入,可能导致 XSS
|
||||
- **修复**: 改用 React 组件渲染
|
||||
|
||||
### L-09 插件恢复计数不准确
|
||||
|
||||
- **现象**: `Plugin recovered: 0` 但实际 WASM 已加载
|
||||
- **修复**: 修正 recovery 计数逻辑
|
||||
|
||||
---
|
||||
|
||||
## 六、安全测试矩阵
|
||||
|
||||
| 测试项 | 结果 | 备注 |
|
||||
|--------|------|------|
|
||||
| 无 Token 访问受保护端点 | ✅ 401 | 正确拦截 |
|
||||
| 无效 Token | ✅ 401 | 正确拦截 |
|
||||
| 篡改 Token payload | ✅ 401 | HMAC 签名校验有效 |
|
||||
| 错误密码登录 | ✅ 401 | 正确拒绝 |
|
||||
| 短密码创建用户 | ✅ 400 | 验证 min=6 生效 |
|
||||
| 空 Token 刷新 | ✅ 401 | 正确拒绝 |
|
||||
| 旧 Refresh Token 重用 | ✅ 401 | 轮换机制生效 |
|
||||
| SQL 注入(搜索参数) | ✅ 安全 | SeaORM 参数化查询 |
|
||||
| SQL 注入(UUID 路径) | ✅ 安全 | UUID 解析拒绝非法字符 |
|
||||
| 存储型 XSS | ❌ 入库 | 后端未清理 HTML,React 前端安全 |
|
||||
| 无权限用户访问 API | ✅ 403 | 权限校验正确 |
|
||||
| 无权限用户提权 | ✅ 403 | 角色分配受权限保护 |
|
||||
| 限流 | ✅ 生效 | 5 次失败后触发 429 |
|
||||
| CORS 配置 | ✅ 白名单 | 仅允许 localhost 端口 |
|
||||
| 凭据泄露 | ❌ Redis 密码硬编码 | 已提交到 Git |
|
||||
|
||||
---
|
||||
|
||||
## 七、功能链路审计结果
|
||||
|
||||
### 7.1 认证链路 ✅ 基本正常
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 登录 → JWT 签发 | ✅ | access (15min) + refresh (7d) |
|
||||
| Token 刷新轮换 | ✅ | 旧 Token 使用后立即失效 |
|
||||
| 密码修改 → Token 吊销 | ✅ | 所有 refresh token 失效 |
|
||||
| 登出 → Token 吊销 | ✅ | |
|
||||
| 限流保护 | ✅ | 5 次失败后 429 |
|
||||
| 审计日志记录 | ✅ | 登录成功/失败均有记录 |
|
||||
|
||||
### 7.2 用户管理 ✅ 基本正常,有缺陷
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| CRUD 操作 | ✅ | |
|
||||
| 角色分配 | ✅ | |
|
||||
| 用户名唯一性 | ❌ | 重复用户名可创建 |
|
||||
| 输入验证 | ⚠️ | 密码有验证,其他字段长度/XSS 无验证 |
|
||||
| 软删除 | ✅ | |
|
||||
|
||||
### 7.3 权限管理 ✅ 正常
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 角色列表 | ✅ | 3 个角色(admin/viewer/test) |
|
||||
| 权限分配 | ✅ | 54 个权限可精确分配 |
|
||||
| 系统角色保护 | ⚠️ | admin 角色权限可被修改 |
|
||||
| data_scope 配置 | ❌ | 权限对话框中无 data_scope 配置入口 |
|
||||
|
||||
### 7.4 工作流引擎 ✅ 正常
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 流程定义 CRUD | ✅ | 3 个定义(draft/published) |
|
||||
| 流程发起 | ✅ | |
|
||||
| 任务审批 | ✅ | approve/reject |
|
||||
| 任务委派 | ✅ | |
|
||||
| 实例监控 | ✅ | running/terminated/suspended |
|
||||
| 超时检测 | ✅ | 60s 间隔扫描 |
|
||||
|
||||
### 7.5 消息中心 ⚠️ 部分异常
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 消息列表 | ✅ | 10 条消息 |
|
||||
| 未读计数 | ✅ | bell 图标显示未读数 |
|
||||
| 标记已读 | ✅ | |
|
||||
| 全部已读 | ✅ | |
|
||||
| 消息模板 | ❌ | API 返回空 body |
|
||||
| 通知设置 | ⚠️ | 未验证 |
|
||||
| 工作流事件 → 消息 | ✅ | "流程已启动" 消息自动生成 |
|
||||
|
||||
### 7.6 系统配置 ⚠️ 数据缺失
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| 数据字典 | ❌ | 空数据,无种子 |
|
||||
| 菜单配置 | ❌ | 后端空,前端硬编码 |
|
||||
| 编号规则 | ❌ | 空 |
|
||||
| 系统参数 | ⚠️ | 未验证 |
|
||||
| 主题设置 | ❌ | API 返回空 |
|
||||
| 语言管理 | ⚠️ | 未验证 |
|
||||
| 修改密码 | ✅ | 功能正常 |
|
||||
| 审计日志 | ✅ | |
|
||||
|
||||
### 7.7 插件系统 ✅ 基本正常
|
||||
|
||||
| 环节 | 状态 | 备注 |
|
||||
|------|------|------|
|
||||
| CRM 插件运行 | ✅ | 状态:运行中 |
|
||||
| 客户 CRUD | ✅ | 6 条客户数据 |
|
||||
| 联系人 | ✅ | |
|
||||
| 沟通记录 | ✅ | |
|
||||
| 标签管理 | ✅ | |
|
||||
| 客户关系 | ✅ | |
|
||||
| 统计概览 | ⚠️ | |
|
||||
| 销售漏斗 | ⚠️ | |
|
||||
|
||||
---
|
||||
|
||||
## 八、修复优先级建议
|
||||
|
||||
### 🔴 立即修复(本周内)
|
||||
|
||||
| 编号 | 问题 | 预计工作量 |
|
||||
|------|------|-----------|
|
||||
| C-01 | Redis 凭据从 config 移除,改用环境变量 | 0.5h |
|
||||
| C-02 | 后端添加 HTML sanitize 中间件 | 2h |
|
||||
| C-03 | 修复首页统计卡片数据格式 | 1h |
|
||||
| H-01 | 添加用户名唯一性校验 | 1h |
|
||||
|
||||
### 🟡 本迭代修复(2 周内)
|
||||
|
||||
| 编号 | 问题 | 预计工作量 |
|
||||
|------|------|-----------|
|
||||
| H-02 | 修复消息模板空返回 | 0.5h |
|
||||
| H-03 | 修复主题 API 空返回 | 0.5h |
|
||||
| H-04 | JWT 权限压缩或改为服务端查询 | 4h |
|
||||
| H-05 | 添加字段长度验证 | 1h |
|
||||
| M-03 | 添加前端路由权限守卫 | 2h |
|
||||
| M-05 | 搜索添加防抖 | 0.5h |
|
||||
| M-07 | 清理测试数据 | 1h |
|
||||
| M-08 | 初始化默认系统配置数据 | 2h |
|
||||
|
||||
### 🟢 迭代中逐步修复
|
||||
|
||||
| 编号 | 问题 |
|
||||
|------|------|
|
||||
| M-01 | 菜单动态加载 |
|
||||
| M-02 | 时间戳本地化 |
|
||||
| M-04 | API 响应排除 tenant_id |
|
||||
| M-06 | Organizations 性能修复 |
|
||||
| L-01~L-09 | 代码质量清理 |
|
||||
|
||||
---
|
||||
|
||||
## 九、系统亮点(做得好的地方)
|
||||
|
||||
1. **Token 刷新轮换机制** — 旧 Refresh Token 重用被正确拒绝
|
||||
2. **限流保护** — 登录失败 5 次后触发 429 Too Many Requests
|
||||
3. **SeaORM 参数化查询** — SQL 注入测试全部被拦截
|
||||
4. **权限校验完整性** — 无权限用户所有操作返回 403
|
||||
5. **多租户架构** — JWT 注入 tenant_id,中间件自动过滤
|
||||
6. **审计日志** — 登录/登出/密码修改等关键操作有完整记录
|
||||
7. **WASM 插件沙箱** — CRM 插件运行稳定,6 个实体全部可用
|
||||
8. **工作流引擎** — BPMN 解析、Token 驱动、任务分配完整实现
|
||||
9. **错误处理链** — thiserror → AppError → HTTP 响应的统一错误体系
|
||||
10. **优雅关闭** — CTRL+C 信号处理、模块按拓扑逆序关闭
|
||||
@@ -1,155 +0,0 @@
|
||||
# 系统全面审计报告 — 2026-04-18
|
||||
|
||||
## 审计环境
|
||||
|
||||
| 项目 | 值 |
|
||||
|---|---|
|
||||
| PostgreSQL | 18 (原生安装 D:\postgreSQL), 端口 5432 |
|
||||
| Redis | 未安装/未运行 (限流改为 fail-open 降级) |
|
||||
| 后端 | Axum 0.8, 端口 3000 |
|
||||
| 前端 | Vite 8, 端口 5174 |
|
||||
| 操作系统 | Windows 11 Pro |
|
||||
|
||||
## P0 — 严重问题(必须立即修复)
|
||||
|
||||
### 1. CRM 插件数据 403 Forbidden — ✅ 已修复
|
||||
|
||||
**现象**: 所有 CRM 数据页面(客户、联系人、沟通记录等)返回 403 错误,页面显示"加载数据失败"。
|
||||
|
||||
**根因**: CRM 插件在安装时正确注册了 9 条权限到 `permissions` 表(`erp-crm.customer.list` 等),但 **没有自动将这些权限分配给 admin 角色**。导致 JWT 中只有 `plugin.admin` 和 `plugin.list`,缺少 `erp-crm.*` 权限。
|
||||
|
||||
**修复**: 在 `erp-plugin/src/service.rs` 中新增 `grant_permissions_to_admin()` 函数,在 `install()` 和 `enable()` 中自动调用。修复后 CRM 客户列表 API 正常返回数据。
|
||||
|
||||
### 2. CRM 插件启动恢复失败 — ✅ 已修复
|
||||
|
||||
**现象**: 后端日志 `Failed to recover plugin (initialize): 数据库错误: 关系 "plugin_erp_crm_inventory_item" 不存在`
|
||||
|
||||
**根因**: CRM 插件的 `on_init` 回调尝试创建 `inventory_item` 实体的种子数据,但该表不存在。可能是 CRM 插件 WASM 代码中的实体定义与数据库迁移不匹配。
|
||||
|
||||
**影响**: 服务器重启后 CRM 插件恢复失败,`Plugins recovered: 0`。
|
||||
|
||||
**修复**: 通过升级 API 重新上传正确的 CRM WASM 二进制(22KB 替换错误的 110KB 测试插件)。修复后插件正常恢复并运行。
|
||||
|
||||
### 3. 首页统计数据卡片永久 Loading
|
||||
|
||||
**现象**: 工作台首页 4 个统计卡片(用户总数、角色数量、流程实例、未读消息)显示 loading 状态(`busy` 属性),数字不显示。
|
||||
|
||||
**根因**: 首页统计卡片使用 `useCountUp` 动画但依赖数据加载,数据加载可能失败或 API 返回格式不匹配。
|
||||
|
||||
### 4. 插件 API 路由不支持字符串 ID
|
||||
|
||||
**现象**: `/api/v1/plugins/erp-crm/customer` 返回 `UUID parsing failed`。
|
||||
|
||||
**根因**: 后端路由定义 `Path<(Uuid, String)>`,要求 `plugin_id` 必须是 UUID 格式。但插件的 manifest ID 是字符串(如 `erp-crm`)。
|
||||
|
||||
**影响**: 直接用 manifest ID 调用 API 不行,必须先查 UUID。前端已绕过此问题(使用 UUID),但 API 设计不够友好。
|
||||
|
||||
## P1 — 高优先级问题
|
||||
|
||||
### 5. XSS: 显示名未转义存储
|
||||
|
||||
**现象**: `POST /api/v1/users` 时 `display_name` 字段可以存储 `<script>alert(1)</script>`,API 返回原样值。
|
||||
|
||||
**评估**: React 框架自动转义防止了前端 XSS。但数据库中存储了原始 HTML,如果有其他客户端(如邮件、导出 PDF 等)不转义渲染,仍存在风险。
|
||||
|
||||
**建议**: 后端入库时 strip HTML tags 或 escape。
|
||||
|
||||
### 6. 重复用户名检测缺失
|
||||
|
||||
**现象**: `POST /api/v1/users` 用 `audit_test_user` 创建两次,第二次也返回 `success: true`,没有报重复错误。
|
||||
|
||||
**评估**: 第二次创建返回的 `id` 不同但 `username` 相同,说明用户名唯一性约束可能没生效。
|
||||
|
||||
### 7. 消息模板 API 返回空
|
||||
|
||||
**现象**: `GET /api/v1/messages/templates` 返回空 body(非 JSON)。
|
||||
|
||||
**根因**: 可能数据库无模板数据,且空列表情况下序列化异常。
|
||||
|
||||
### 8. 主题 API 返回空
|
||||
|
||||
**现象**: `GET /api/v1/config/theme` 返回空 body。
|
||||
|
||||
### 9. `roles/permissions` 路由冲突 — ✅ 已修复
|
||||
|
||||
**现象**: `GET /api/v1/roles/permissions` 返回 UUID 解析错误。
|
||||
|
||||
**根因**: 路由 `GET /roles/{id}` 把 `permissions` 当成 UUID 解析了。
|
||||
|
||||
**修复**: 在 `erp-auth/src/module.rs` 中,在 `/roles/{id}` 之前注册 `/roles/permissions` 精确匹配路由。修复后返回 64 条权限数据。
|
||||
|
||||
## P2 — 中优先级问题
|
||||
|
||||
### 10. CRM 插件恢复后 Plugin recovered: 0
|
||||
|
||||
后端日志显示插件加载成功但 recovery 报 0。on_init 失败导致插件状态变为 error,但实际插件 WASM 已加载到内存。
|
||||
|
||||
### 11. 创建用户时中文 display_name 解析失败
|
||||
|
||||
`POST /api/v1/users` 带 `display_name` 含中文字符时,返回 `invalid unicode code point`。可能与 curl 的编码有关而非后端 bug,需要进一步验证。
|
||||
|
||||
### 12. 菜单数据为空
|
||||
|
||||
`GET /api/v1/config/menus` 返回空数组。系统侧边栏菜单是前端硬编码的,后端菜单配置未使用。
|
||||
|
||||
### 13. 数据字典为空
|
||||
|
||||
`GET /api/v1/config/dictionaries` 返回空。这是正常的(未创建字典数据)。
|
||||
|
||||
## P3 — 低优先级 / 代码质量
|
||||
|
||||
### 14. 前端死代码
|
||||
|
||||
- `src/pages/plugins/graph/` 6 个文件完全未使用
|
||||
- `src/hooks/` 下 4 个 Hook 未被任何组件引用(useDarkMode, useDebouncedValue, usePaginatedData, useApiRequest)
|
||||
- `useCountUp` 在 3 处重复定义
|
||||
|
||||
### 15. i18n 已配置但完全未使用
|
||||
|
||||
i18next 已初始化,翻译文件有 30 个 key,但所有页面组件硬编码中文。
|
||||
|
||||
### 16. 暗色模式检测逻辑重复 20+ 次
|
||||
|
||||
`const isDark = token.colorBgContainer === '#111827'` 在 20+ 组件中重复,已有 `useDarkMode` Hook 但未使用。
|
||||
|
||||
### 17. antd 废弃 API 警告
|
||||
|
||||
- `Drawer` 的 `width` 属性已废弃
|
||||
- `Modal` 的 `destroyOnClose` 已废弃
|
||||
- `message` 静态方法无法消费 context
|
||||
|
||||
## 安全测试结果
|
||||
|
||||
| 测试项 | 结果 |
|
||||
|---|---|
|
||||
| 无 token 访问 | 401 Unauthorized |
|
||||
| 错误 token | 401 Unauthorized |
|
||||
| 错误密码登录 | 401 Unauthorized |
|
||||
| 空请求体登录 | 反序列化错误(非 500) |
|
||||
| 短密码验证 | 400 Bad Request + 详细验证信息 |
|
||||
| SQL 注入(用户名) | JSON 解析失败(被拦截) |
|
||||
| XSS(显示名) | 存储了原始 HTML(需后端过滤) |
|
||||
| 权限不足操作 | 403 Forbidden |
|
||||
|
||||
## 正常工作的功能
|
||||
|
||||
- 登录/登出/Token 刷新
|
||||
- 用户 CRUD(创建/列表/删除)
|
||||
- 角色 CRUD + 权限查看
|
||||
- 组织架构三栏管理
|
||||
- 工作流定义列表/待办任务
|
||||
- 消息列表/已读/未读计数
|
||||
- 审计日志记录
|
||||
- 插件管理(上传/启用/停用)
|
||||
- 系统设置 Tab 页(字典/语言/菜单/编号/主题/参数/审计/密码)
|
||||
- OpenAPI 文档端点
|
||||
|
||||
## 下一步工作建议
|
||||
|
||||
1. **P0-1**: 修复插件权限自动分配给 admin 角色
|
||||
2. **P0-2**: 修复 CRM 插件 on_init 中 inventory_item 表不存在的问题
|
||||
3. **P0-3**: 修复首页统计卡片数据加载
|
||||
4. **P1-5**: 后端 display_name HTML 过滤
|
||||
5. **P1-6**: 用户名唯一性约束
|
||||
6. **P1-9**: 修复 roles/permissions 路由冲突
|
||||
7. 更新所有相关文档(wiki/插件系统文档)
|
||||
@@ -1,269 +0,0 @@
|
||||
# HMS 三端一致性检查报告
|
||||
|
||||
> 日期: 2026-05-08 | 审查范围: 后端 API / Web 前端 / 微信小程序
|
||||
|
||||
## 一、审查概要
|
||||
|
||||
| 维度 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 功能设计一致性 | ⚠️ 基本一致 | 三端定位不同(管理端/患者端/医护端),功能差异多为设计意图 |
|
||||
| 数据接口一致性 | ✅ 高度一致 | 小程序 91 个端点 / Web 270+ 端点,路径/参数/响应格式统一 |
|
||||
| 业务流程链路一致性 | ⚠️ 存在差异 | 透析管理、积分商城、AI 分析存在端间覆盖不完整 |
|
||||
|
||||
**总体评分**: **一致性 82%** — 不一致项多为设计意图(端定位不同),少量为遗漏需修复。
|
||||
|
||||
---
|
||||
|
||||
## 二、三端功能覆盖矩阵
|
||||
|
||||
### 2.1 完整覆盖(三端一致)✅
|
||||
|
||||
| 业务模块 | 后端 | Web | 小程序 | 说明 |
|
||||
|----------|------|-----|--------|------|
|
||||
| 患者管理 CRUD | ✅ | ✅ | ✅(患者端) | Web 管理端 + MP 患者端 |
|
||||
| 预约管理 | ✅ | ✅ | ✅ | 完整覆盖 |
|
||||
| 咨询管理 | ✅ | ✅ | ✅ | 含医生端会话处理 |
|
||||
| 随访管理 | ✅ | ✅ | ✅ | Web 管理 + MP 医生端 + 患者端 |
|
||||
| 化验报告 | ✅ | ✅ | ✅ | 含医生端审阅 |
|
||||
| 告警管理 | ✅ | ✅ | ✅ | 确认/忽略/解除三端一致 |
|
||||
| 健康记录 | ✅ | ✅ | ✅ | CRUD 完整 |
|
||||
| 知情同意 | ✅ | ✅ | ✅ | 授权/撤回 |
|
||||
| 诊断记录 | ✅ | ✅ | ✅ | CRUD 完整 |
|
||||
| 消息通知 | ✅ | ✅ | ✅ | 列表/已读/未读(MP 不支持 SSE) |
|
||||
| 日常监测 | ✅ | ✅ | ✅ | 创建/查看 |
|
||||
| 设备读数 | ✅ | ✅ | ✅ | BLE 上传 + 查询 |
|
||||
|
||||
### 2.2 部分覆盖(存在差异)⚠️
|
||||
|
||||
| 业务模块 | 后端 | Web | 小程序 | 差异说明 |
|
||||
|----------|------|-----|--------|----------|
|
||||
| 透析管理 | ✅ 46 端点 | ⚠️ 冻结 | ✅ 完整 | **Web 端路由标记 frozen**,小程序医生端完整可用 |
|
||||
| 透析处方 | ✅ | ❌ | ✅ | **Web 端无处方管理页面**,小程序医生端有 |
|
||||
| 积分商城(患者) | ✅ | ❌ | ✅ | 签到/兑换/商品浏览仅小程序 |
|
||||
| 积分商城(管理) | ✅ | ✅ | ❌ | 规则/商品/订单管理仅 Web |
|
||||
| AI 分析(SSE) | ✅ | ✅ | ❌ | 小程序不支持 SSE 流式,仅查看历史 |
|
||||
| AI 建议审批 | ✅ | ✅ | ❌ | 仅 Web 端可审批 |
|
||||
| 文章审核流程 | ✅ | ✅ | ❌ | submit/approve/reject 仅 Web |
|
||||
| 班次管理 | ✅ | ✅ | ❌ | 管理功能仅 Web |
|
||||
| 护理计划 | ✅ | ⚠️ 冻结 | ❌ | Web 冻结,小程序无 |
|
||||
| 排班管理 | ✅ | ✅ | ❌ | 创建/管理仅 Web,小程序仅查看 |
|
||||
| 设备管理 | ✅ | ✅ | ❌ | 解绑/管理仅 Web,小程序仅 BLE 同步 |
|
||||
| BLE 网关管理 | ✅ | ✅ | ❌ | 注册/绑定/管理仅 Web |
|
||||
| 危急值阈值 | ✅ | ✅ | ⚠️ | Web 可管理,MP 仅查看 public 端点 |
|
||||
| OAuth 客户端 | ✅ | ✅ | ❌ | FHIR 合作方管理仅 Web |
|
||||
| 用药提醒 | ✅ | ❌ | ✅ | **Web 端无用药提醒页面**,小程序有 CRUD |
|
||||
|
||||
### 2.3 单端独有(设计意图,非遗漏)
|
||||
|
||||
| 独有功能 | 端 | 说明 |
|
||||
|----------|-----|------|
|
||||
| 用户/角色/权限管理 | Web | 管理后台职责 |
|
||||
| 组织/部门/岗位 | Web | 管理后台职责 |
|
||||
| 工作流引擎 | Web | 管理后台职责 |
|
||||
| 插件系统 | Web | 管理后台职责 |
|
||||
| 系统设置/字典/编号规则 | Web | 管理后台职责 |
|
||||
| 微信登录+手机号绑定 | MP | 小程序专属 |
|
||||
| 每日签到 | MP | 小程序用户粘性功能 |
|
||||
| 线下活动报名 | MP | 患者端功能 |
|
||||
| 法律文件(用户协议/隐私) | MP | 小程序合规要求 |
|
||||
| BLE 设备蓝牙连接 | MP | 小程序蓝牙能力 |
|
||||
| 埋点数据上报 | MP | 小程序分析功能 |
|
||||
| FHIR R4 接口 | 后端 | 标准互操作,无前端页面 |
|
||||
|
||||
---
|
||||
|
||||
## 三、API 接口一致性分析
|
||||
|
||||
### 3.1 请求格式一致性 ✅
|
||||
|
||||
| 维度 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| URL 路径前缀 | ✅ 一致 | 三端统一 `/api/v1/` |
|
||||
| 分页参数 | ✅ 一致 | `page`, `page_size`, 响应 `PaginatedResponse<T>` |
|
||||
| 乐观锁参数 | ✅ 一致 | 更新/删除均带 `version` 字段 |
|
||||
| 认证方式 | ✅ 一致 | Bearer JWT Token |
|
||||
| 多租户 | ✅ 一致 | 中间件自动注入 `tenant_id` |
|
||||
|
||||
### 3.2 接口覆盖统计
|
||||
|
||||
| 指标 | 后端 | Web 前端 | 小程序 |
|
||||
|------|------|----------|--------|
|
||||
| API 端点总数 | ~300+ | ~270 | ~91 |
|
||||
| Health 端点 | ~200 | ~140 | ~70 |
|
||||
| AI 端点 | ~18 | ~18 | ~3 |
|
||||
| Auth 端点 | ~8 | ~4 | ~4 |
|
||||
| Config/基础端点 | ~74 | ~108 | ~4 |
|
||||
| 消息端点 | ~7 | ~9 | ~4 |
|
||||
|
||||
### 3.3 发现的接口不一致
|
||||
|
||||
| # | 不一致项 | 后端 | Web | 小程序 | 严重度 |
|
||||
|---|----------|------|-----|--------|--------|
|
||||
| 1 | **透析处方 CRUD** | ✅ 完整端点 | ❌ 无 API 调用 | ✅ 完整调用 | **HIGH** |
|
||||
| 2 | **用药提醒 CRUD** | ✅ 完整端点 | ❌ 无 API 调用 | ✅ 完整调用 | **MEDIUM** |
|
||||
| 3 | **小程序趋势查询** `GET /health/vital-signs/trend` | ✅ 专属端点 | ❌ 使用患者级趋势 | ✅ 专属调用 | LOW(设计意图) |
|
||||
| 4 | **小程序今日体征** `GET /health/vital-signs/today` | ✅ 专属端点 | ❌ 不需要 | ✅ 专属调用 | LOW(设计意图) |
|
||||
| 5 | **公开阈值** `GET /health/critical-value-thresholds/public` | ✅ 专属端点 | ❌ 使用管理端点 | ✅ 专属调用 | LOW(设计意图) |
|
||||
| 6 | **小程序未调用透析审阅** `PUT /health/dialysis-records/:id/review` | ✅ | ❌ 冻结 | ✅ 医生端调用 | LOW |
|
||||
| 7 | **AI SSE 端点** | ✅ 4 个 SSE | ✅ 调用 | ❌ 不支持 SSE | LOW(平台限制) |
|
||||
|
||||
---
|
||||
|
||||
## 四、业务流程链路一致性
|
||||
|
||||
### 4.1 用户认证流程
|
||||
|
||||
| 步骤 | Web | 小程序 | 一致性 |
|
||||
|------|-----|--------|--------|
|
||||
| 登录方式 | 账号密码 `POST /auth/login` | 微信授权 `POST /auth/wechat/login` | ⚠️ 设计意图不同 |
|
||||
| Token 管理 | 自动刷新(过期前 30s) | 自动刷新(401 触发) | ✅ 机制一致 |
|
||||
| 登出 | `POST /auth/logout` | 清除本地 token | ✅ |
|
||||
| 手机号绑定 | N/A | `POST /auth/wechat/bind-phone` | ⚠️ MP 独有 |
|
||||
|
||||
**结论**: 认证流程符合各端定位,设计合理。
|
||||
|
||||
### 4.2 预约流程
|
||||
|
||||
| 步骤 | Web | 小程序 | 一致性 |
|
||||
|------|-----|--------|--------|
|
||||
| 选择医生 | ✅ 医生列表 | ✅ 医生列表 | ✅ |
|
||||
| 查看排班 | ✅ 日历视图 | ✅ 日历视图 | ✅ |
|
||||
| 创建预约 | ✅ `POST /health/appointments` | ✅ 相同 | ✅ |
|
||||
| 查看预约 | ✅ 列表+详情 | ✅ 列表+详情 | ✅ |
|
||||
| 取消预约 | ✅ `PUT /appointments/:id/status` | ✅ 相同 | ✅ |
|
||||
|
||||
**结论**: 预约流程三端完全一致。
|
||||
|
||||
### 4.3 健康数据录入流程
|
||||
|
||||
| 步骤 | Web | 小程序 | 一致性 |
|
||||
|------|-----|--------|--------|
|
||||
| 体征录入 | ✅ `POST /patients/:id/vital-signs` | ✅ 相同 | ✅ |
|
||||
| 查看趋势 | ✅ `GET /patients/:id/trends` | ✅ `GET /vital-signs/trend` | ⚠️ 路径不同 |
|
||||
| 今日概览 | ❌ 无此功能 | ✅ `GET /vital-signs/today` | ⚠️ MP 独有 |
|
||||
| 日常监测 | ✅ | ✅ | ✅ |
|
||||
| 化验报告上传 | ✅ 含文件上传 | ✅ 仅查看 | ⚠️ MP 无上传 |
|
||||
|
||||
**结论**: 核心录入一致,查看路径有差异(患者自服务 vs 管理端视角)。
|
||||
|
||||
### 4.4 咨询流程
|
||||
|
||||
| 步骤 | Web | 小程序 | 一致性 |
|
||||
|------|-----|--------|--------|
|
||||
| 创建会话 | ✅ | ✅ | ✅ |
|
||||
| 发送消息 | ✅ `POST /consultation-messages` | ✅ 相同 | ✅ |
|
||||
| 接收消息 | ✅ SSE 实时 | ⚠️ 8s 轮询 | ⚠️ 实时性差异 |
|
||||
| 标记已读 | ✅ | ✅ | ✅ |
|
||||
| 关闭会话 | ✅ | ✅(仅医生端) | ✅ |
|
||||
|
||||
**结论**: 核心流程一致,消息接收机制因平台限制不同。
|
||||
|
||||
### 4.5 透析管理流程 ⚠️
|
||||
|
||||
| 步骤 | Web | 小程序 | 一致性 |
|
||||
|------|-----|--------|--------|
|
||||
| 透析记录列表 | ⚠️ 冻结 | ✅ | ❌ |
|
||||
| 创建透析记录 | ⚠️ 冻结 | ✅(医生端) | ❌ |
|
||||
| 审阅透析记录 | ⚠️ 冻结 | ✅(医生端) | ❌ |
|
||||
| 透析处方管理 | ❌ 无页面 | ✅(医生端) | ❌ |
|
||||
| 透析统计 | ✅ | ✅(医生端) | ✅ |
|
||||
|
||||
**结论**: Web 端透析模块冻结,小程序端完整可用。这是最大的不一致项。
|
||||
|
||||
### 4.6 积分商城流程
|
||||
|
||||
| 步骤 | Web(管理) | 小程序(患者) | 一致性 |
|
||||
|------|------------|----------------|--------|
|
||||
| 每日签到 | ❌ | ✅ | ⚠️ MP 独有 |
|
||||
| 积分查询 | ✅ | ✅ | ✅ |
|
||||
| 商品浏览 | ✅(管理) | ✅(浏览) | ✅ |
|
||||
| 积分兑换 | ❌ | ✅ | ⚠️ MP 独有 |
|
||||
| 订单核销 | ✅ | ❌ | ⚠️ Web 独有 |
|
||||
|
||||
**结论**: 管理端与患者端分工明确,无遗漏。
|
||||
|
||||
---
|
||||
|
||||
## 五、权限码一致性
|
||||
|
||||
### 5.1 权限覆盖
|
||||
|
||||
| 模块 | 后端权限码 | Web 路由守卫 | 小程序角色检查 |
|
||||
|------|-----------|-------------|---------------|
|
||||
| health.patient | .list / .manage | ✅ 路由守卫 | ✅ isMedicalStaff |
|
||||
| health.health-data | .list / .manage | ✅ | ✅ |
|
||||
| health.appointment | .list / .manage | ✅ | ✅ |
|
||||
| health.follow-up | .list / .manage | ✅ | ✅ |
|
||||
| health.consultation | .list / .manage | ✅ | ✅ |
|
||||
| health.alerts | .list / .manage | ✅ | ✅ |
|
||||
| health.dialysis | .list / .manage | ⚠️ 冻结路由 | ✅ 医生角色 |
|
||||
| health.points | .list / .manage | ✅ | ✅ |
|
||||
| ai.analysis | .list / .manage | ✅ | ✅(仅查看) |
|
||||
| ai.suggestion | .list / .manage | ✅ | ⚠️ 仅 list |
|
||||
|
||||
**结论**: 权限码体系完整,Web 路由守卫与后端权限一一对应。
|
||||
|
||||
---
|
||||
|
||||
## 六、需要修复的不一致项
|
||||
|
||||
### CRITICAL — 无
|
||||
|
||||
### HIGH — 1 项
|
||||
|
||||
| # | 问题 | 影响 | 状态 |
|
||||
|---|------|------|------|
|
||||
| H1 | **小程序咨询消息为 8s 轮询,Web 为 SSE 实时** | 小程序消息延迟,体验不一致 | 🔧 待实现 |
|
||||
|
||||
### 已关闭(产品决策冻结)
|
||||
|
||||
| # | 问题 | 决策 |
|
||||
|---|------|------|
|
||||
| ~~H1~~ | Web 端透析管理路由冻结 | ✅ 保持冻结,当前版本不涉及医疗业务 |
|
||||
| ~~H2~~ | Web 端无透析处方管理页面 | ✅ 冻结,与透析管理同步 |
|
||||
| ~~M1~~ | Web 端无用药提醒功能 | ✅ 三端冻结 |
|
||||
| ~~M2~~ | 小程序 AI 分析仅查看历史 | ✅ 设计意图,小程序仅展示结果 |
|
||||
|
||||
### LOW — 5 项(多为设计意图)
|
||||
|
||||
| # | 问题 | 说明 |
|
||||
|---|------|------|
|
||||
| L1 | 小程序趋势查询使用专属端点 | 患者自服务视角 vs 管理端视角,设计意图 |
|
||||
| L2 | 小程序今日体征为独有功能 | 患者端需求,管理端不需要 |
|
||||
| L3 | 小程序不支持 SSE 流式分析 | 平台限制,非遗漏 |
|
||||
| L4 | 积分签到仅小程序 | 用户粘性功能,管理端不需要 |
|
||||
| L5 | 法律文件仅小程序 | 小程序上架合规要求 |
|
||||
|
||||
---
|
||||
|
||||
## 七、统计数据
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 后端 API 端点 | ~300+ |
|
||||
| Web 前端 API 调用 | ~270 |
|
||||
| 小程序 API 调用 | ~91 |
|
||||
| 三端完全一致的业务流程 | 8/11 (73%) |
|
||||
| 需要修复的不一致项 | HIGH ×2 + MEDIUM ×3 + LOW ×5 |
|
||||
| 设计意图导致的差异 | 13 项(非遗漏) |
|
||||
| 总体一致性评分 | **82%** |
|
||||
|
||||
---
|
||||
|
||||
## 八、结论与建议
|
||||
|
||||
### 8.1 总体评价
|
||||
|
||||
HMS 三端在 API 接口层面保持了高度一致性(统一前缀、统一响应格式、统一分页、统一乐观锁),差异主要集中在:
|
||||
|
||||
1. **端定位不同导致的功能差异** — 这是设计意图,不需要修复
|
||||
2. **Web 端透析模块冻结** — 这是最大的不一致项,需要产品决策
|
||||
3. **个别功能仅在单端实现** — 用药提醒、透析处方等需评估是否补齐
|
||||
|
||||
### 8.2 优先行动建议
|
||||
|
||||
1. **产品决策**: 确认透析管理模块是否在 Web 端解冻。如果血透中心是首发场景,Web 管理端的透析能力不应缺失
|
||||
2. **功能补齐**: Web 端补充透析处方管理页面(后端 API 已就绪)
|
||||
3. **功能补齐**: Web 端患者详情增加用药提醒管理(后端 API 已就绪)
|
||||
4. **体验优化**: 评估小程序咨询消息是否需要更实时的方案
|
||||
5. **能力对齐**: 评估小程序是否需要 AI 分析触发入口
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 884 KiB |
Reference in New Issue
Block a user