Merge develop into test - sync latest code
This commit is contained in:
252
surgery_charge_issue_analysis.md
Normal file
252
surgery_charge_issue_analysis.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 门诊医生站手术申请未生成预收费明细记录问题深度分析报告
|
||||
|
||||
## 一、问题概述
|
||||
|
||||
门诊医生站手术申请保存后,门诊收费系统无法查询到对应的预收费明细记录。
|
||||
|
||||
## 二、已做的修复
|
||||
|
||||
已将 `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 核心问题定位
|
||||
|
||||
#### 问题1:ChargeItemContext 枚举定义
|
||||
|
||||
**文件**: `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` 是手术ID(cli_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` | 住院申请单服务(对比参考) |
|
||||
Reference in New Issue
Block a user