66 lines
3.0 KiB
Markdown
66 lines
3.0 KiB
Markdown
# Bug #472 深度分析报告
|
||
|
||
## 标题
|
||
住院医生工作站-手术申请单:勾选手术项目无效,导致无法正常开立医嘱
|
||
|
||
## 根因分析
|
||
|
||
### 问题链路
|
||
1. 当前分支将手术项目数据源从 `getApplicationList` 改为专用接口 `getSurgeryPage`
|
||
2. `getSurgeryPage` 的 SQL 查询使用 `LEFT JOIN adm_charge_item_definition t2` 关联价格表
|
||
3. **关键问题**:SQL 中缺少 `DISTINCT ON (t1.ID)` 去重逻辑
|
||
4. 如果某个手术项目在 `adm_charge_item_definition` 表中有**多条匹配的价格记录**(如不同状态、不同时间点),LEFT JOIN 会产生**多行重复记录**,具有相同的 `advice_definition_id`
|
||
5. 前端 `mapToTransferItem` 将这些重复记录映射为 el-transfer 数据项,所有重复项的 `key` 相同
|
||
6. el-transfer 组件内部使用 key 进行 Vue 的列表渲染追踪。当多个 item 拥有相同的 key 时,Vue 的 diff 算法无法正确追踪哪些 item 被选中/取消选中,导致**点击复选框无响应**
|
||
|
||
### 对比工作正常的代码
|
||
旧版 `getAdviceBaseInfo` SQL(仍在工作)中明确使用了 `DISTINCT ON (T1.ID)` 去重:
|
||
```sql
|
||
SELECT DISTINCT ON (T1.ID) ...
|
||
```
|
||
|
||
新版 `getSurgeryPage` SQL 遗漏了这个去重逻辑。
|
||
|
||
## 影响范围
|
||
- **前端**:`surgery.vue` — el-transfer 复选框交互异常
|
||
- **后端 SQL**:`DoctorStationAdviceAppMapper.xml` — getSurgeryPage 查询缺少去重
|
||
- **数据库表**:`wor_activity_definition`(手术项目定义)、`adm_charge_item_definition`(价格定义)
|
||
- **同类问题**:`getExaminationPage` 查询也存在相同缺陷
|
||
|
||
## 修复方案
|
||
|
||
### 1. 后端 SQL 修复(根因修复)
|
||
在 `DoctorStationAdviceAppMapper.xml` 的 `getSurgeryPage` 和 `getExaminationPage` 查询中添加 `DISTINCT ON (t1.ID)`:
|
||
- `DISTINCT ON (t1.ID)` 确保每个手术/检查项目只返回一行
|
||
- PostgreSQL 的 DISTINCT ON 按 t1.ID 去重,保留每个组的第一行
|
||
|
||
### 2. 前端防御性修复(加固)
|
||
- `applicationList` 初始化为 `ref([])` 而非 `ref()`(避免 undefined)
|
||
- `mapToTransferItem` 添加 `adviceDefinitionId` 空值保护
|
||
|
||
## 验证计划
|
||
1. 修改 SQL 后,进入住院医生工作站 → 手术申请单
|
||
2. 确认"未选择"列表中每个手术项目只显示一次(无重复)
|
||
3. 点击复选框,项目应被正确选中并移入"已选择"列表
|
||
4. 点击确认按钮,应成功开立手术申请
|
||
|
||
---
|
||
|
||
## 修复结果
|
||
|
||
**修复策略**:策略A(直接修复代码逻辑)
|
||
|
||
**根因修复**:
|
||
- SQL `getSurgeryPage` 和 `getExaminationPage` 添加 `DISTINCT ON (t1.ID)` 去重
|
||
- ORDER BY 调整为 `t1.ID, t1.name ASC, t2.ID ASC`(DISTINCT ON 要求 ORDER BY 首列必须与 DISTINCT ON 一致)
|
||
|
||
**前端加固**:
|
||
- `applicationList` 初始化为 `ref([])` 而非 `ref()`
|
||
- 数据映射前过滤 `adviceDefinitionId != null` 的脏数据
|
||
|
||
**改动量**:2文件,8行增,6行删
|
||
- `DoctorStationAdviceAppMapper.xml`:+4/-4(DISTINCT ON + ORDER BY 调整)
|
||
- `surgery.vue`:+4/-2(初始化空数组 + 空值过滤)
|
||
|
||
**修复结果:✅ 成功,8行改动**
|