Files
his/surgery_charge_issue_analysis.md

10 KiB
Raw Blame History

门诊医生站手术申请未生成预收费明细记录问题深度分析报告

一、问题概述

门诊医生站手术申请保存后,门诊收费系统无法查询到对应的预收费明细记录。

二、已做的修复

已将 SurgeryAppServiceImpl.java 中的 ChargeItem 状态从 DRAFT 改为 PLANNED

// 第350行
chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue()); // 收费状态:待收费

三、深入分析发现的问题

3.1 前端提交数据检查

文件: openhis-ui-vue3/src/views/doctorstation/components/surgery/surgeryApplication.vue

前端表单包含费用字段:

  • surgeryFee - 手术费用第419行
  • anesthesiaFee - 麻醉费用第426行
  • totalFee - 总费用(通过计算属性 totalCalculatedFee 自动计算并同步到表单第564-583行

提交时调用 API

  • 新增:addSurgery(form.value)第1050行
  • 修改:updateSurgery(form.value)第1066行

结论: 前端正确传递了费用字段,问题不在前端。

3.2 后端收费生成逻辑分析

文件: openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/appservice/impl/SurgeryAppServiceImpl.java

手术申请生成 ChargeItem 的代码第348-369行

ChargeItem chargeItem = new ChargeItem();
chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue()); // 收费状态:待收费
chargeItem.setBusNo("CI" + serviceRequest.getBusNo());
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue());
chargeItem.setPatientId(surgeryDto.getPatientId());
chargeItem.setContextEnum(3); // 类型3-诊疗
chargeItem.setEncounterId(surgeryDto.getEncounterId());
chargeItem.setAccountId(accountId);
chargeItem.setDefinitionId(surgeryId);
chargeItem.setEntererId(practitionerId);
chargeItem.setEnteredDate(curDate);
chargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);
chargeItem.setServiceId(serviceRequest.getId());
chargeItem.setProductTable("cli_surgery");  // 【问题1】产品表是手术表
chargeItem.setProductId(surgeryId);          // 【问题2】产品ID是手术ID
chargeItem.setRequestingOrgId(orgId);
chargeItem.setQuantityValue(BigDecimal.valueOf(1));
chargeItem.setQuantityUnit("次");
chargeItem.setUnitPrice(surgeryDto.getSurgeryFee() != null ? surgeryDto.getSurgeryFee() : new BigDecimal("0.0"));
chargeItem.setTotalPrice(surgeryDto.getTotalFee() != null ? surgeryDto.getTotalFee() : new BigDecimal("0.0"));
chargeItemService.save(chargeItem);

3.3 门诊收费查询逻辑分析

文件: openhis-server-new/openhis-application/src/main/resources/mapper/chargemanage/OutpatientChargeAppMapper.xml

关键 SQL 查询第67-148行

SELECT T1.encounter_id, T1.id, T1.patient_id, T1.context_enum, T1.status_enum, ...
    CASE
        WHEN T1.context_enum = #{activity} THEN T2."name"
        WHEN T1.context_enum = #{medication} THEN T3."name"
        WHEN T1.context_enum = #{device} THEN T4."name"
        END AS item_name,
FROM adm_charge_item AS T1
    LEFT JOIN wor_activity_definition AS T2
        ON T1.context_enum = #{activity}
            AND T1.product_id = T2.id
    LEFT JOIN med_medication_definition AS T3
        ON T1.context_enum = #{medication}
            AND T1.product_id = T3.id
    LEFT JOIN adm_device_definition AS T4
        ON T1.context_enum = #{device}
            AND T1.product_id = T4.id
