Files
his/MD/bugs/BUG_715_ANALYSIS.md

236 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<InpatientAdviceDto> selectInpatientAdvicePage(
@Param("page") Page<InpatientAdviceDto> page,
@Param(Constants.WRAPPER) QueryWrapper<InpatientAdviceParam> 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<InpatientAdviceDto> 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 分析决策
> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。