3.0 KiB
3.0 KiB
Bug #472 深度分析报告
标题
住院医生工作站-手术申请单:勾选手术项目无效,导致无法正常开立医嘱
根因分析
问题链路
- 当前分支将手术项目数据源从
getApplicationList改为专用接口getSurgeryPage getSurgeryPage的 SQL 查询使用LEFT JOIN adm_charge_item_definition t2关联价格表- 关键问题:SQL 中缺少
DISTINCT ON (t1.ID)去重逻辑 - 如果某个手术项目在
adm_charge_item_definition表中有多条匹配的价格记录(如不同状态、不同时间点),LEFT JOIN 会产生多行重复记录,具有相同的advice_definition_id - 前端
mapToTransferItem将这些重复记录映射为 el-transfer 数据项,所有重复项的key相同 - el-transfer 组件内部使用 key 进行 Vue 的列表渲染追踪。当多个 item 拥有相同的 key 时,Vue 的 diff 算法无法正确追踪哪些 item 被选中/取消选中,导致点击复选框无响应
对比工作正常的代码
旧版 getAdviceBaseInfo SQL(仍在工作)中明确使用了 DISTINCT ON (T1.ID) 去重:
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空值保护
验证计划
- 修改 SQL 后,进入住院医生工作站 → 手术申请单
- 确认"未选择"列表中每个手术项目只显示一次(无重复)
- 点击复选框,项目应被正确选中并移入"已选择"列表
- 点击确认按钮,应成功开立手术申请
修复结果
修复策略:策略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行改动