-- ... 其他条件
WHERE T1.encounter_id = #{encounterId}
  AND T1.status_enum IN (#{planned}, #{billable}, #{billed}, #{refunding}, #{refunded}, #{partRefund})
  AND T1.context_enum != #{register}
  AND T1.delete_flag = '0'

参数值:

  • activity = 3 (ChargeItemContext.ACTIVITY.getValue())
  • planned = 1 (ChargeItemStatus.PLANNED.getValue())

3.4 核心问题定位

问题1ChargeItemContext 枚举定义

文件: openhis-common/src/main/java/com/openhis/common/enums/ChargeItemContext.java

public enum ChargeItemContext implements HisEnumInterface {
    MEDICATION(1, "1", "药品"),
    DEVICE(2, "2", "耗材"),
    ACTIVITY(3, "3", "项目"),    // 诊疗项目
    REGISTER(4, "4", "挂号");
}

问题2数据关联不匹配根本原因

手术申请生成的 ChargeItem

字段 说明
contextEnum 3 ACTIVITY项目
productTable "cli_surgery" 手术表
productId surgeryId 手术ID

门诊收费 SQL 查询逻辑:

  • context_enum = 3 (ACTIVITY) 时,关联 wor_activity_definition
  • SQL: LEFT JOIN wor_activity_definition AS T2 ON T1.context_enum = #{activity} AND T1.product_id = T2.id

核心问题

  1. 手术申请保存的 product_id 是手术IDcli_surgery表的ID
  2. 但 SQL 查询时关联的是 wor_activity_definition
  3. 手术ID在 wor_activity_definition 表中不存在
  4. 导致 LEFT JOIN 返回 NULL手术收费记录无法显示

3.5 对比其他申请类型的收费生成

检查申请ExamApplyController.java

chargeItem.setContextEnum(2); // 类型2=耗材(不是诊疗!)
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
chargeItem.setProductId(0L);

住院申请单RequestFormManageAppServiceImpl.java

surgeryChargeItem.setContextEnum(3); // 3-诊疗
surgeryChargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
surgeryChargeItem.setProductId(activityList.get(0).getAdviceDefinitionId()); // 诊疗定义ID

差异总结

申请类型 contextEnum productTable productId
手术申请(门诊) 3 (ACTIVITY) cli_surgery surgeryId
检查申请 2 (DEVICE) wor_activity_definition 0L
住院申请单 3 (ACTIVITY) wor_activity_definition adviceDefinitionId

四、解决方案

方案1修改手术申请的 productTable 和 productId推荐

修改 SurgeryAppServiceImpl.java 第362-363行

// 修改前
chargeItem.setProductTable("cli_surgery");
chargeItem.setProductId(surgeryId);

// 修改后
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
// 需要获取手术对应的诊疗项目定义ID
chargeItem.setProductId(surgeryDefinitionId); // 从手术项目定义表中获取

注意:此方案需要手术项目与诊疗项目定义有对应关系。

方案2修改门诊收费查询 SQL

修改 OutpatientChargeAppMapper.xml,增加对 cli_surgery 表的关联:

<select id="selectEncounterPatientPrescription" ...>
    SELECT T1.encounter_id, ...,
        CASE
            WHEN T1.context_enum = #{activity} THEN T2."name"
            WHEN T1.context_enum = #{medication} THEN T3."name"
            WHEN T1.context_enum = #{device} THEN T4."name"
            END AS item_name,
        -- 增加:从手术表获取名称
        T5.name AS surgery_name
    FROM adm_charge_item AS T1
        LEFT JOIN wor_activity_definition AS T2 ...
        LEFT JOIN med_medication_definition AS T3 ...
        LEFT JOIN adm_device_definition AS T4 ...
        -- 增加手术表关联
        LEFT JOIN cli_surgery AS T5
            ON T1.product_table = 'cli_surgery'
            AND T1.product_id = T5.id
    WHERE ...
</select>

方案3修改 ChargeItem 保存逻辑(临时方案)

如果手术项目暂时没有对应的诊疗项目定义,可以:

// 设置 productTable 为 wor_activity_definition但 productId 设为 0
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
chargeItem.setProductId(0L);
// 在 item_name 或其他字段中保存手术名称

五、额外发现的问题

5.1 检查申请的 contextEnum 设置错误

ExamApplyController.java 第249行

chargeItem.setContextEnum(2); // 类型2=诊疗

但实际上 2 对应的是 ChargeItemContext.DEVICE(耗材),不是诊疗。正确的应该是:

chargeItem.setContextEnum(ChargeItemContext.ACTIVITY.getValue()); // 3

5.2 手术申请缺少处方号

对比检查申请,手术申请的 ChargeItem 没有设置 prescriptionNo 字段,可能导致收费查询时无法关联到处方信息。

六、修复建议优先级

  1. 高优先级:修改手术申请的 productTableproductId,使其与门诊收费 SQL 查询兼容
  2. 中优先级:增加手术申请 ChargeItem 的 prescriptionNo 字段设置
  3. 低优先级:修复检查申请的 contextEnum 设置错误

七、验证步骤

  1. 修改代码后,重新编译部署
  2. 在门诊医生站创建新的手术申请
  3. 检查 adm_charge_item 表,确认记录已生成且字段正确
  4. 在门诊收费系统查询该患者的收费明细,确认手术费用能正常显示
  5. 测试收费、结算流程是否正常

八、相关文件清单

文件路径 说明
openhis-ui-vue3/src/views/doctorstation/components/surgery/surgeryApplication.vue 前端手术申请组件
openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/appservice/impl/SurgeryAppServiceImpl.java 手术申请服务实现
openhis-server-new/openhis-application/src/main/resources/mapper/chargemanage/OutpatientChargeAppMapper.xml 门诊收费查询Mapper
openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientChargeAppServiceImpl.java 门诊收费服务实现
openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/ChargeItemContext.java 收费项目类型枚举
openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/ChargeItem.java 收费项实体类
openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/controller/ExamApplyController.java 检查申请控制器(对比参考)
openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java 住院申请单服务(对比参考)