From 4795496a6b8457274e0de0ccc989d2367f648011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8E=E4=BD=97?= Date: Sat, 13 Jun 2026 02:38:26 +0800 Subject: [PATCH] =?UTF-8?q?docs(bug):=20=E8=AF=B8=E8=91=9B=E4=BA=AE?= =?UTF-8?q?=E5=88=86=E6=9E=90=E6=8A=A5=E5=91=8A=20Bug=20#715?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/bugs/BUG_715_ANALYSIS.md | 235 ++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 MD/bugs/BUG_715_ANALYSIS.md diff --git a/MD/bugs/BUG_715_ANALYSIS.md b/MD/bugs/BUG_715_ANALYSIS.md new file mode 100644 index 000000000..e41e2c60b --- /dev/null +++ b/MD/bugs/BUG_715_ANALYSIS.md @@ -0,0 +1,235 @@ +# Bug #715 诸葛亮分析报告 + +> **文档类型**: Bug分析 +> **分析时间**: 2026-06-13 02:38:26 +> **分析模型**: mimo-v2.5 (LLM深度分析) + +--- + +## 基本信息 +- **Bug #**: 715 +- **标题**: 【住院护士站-医嘱校对】医生已签发的“临时耗材医嘱”未同步至护士站“医嘱校对”界面 +- **模块**: 病区护士工作站 +- **提出人**: 陈显精 + +--- + +Now I have all the information needed. Let me write the comprehensive analysis. + +--- + +## 一、Bug 理解 + +**禅道 Bug 标题**:【住院护士站-医嘱校对】医生已签发的"临时耗材医嘱"未同步至护士站"医嘱校对"界面 + +**重现步骤**: +1. 医生端:进入"住院医生工作站",为患者刘潇凡开具并手动点击【签发】一条"临时"医嘱(如:一次性使用导尿包) +2. 护士端:进入"住院护士站" > "医嘱校对"界面 +3. 在"未校对"标签页中,选中患者,点击【查询】 + +**期望结果**:系统应正确加载并显示已签发的临时耗材医嘱,以便护士进行核对操作。 + +**实际结果**:医嘱核对页面显示空白,提示"点击查询获取患者医嘱信息",无法查到已签发的临时耗材医嘱。 + +**附图关键信息**: +- 截图1(医生端):显示患者刘潇凡有一条状态为"**已签发**"的临时医嘱"一次性使用导尿包",类别为"耗材",金额 38.00 元 +- 截图2(护士端):"医嘱校对 → 未校对"页面,患者已选中但右侧**内容区域完全空白**,仅显示"点击查询获取患者医嘱信息" + +**综合总结**:医生签发的耗材医嘱(存储在 `wor_device_request` 表)在护士站医嘱校对页面无法查询到,因为该页面的 SQL 查询只包含了药品请求(`med_medication_request`)和服务请求(`wor_service_request`)两个 UNION 子查询,**完全缺失了耗材请求(`wor_device_request`)的第三个 UNION 子查询**。 + +--- + +## 二、根因分析 + +### 根因定位 + +**核心缺陷文件**:`healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/AdviceProcessAppMapper.xml` + +`selectInpatientAdvicePage` 查询使用 UNION 组合三种医嘱类型,但只写了两个子查询: + +| 序号 | 表名 | 医嘱类型 | 状态 | +|------|------|---------|------| +| ✅ 1 | `med_medication_request` | 药品医嘱 | 有 | +| ✅ 2 | `wor_service_request` | 诊疗服务医嘱 | 有 | +| ❌ 3 | `wor_device_request` | **耗材/器材医嘱** | **缺失** | + +**对比证据**:住院医生站的 `DoctorStationAdviceAppMapper.xml`(行 674-732)和住院医生工作站的 `AdviceManageAppMapper.xml`(行 247-305)都正确包含了 `wor_device_request` 的 UNION 子查询,证明耗材医嘱是系统支持的医嘱类型。 + +### 签发流程差异 + +从 `AdviceManageAppServiceImpl.java`(行 842-845)可确认: +- 药品/服务医嘱签发:状态保持 `DRAFT(1)` + 设置 `performer_check_id` +- **耗材医嘱签发**:状态直接设为 `ACTIVE(2)`(不使用 `performer_check_id`) + +### 涉及文件 + +| 文件 | 作用 | +|------|------| +| `AdviceProcessAppMapper.xml` | **需修改** — 缺少 `wor_device_request` UNION 子查询 | +| `AdviceProcessAppServiceImpl.java` | **可能需微调** — `therapyEnum` 条件处理(设备医嘱的 `therapy_enum` 为 NULL) | +| `InpatientAdviceDto.java` | DTO,已包含 `adviceTable` 字段,可区分医嘱来源表 | +| `DeviceRequest.java` | 基础实体,注意:**没有** `performerCheckId` 字段 | +| `CommonConstants.java:150` | 常量 `WOR_DEVICE_REQUEST = "wor_device_request"` 已定义 | + +--- + +## 三、修复方案 + +### Step 1: 在 `AdviceProcessAppMapper.xml` 中添加第三个 UNION 子查询 + +在现有的 `wor_service_request` UNION 之后、`ORDER BY T1.status_enum )` 之前,添加 `wor_device_request` 的 UNION 子查询。需映射到 `InpatientAdviceDto` 的所有字段: + +```xml +UNION +( SELECT DISTINCT T1.encounter_id, + T1.tenant_id, + #{worDeviceRequest} AS advice_table, + T1.id AS request_id, + T1.use_start_time AS start_time, + T1.use_end_time AS end_time, + T1.requester_id AS requester_id, + T1.create_time AS request_time, + NULL::integer AS skin_test_flag, + NULL::integer AS inject_flag, + NULL::bigint AS group_id, + NULL::bigint AS performer_check_id, + T2."name" AS advice_name, + T2.id AS item_id, + NULL::varchar AS volume, + T1.lot_number AS lot_number, + T1.quantity AS quantity, + T1.unit_code AS unit_code, + T1.status_enum AS request_status, + NULL::varchar AS method_code, + T1.rate_code AS rate_code, + NULL::numeric AS dose, + NULL::varchar AS dose_unit_code, + al1.id AS position_id, + al1."name" AS position_name, + NULL::integer AS dispense_per_duration, + T2.part_percent AS part_percent, + NULL::varchar AS condition_definition_name, + NULL::integer AS therapy_enum, + NULL::integer AS sort_number, + NULL::integer AS execute_num, + af.day_times, + ae.bus_no, + ap."name" AS patient_name, + al2."name" AS bed_name, + ap.gender_enum, + ap.birth_date, + ap.id AS patient_id, + fc.contract_name, + diagnosis.condition_names, + pra."name" AS admitting_doctor_name, + personal_account.balance_amount, + personal_account.id AS account_id, + NULL::varchar AS category_code, + NULL::integer AS dispense_status, + NULL::numeric AS unit_price, + NULL::numeric AS total_price, + NULL::bigint AS stopper_id, + T1.update_by AS stopper_name + FROM wor_device_request AS T1 + LEFT JOIN adm_device_definition AS T2 + ON T2.id = T1.device_def_id AND T2.delete_flag = '0' + LEFT JOIN adm_location AS al1 + ON al1.id = T1.perform_location AND al1.delete_flag = '0' + LEFT JOIN adm_encounter ae + ON ae.id = T1.encounter_id AND ae.class_enum = #{imp} AND ae.delete_flag = '0' + LEFT JOIN adm_patient ap ON ae.patient_id = ap.id AND ap.delete_flag = '0' + LEFT JOIN adm_encounter_location ael + ON ae.id = ael.encounter_id AND ael.delete_flag = '0' + AND ael.status_enum = #{active} AND ael.form_enum = #{bed} + LEFT JOIN adm_location al2 ON ael.location_id = al2.id AND al2.delete_flag = '0' + LEFT JOIN adm_account aa + ON ae.id = aa.encounter_id AND aa.encounter_flag = 1 AND aa.delete_flag = '0' + LEFT JOIN fin_contract fc ON aa.contract_no = fc.bus_no AND fc.delete_flag = '0' + LEFT JOIN ( + SELECT aed.encounter_id, STRING_AGG(ccd.name, ', ') AS condition_names + FROM adm_encounter_diagnosis aed + INNER JOIN cli_condition cc ON cc.id = aed.condition_id AND cc.delete_flag = '0' + INNER JOIN cli_condition_definition ccd ON ccd.id = cc.definition_id AND ccd.delete_flag = '0' + WHERE aed.delete_flag = '0' + GROUP BY aed.encounter_id + ) AS diagnosis ON ae.id = diagnosis.encounter_id + LEFT JOIN adm_encounter_participant aep + ON ae.id = aep.encounter_id AND aep.delete_flag = '0' + AND aep.status_enum = #{active} AND aep.type_code = #{admittingDoctor} + LEFT JOIN adm_practitioner pra ON aep.practitioner_id = pra.id AND pra.delete_flag = '0' + LEFT JOIN ( + SELECT aa.id, aa.encounter_id, + (aa.balance_amount - + COALESCE(SUM(CASE WHEN aci.status_enum IN (#{billed}, #{billable}) + THEN aci.total_price ELSE 0 END), 0) + + COALESCE(SUM(CASE WHEN aci.status_enum = #{refunded} + THEN aci.total_price ELSE 0 END), 0)) AS balance_amount + FROM adm_account aa + LEFT JOIN adm_charge_item aci ON aa.encounter_id = aci.encounter_id AND aa.delete_flag = '0' + WHERE aa.type_code = #{personalCashAccount} AND aa.delete_flag = '0' + GROUP BY aa.id, aa.encounter_id, aa.balance_amount + ) AS personal_account ON personal_account.encounter_id = ae.id + LEFT JOIN adm_frequency af ON af.rate_code = T1.rate_code AND af.delete_flag = '0' + WHERE T1.delete_flag = '0' + AND T1.generate_source_enum = #{doctorPrescription} + AND T1.refund_device_id IS NULL + AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL) + ORDER BY T1.status_enum ) +``` + +**关键差异注意**: +1. `performer_check_id` — 设为 `NULL::bigint`,因为 `wor_device_request` 没有此字段,耗材签发时直接将 `statusEnum` 设为 `ACTIVE(2)` +2. `therapy_enum` — 设为 `NULL::integer`,因为耗材医嘱没有治疗类型分类 +3. `advice_table` — 使用新的参数 `#{worDeviceRequest}` +4. WHERE 条件中不需要 `performer_check_id IS NOT NULL` 检查,因为耗材签发后直接为 `ACTIVE(2)` 状态 +5. 需要排除基于其他医嘱生成的执行记录:`AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL)` + +### Step 2: 修改 `AdviceProcessAppMapper.java` — 添加参数 + +在 `selectInpatientAdvicePage` 方法签名中添加 `worDeviceRequest` 参数: + +```java +Page selectInpatientAdvicePage( + @Param("page") Page page, + @Param(Constants.WRAPPER) QueryWrapper queryWrapper, + @Param("medMedicationRequest") String medMedicationRequest, + @Param("worServiceRequest") String worServiceRequest, + @Param("worDeviceRequest") String worDeviceRequest, // 新增 + // ... 其余参数不变 +); +``` + +### Step 3: 修改 `AdviceProcessAppServiceImpl.java` — 传递新参数 + +在 `getInpatientAdvicePage` 方法中,调用 mapper 时添加 `CommonConstants.TableName.WOR_DEVICE_REQUEST`: + +```java +Page inpatientAdvicePage + = adviceProcessAppMapper.selectInpatientAdvicePage( + new Page<>(pageNo, pageSize), queryWrapper, + CommonConstants.TableName.MED_MEDICATION_REQUEST, + CommonConstants.TableName.WOR_SERVICE_REQUEST, + CommonConstants.TableName.WOR_DEVICE_REQUEST, // 新增 + RequestStatus.DRAFT.getValue(), ...); +``` + +### Step 4: 确保 `therapyEnum` 条件兼容 + +当前代码已修复(行 `queryWrapper.and(w -> w.eq("therapy_enum", therapyEnum).or().isNull("therapy_enum"))`),允许 NULL 的 `therapy_enum` 通过。耗材医嘱的 `therapy_enum` 为 NULL,会匹配 "全部" 和 "临时" 两个筛选条件,这是正确的行为。 + +--- + +## 四、路由决策 + +**FIXER**: `guanyu`(后端开发) + +**REASON**: 此 Bug 为纯后端 SQL 查询缺陷——Mapper XML 中 UNION 缺少 `wor_device_request` 子查询,需修改 XML、Mapper 接口、ServiceImpl 调用三处。前端无需改动,属于后端数据查询层的修复,交给后端开发最合适。 + +--- + +## 路由决策 +- **FIXER_ID**: guanyu +- **修复 Agent**: guanyu(后端) +- **原因**: LLM 分析决策 + +> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。