From aabc73535748981331e3fafa39ea254e6eae2228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E4=BA=91?= <赵云@gentronhealth.com> Date: Sat, 16 May 2026 14:30:26 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#476:=20=E6=A0=B9=E5=9B=A0+?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=B9=E6=A1=88=E6=91=98=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUG_472_ANALYSIS.md | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 BUG_472_ANALYSIS.md diff --git a/BUG_472_ANALYSIS.md b/BUG_472_ANALYSIS.md new file mode 100644 index 000000000..a0c378da9 --- /dev/null +++ b/BUG_472_ANALYSIS.md @@ -0,0 +1,65 @@ +# 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行改动**