Files
his/surgery_charge_issue_analysis.md

253 lines
10 KiB
Markdown
Raw Permalink 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.

# 门诊医生站手术申请未生成预收费明细记录问题深度分析报告
## 一、问题概述
门诊医生站手术申请保存后,门诊收费系统无法查询到对应的预收费明细记录。
## 二、已做的修复
已将 `SurgeryAppServiceImpl.java` 中的 `ChargeItem` 状态从 `DRAFT` 改为 `PLANNED`
```java
// 第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行
```java
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行
```sql
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`
```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
```java
chargeItem.setContextEnum(2); // 类型2=耗材(不是诊疗!)
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
chargeItem.setProductId(0L);
```
#### 住院申请单RequestFormManageAppServiceImpl.java
```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行
```java
// 修改前
chargeItem.setProductTable("cli_surgery");
chargeItem.setProductId(surgeryId);
// 修改后
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
// 需要获取手术对应的诊疗项目定义ID
chargeItem.setProductId(surgeryDefinitionId); // 从手术项目定义表中获取
```
**注意**:此方案需要手术项目与诊疗项目定义有对应关系。
### 方案2修改门诊收费查询 SQL
修改 `OutpatientChargeAppMapper.xml`,增加对 `cli_surgery` 表的关联:
```xml
<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 保存逻辑(临时方案)
如果手术项目暂时没有对应的诊疗项目定义,可以:
```java
// 设置 productTable 为 wor_activity_definition但 productId 设为 0
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
chargeItem.setProductId(0L);
// 在 item_name 或其他字段中保存手术名称
```
## 五、额外发现的问题
### 5.1 检查申请的 contextEnum 设置错误
`ExamApplyController.java` 第249行
```java
chargeItem.setContextEnum(2); // 类型2=诊疗
```
但实际上 `2` 对应的是 `ChargeItemContext.DEVICE`(耗材),不是诊疗。正确的应该是:
```java
chargeItem.setContextEnum(ChargeItemContext.ACTIVITY.getValue()); // 3
```
### 5.2 手术申请缺少处方号
对比检查申请,手术申请的 ChargeItem 没有设置 `prescriptionNo` 字段,可能导致收费查询时无法关联到处方信息。
## 六、修复建议优先级
1. **高优先级**:修改手术申请的 `productTable``productId`,使其与门诊收费 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` | 住院申请单服务(对比参考) |