# 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%(透析/知情同意完全缺失,体征映射丢失) |