Files
his/MD/bugs/BUG_715_ANALYSIS.md

11 KiB
Raw Blame History

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 的所有字段:

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 参数:

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

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 分析决策

⚠️ 修复人员请先验证以上分析是否正确,再执行修复。