docs: 审计报告(8 份) + 讨论记录(4 份)

审计报告: 基线快照/功能清单/后端完整性/事件系统/参数配置/
差距模式/错误处理/测试覆盖/审计总结报告
讨论记录: 设备管线/端到端测试/三端审计/工作台重构
This commit is contained in:
iven
2026-05-03 19:32:15 +08:00
parent 78c783d332
commit d712ad78c3
13 changed files with 2683 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
# 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
```
**结果**:✅ 晨间血压/心率/体重/血糖/饮水量/尿量正常写入。❌ 晚间血压/体温/血氧未写入(前端未传)。
### 路径 2Web 创建预约
```
AppointmentList → POST /health/appointments
→ appointment_handler::create_appointment()
→ appointment_service::create_appointment()
→ CAS 原子检查 slot_available → INSERT appointment
```
**结果**:✅ 完整。含 CAS 并发控制、乐观锁、事务保证。
### 路径 3BLE 设备同步
```
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 | LOWSSE 推送 + 审计追踪仍有价值) |
| 未读取数据库字段 | 1raw_data | MEDIUM数据溯源需要但无 UI 入口) |
| 死代码 struct | 1RefRow | 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%(透析/知情同意完全缺失,体征映射丢失) |