Merge develop into test - sync latest code

This commit is contained in:
2026-04-10 12:31:19 +08:00
1255 changed files with 107256 additions and 24904 deletions

View 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 核心问题定位
#### 问题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` | 住院申请单服务(对比参考) |