主要修复: - 检查申请医嘱类型正确设置为诊疗(3),避免被错误归类为药品 - 检查申请收费项获取真实自费账户,预结算验证accountId必须有效存在 - 签发时补充诊疗费用项诊断关联信息变更模块: - ExamApplyController:使用ItemType枚举,获取真实账户替代占位值0 -DoctorStationAdviceAppService:按枚举标准分类医嘱,验证accountId有效性 - IChargeBillService:productId=0时从ServiceRequest.contentJson获取项目名称 - PaymentRecService:预结算自动修复账户不存在的历史数据 - Mapper:排除衍生执行记录,productId=0时补充查询逻辑 - ServiceRequest实体:activityId字段添加ALWAYS插入策略
This commit is contained in:
@@ -5,9 +5,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.core.page.TableDataInfo;
|
||||
import com.core.common.exception.ServiceException;
|
||||
import com.core.common.utils.AssignSeqUtil;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.administration.domain.Account;
|
||||
import com.openhis.administration.domain.ChargeItem;
|
||||
import com.openhis.administration.service.IAccountService;
|
||||
import com.openhis.administration.service.IChargeItemService;
|
||||
import com.openhis.check.domain.ExamApply;
|
||||
import com.openhis.check.domain.ExamApplyItem;
|
||||
@@ -17,8 +20,8 @@ import com.openhis.common.constant.CommonConstants;
|
||||
import com.openhis.common.enums.AssignSeqEnum;
|
||||
import com.openhis.common.enums.ChargeItemStatus;
|
||||
import com.openhis.common.enums.GenerateSource;
|
||||
import com.openhis.common.enums.ItemType;
|
||||
import com.openhis.common.enums.RequestStatus;
|
||||
import com.openhis.common.enums.EncounterClass;
|
||||
import com.openhis.web.check.dto.ExamApplyDto;
|
||||
import com.openhis.web.check.dto.ExamApplyItemDto;
|
||||
import com.openhis.workflow.domain.ServiceRequest;
|
||||
@@ -58,6 +61,9 @@ public class ExamApplyController extends BaseController {
|
||||
@Autowired
|
||||
private IChargeItemService chargeItemService;
|
||||
|
||||
@Autowired
|
||||
private IAccountService accountService;
|
||||
|
||||
@Autowired
|
||||
private AssignSeqUtil assignSeqUtil;
|
||||
|
||||
@@ -213,8 +219,9 @@ public class ExamApplyController extends BaseController {
|
||||
|
||||
// 检查申请不走诊疗定义,设置为0占位(数据库有NOT NULL约束)
|
||||
serviceRequest.setActivityId(0L);
|
||||
// 🔧 Bug #407修复:设置医嘱类型为诊疗(3),避免被错误识别为中成药
|
||||
serviceRequest.setCategoryEnum(3);
|
||||
// 🔧 Bug Fix: 设置医嘱类型为诊疗(3),与检验申请保持一致
|
||||
// categoryEnum=3 → SQL查询返回 adviceType=3(诊疗),避免被错误归类为药品
|
||||
serviceRequest.setCategoryEnum(ItemType.ACTIVITY.getValue());
|
||||
|
||||
// 患者和就诊信息 —— 使用前端传递的数字型ID
|
||||
if (dto.getPatientIdNum() != null) {
|
||||
@@ -227,7 +234,8 @@ public class ExamApplyController extends BaseController {
|
||||
serviceRequest.setRequesterId(currentUserId); // 开单医生
|
||||
serviceRequest.setOrgId(currentOrgId); // 执行科室
|
||||
serviceRequest.setAuthoredTime(now); // 签发时间
|
||||
serviceRequest.setCategoryEnum(EncounterClass.AMB.getValue()); // 请求类型:门诊
|
||||
// 🔧 Bug Fix: 不设置门诊类型,保留上面已设置的 categoryEnum=3(诊疗类型)
|
||||
// EncounterClass.AMB.getValue()=2 表示门诊类型,会覆盖诊疗类型导致医嘱被错误归类
|
||||
serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 来源=医生开立
|
||||
|
||||
// 将项目名称存入 contentJson,使医嘱列表能通过 JSON 字段回显 adviceName
|
||||
@@ -268,10 +276,17 @@ public class ExamApplyController extends BaseController {
|
||||
chargeItem.setRequestingOrgId(currentOrgId); // 开立科室
|
||||
chargeItem.setEnteredDate(now); // 开立时间
|
||||
|
||||
// 以下字段均有 NOT NULL 约束,检查申请不走定价/账户体系,用0占位
|
||||
// 以下字段均有 NOT NULL 约束,检查申请不走定价体系,用0占位
|
||||
chargeItem.setDefinitionId(0L); // 费用定价ID
|
||||
chargeItem.setAccountId(0L); // 关联账户ID
|
||||
chargeItem.setContextEnum(2); // 类型:2=诊疗
|
||||
// 🔧 BugFix#385: 获取患者真实的自费账户,预结算验证要求accountId必须真实存在
|
||||
Account selfAccount = accountService.getSelfAccount(dto.getEncounterId());
|
||||
if (selfAccount == null) {
|
||||
throw new ServiceException("患者自费账户不存在,无法创建检查收费项,encounterId=" + dto.getEncounterId());
|
||||
}
|
||||
chargeItem.setAccountId(selfAccount.getId());
|
||||
// 🔧 BugFix#385: 使用 ItemType.ACTIVITY.getValue()=3 表示诊疗,而不是硬编码的2
|
||||
// ItemType 枚举定义:MEDICINE=1, DEVICE=2(耗材), ACTIVITY=3(诊疗)
|
||||
chargeItem.setContextEnum(ItemType.ACTIVITY.getValue()); // 类型:3=诊疗
|
||||
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION); // 产品来源表
|
||||
chargeItem.setProductId(0L); // 产品ID
|
||||
|
||||
@@ -393,8 +408,9 @@ public class ExamApplyController extends BaseController {
|
||||
serviceRequest.setBasedOnTable("exam_apply");
|
||||
serviceRequest.setBasedOnId(examApply.getId());
|
||||
serviceRequest.setActivityId(0L);
|
||||
// 🔧 Bug #407修复:设置医嘱类型为诊疗(3),避免被错误识别为中成药
|
||||
serviceRequest.setCategoryEnum(3);
|
||||
// 🔧 Bug Fix: 设置医嘱类型为诊疗(3),与检验申请保持一致
|
||||
// categoryEnum=3 → SQL查询返回 adviceType=3(诊疗),避免被错误归类为药品
|
||||
serviceRequest.setCategoryEnum(ItemType.ACTIVITY.getValue());
|
||||
|
||||
if (dto.getPatientIdNum() != null) {
|
||||
serviceRequest.setPatientId(dto.getPatientIdNum());
|
||||
@@ -405,7 +421,8 @@ public class ExamApplyController extends BaseController {
|
||||
serviceRequest.setRequesterId(currentUserId);
|
||||
serviceRequest.setOrgId(currentOrgId);
|
||||
serviceRequest.setAuthoredTime(now);
|
||||
serviceRequest.setCategoryEnum(EncounterClass.AMB.getValue()); // 请求类型:门诊
|
||||
// 🔧 Bug Fix: 不设置门诊类型,保留上面已设置的 categoryEnum=3(诊疗类型)
|
||||
// EncounterClass.AMB.getValue()=2 表示门诊类型,会覆盖诊疗类型导致医嘱被错误归类
|
||||
serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
|
||||
// 将项目名称存入 contentJson,使医嘱列表能通过 JSON 字段回显 adviceName
|
||||
@@ -440,8 +457,14 @@ public class ExamApplyController extends BaseController {
|
||||
chargeItem.setRequestingOrgId(currentOrgId);
|
||||
chargeItem.setEnteredDate(now);
|
||||
chargeItem.setDefinitionId(0L);
|
||||
chargeItem.setAccountId(0L);
|
||||
chargeItem.setContextEnum(2);
|
||||
// 🔧 BugFix#385: 获取患者真实的自费账户,预结算验证要求accountId必须真实存在
|
||||
Account selfAccount = accountService.getSelfAccount(dto.getEncounterId());
|
||||
if (selfAccount == null) {
|
||||
throw new ServiceException("患者自费账户不存在,无法创建检查收费项,encounterId=" + dto.getEncounterId());
|
||||
}
|
||||
chargeItem.setAccountId(selfAccount.getId());
|
||||
// 🔧 BugFix#385: 使用 ItemType.ACTIVITY.getValue()=3 表示诊疗
|
||||
chargeItem.setContextEnum(ItemType.ACTIVITY.getValue());
|
||||
chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);
|
||||
chargeItem.setProductId(0L);
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.openhis.administration.domain.Encounter;
|
||||
import com.openhis.administration.service.IAccountService;
|
||||
import com.openhis.administration.service.IChargeItemService;
|
||||
import com.openhis.administration.service.IEncounterService;
|
||||
import com.openhis.administration.service.IEncounterDiagnosisService;
|
||||
import com.openhis.administration.domain.EncounterDiagnosis;
|
||||
import com.openhis.common.constant.CommonConstants;
|
||||
import com.openhis.common.constant.PromptMsgConstant;
|
||||
import com.openhis.common.enums.*;
|
||||
@@ -115,6 +117,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
@Resource
|
||||
IEncounterService iEncounterService;
|
||||
|
||||
@Resource
|
||||
IEncounterDiagnosisService iEncounterDiagnosisService;
|
||||
|
||||
@Resource
|
||||
IInventoryItemService inventoryItemService;
|
||||
|
||||
@@ -606,27 +611,24 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
|
||||
// 药品(前端adviceType=1=西药, 2=中成药 → 都属于药品后端分类)
|
||||
// 按后端 ItemType 枚举标准分类:
|
||||
// MEDICINE=1(药品)、DEVICE=2(耗材)、ACTIVITY=3(诊疗)、SURGERY=6(手术)
|
||||
|
||||
// 药品分类:adviceType == 1
|
||||
List<AdviceSaveDto> medicineList = adviceSaveList.stream()
|
||||
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType())
|
||||
|| e.getAdviceType() == 1
|
||||
|| e.getAdviceType() == 2) // 前端中成药类型值为2 → 也属于药品分类
|
||||
.collect(Collectors.toList());
|
||||
// 耗材(前端adviceType=4,后端ItemType.DEVICE=2)
|
||||
List<AdviceSaveDto> deviceList = adviceSaveList.stream()
|
||||
.filter(e -> ItemType.DEVICE.getValue().equals(e.getAdviceType())
|
||||
|| e.getAdviceType() == 4) // 前端耗材类型值为4
|
||||
.filter(e -> e.getAdviceType() != null && e.getAdviceType() == ItemType.MEDICINE.getValue())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 诊疗活动(前端adviceType=3诊疗、adviceType=5会诊、adviceType=6手术、adviceType=23检查、adviceType=26护理 → 都属于诊疗后端分类)
|
||||
// 耗材分类:adviceType == 2
|
||||
List<AdviceSaveDto> deviceList = adviceSaveList.stream()
|
||||
.filter(e -> e.getAdviceType() != null && e.getAdviceType() == ItemType.DEVICE.getValue())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 诊疗分类:adviceType == 3
|
||||
List<AdviceSaveDto> activityList = adviceSaveList.stream()
|
||||
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|
||||
|| e.getAdviceType() == 3 // 前端诊疗类型值为3
|
||||
|| e.getAdviceType() == 5 // 前端会诊类型值为5
|
||||
|| e.getAdviceType() == 6 // 前端手术类型值为6
|
||||
|| e.getAdviceType() == 23 // 前端检查类型值为23
|
||||
|| e.getAdviceType() == 26 // 前端护理类型值为26
|
||||
|| ItemType.SURGERY.getValue().equals(e.getAdviceType())) // 后端手术类型值为6
|
||||
.filter(e -> e.getAdviceType() != null
|
||||
&& (e.getAdviceType() == ItemType.ACTIVITY.getValue()
|
||||
|| e.getAdviceType() == ItemType.SURGERY.getValue())) // 手术(6)也走诊疗流程
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 🔍 Debug日志日志: 记录分类结果
|
||||
@@ -803,12 +805,71 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
// 就诊id
|
||||
Long encounterId = adviceSaveList.get(0).getEncounterId();
|
||||
|
||||
// 使用安全的更新方法,避免并发冲突 - 更新费用项状态
|
||||
// 🔧 BugFix#385: 签发时更新诊疗医嘱关联的费用项诊断信息
|
||||
// 检查申请创建的费用项没有诊断关联,需要在签发时补充
|
||||
if (!activityList.isEmpty()) {
|
||||
// 先从就诊中获取主诊断,用于补充没有诊断关联的费用项
|
||||
List<EncounterDiagnosis> encounterDiagList = iEncounterDiagnosisService.getDiagnosisList(encounterId);
|
||||
EncounterDiagnosis mainDiagnosis = iEncounterDiagnosisService.getMainDiagnosis(encounterDiagList);
|
||||
Long mainConditionId = mainDiagnosis != null ? mainDiagnosis.getConditionId() : null;
|
||||
Long mainEncounterDiagId = mainDiagnosis != null ? mainDiagnosis.getId() : null;
|
||||
|
||||
log.info("BugFix#385: 签发时获取就诊主诊断, encounterId={}, mainConditionId={}, mainEncounterDiagId={}",
|
||||
encounterId, mainConditionId, mainEncounterDiagId);
|
||||
|
||||
for (AdviceSaveDto adviceDto : activityList) {
|
||||
if (adviceDto.getRequestId() != null) {
|
||||
// 查询诊疗医嘱关联的费用项
|
||||
List<ChargeItem> chargeItems = iChargeItemService.getChargeItemInfoByReqId(
|
||||
Arrays.asList(adviceDto.getRequestId()));
|
||||
if (chargeItems != null && !chargeItems.isEmpty()) {
|
||||
// 过滤只保留诊疗类型的费用项
|
||||
ChargeItem chargeItem = chargeItems.stream()
|
||||
.filter(ci -> CommonConstants.TableName.WOR_SERVICE_REQUEST.equals(ci.getServiceTable()))
|
||||
.findFirst().orElse(null);
|
||||
if (chargeItem != null) {
|
||||
// 🔧 BugFix#385: 如果费用项没有诊断关联,使用主诊断补充
|
||||
Long conditionId = adviceDto.getConditionId();
|
||||
Long encounterDiagId = adviceDto.getEncounterDiagnosisId();
|
||||
|
||||
// 如果传入的诊断为空,使用主诊断
|
||||
if (conditionId == null) {
|
||||
conditionId = mainConditionId;
|
||||
}
|
||||
if (encounterDiagId == null) {
|
||||
encounterDiagId = mainEncounterDiagId;
|
||||
}
|
||||
|
||||
// 更新诊断关联
|
||||
if (conditionId != null || encounterDiagId != null) {
|
||||
chargeItem.setConditionId(conditionId);
|
||||
chargeItem.setEncounterDiagnosisId(encounterDiagId);
|
||||
iChargeItemService.updateById(chargeItem);
|
||||
log.info("BugFix#385: 签发时更新诊疗费用项诊断关联, chargeItemId={}, conditionId={}, encounterDiagnosisId={}",
|
||||
chargeItem.getId(), conditionId, encounterDiagId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 BugFix#385: 使用安全的更新方法,避免并发冲突 - 更新费用项状态
|
||||
// 需要处理两种情况:
|
||||
// 1. 从 DRAFT (0) → PLANNED (1):新创建的收费项目
|
||||
// 2. 从 BILLABLE (2) → PLANNED (1):保存时已设为待结算的项目
|
||||
iChargeItemService.updateChargeStatusByConditionSafe(
|
||||
encounterId,
|
||||
ChargeItemStatus.DRAFT.getValue(),
|
||||
ChargeItemStatus.PLANNED.getValue(),
|
||||
requestIds);
|
||||
|
||||
// 🔧 BugFix#385: 同时处理 BILLABLE 状态的收费项目
|
||||
iChargeItemService.updateChargeStatusByConditionSafe(
|
||||
encounterId,
|
||||
ChargeItemStatus.BILLABLE.getValue(),
|
||||
ChargeItemStatus.PLANNED.getValue(),
|
||||
requestIds);
|
||||
}
|
||||
|
||||
// 数据变更后清理相关缓存
|
||||
@@ -942,12 +1003,26 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
insertOrUpdateList = uniqueInsertOrUpdateList;
|
||||
|
||||
for (AdviceSaveDto adviceSaveDto : insertOrUpdateList) {
|
||||
// 🔧 Bug Fix: 确保accountId不为null,与handleBoundDevices保持一致
|
||||
// 🔧 Bug Fix: 确保accountId有效(不为null且账户存在)
|
||||
boolean needNewAccount = false;
|
||||
if (adviceSaveDto.getAccountId() == null) {
|
||||
needNewAccount = true;
|
||||
} else {
|
||||
// 验证账户是否存在且有效(未被删除,租户匹配)
|
||||
Account existingAccount = iAccountService.getById(adviceSaveDto.getAccountId());
|
||||
if (existingAccount == null) {
|
||||
log.warn("handMedication - 前端传入的accountId无效(账户不存在),accountId={},将重新获取或创建账户",
|
||||
adviceSaveDto.getAccountId());
|
||||
needNewAccount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needNewAccount) {
|
||||
// 尝试从患者就诊中获取默认账户ID(自费账户)
|
||||
Account selfAccount = iAccountService.getSelfAccount(adviceSaveDto.getEncounterId());
|
||||
if (selfAccount != null) {
|
||||
adviceSaveDto.setAccountId(selfAccount.getId());
|
||||
log.info("handMedication - 使用现有自费账户,accountId={}", selfAccount.getId());
|
||||
} else {
|
||||
// 自动创建自费账户
|
||||
Account newAccount = new Account();
|
||||
@@ -961,6 +1036,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
newAccount.setName(AccountType.PERSONAL_CASH_ACCOUNT.getInfo());
|
||||
Long newAccountId = iAccountService.saveAccountByRegister(newAccount);
|
||||
adviceSaveDto.setAccountId(newAccountId);
|
||||
log.info("handMedication - 自动创建自费账户,newAccountId={}", newAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1351,12 +1427,26 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
log.info("BugFix#219: ========== handDevice END ==========");
|
||||
|
||||
for (AdviceSaveDto adviceSaveDto : insertOrUpdateList) {
|
||||
// 🔧 Bug Fix: 确保accountId不为null
|
||||
// 🔧 Bug Fix: 确保accountId有效(不为null且账户存在)
|
||||
boolean needNewAccount = false;
|
||||
if (adviceSaveDto.getAccountId() == null) {
|
||||
needNewAccount = true;
|
||||
} else {
|
||||
// 验证账户是否存在且有效(未被删除,租户匹配)
|
||||
Account existingAccount = iAccountService.getById(adviceSaveDto.getAccountId());
|
||||
if (existingAccount == null) {
|
||||
log.warn("handDevice - 前端传入的accountId无效(账户不存在),accountId={},将重新获取或创建账户",
|
||||
adviceSaveDto.getAccountId());
|
||||
needNewAccount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needNewAccount) {
|
||||
// 尝试从患者就诊中获取默认账户ID(自费账户)
|
||||
Account selfAccount = iAccountService.getSelfAccount(adviceSaveDto.getEncounterId());
|
||||
if (selfAccount != null) {
|
||||
adviceSaveDto.setAccountId(selfAccount.getId());
|
||||
log.info("handDevice - 使用现有自费账户,accountId={}", selfAccount.getId());
|
||||
} else {
|
||||
// 自动创建自费账户
|
||||
Account newAccount = new Account();
|
||||
@@ -1370,9 +1460,10 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
newAccount.setName(AccountType.PERSONAL_CASH_ACCOUNT.getInfo());
|
||||
Long newAccountId = iAccountService.saveAccountByRegister(newAccount);
|
||||
adviceSaveDto.setAccountId(newAccountId);
|
||||
log.info("handDevice - 自动创建自费账户,newAccountId={}", newAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 🔧 Bug Fix: 确保practitionerId不为null
|
||||
if (adviceSaveDto.getPractitionerId() == null) {
|
||||
adviceSaveDto.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
|
||||
@@ -1607,12 +1698,26 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
|
||||
for (AdviceSaveDto adviceSaveDto : insertOrUpdateList) {
|
||||
// 🔧 Bug Fix: 确保accountId不为null
|
||||
// 🔧 Bug Fix: 确保accountId有效(不为null且账户存在)
|
||||
boolean needNewAccount = false;
|
||||
if (adviceSaveDto.getAccountId() == null) {
|
||||
needNewAccount = true;
|
||||
} else {
|
||||
// 验证账户是否存在且有效(未被删除,租户匹配)
|
||||
Account existingAccount = iAccountService.getById(adviceSaveDto.getAccountId());
|
||||
if (existingAccount == null) {
|
||||
log.warn("handService - 前端传入的accountId无效(账户不存在),accountId={},将重新获取或创建账户",
|
||||
adviceSaveDto.getAccountId());
|
||||
needNewAccount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needNewAccount) {
|
||||
// 尝试从患者就诊中获取默认账户ID(自费账户)
|
||||
Account selfAccount = iAccountService.getSelfAccount(adviceSaveDto.getEncounterId());
|
||||
if (selfAccount != null) {
|
||||
adviceSaveDto.setAccountId(selfAccount.getId());
|
||||
log.info("handService - 使用现有自费账户,accountId={}", selfAccount.getId());
|
||||
} else {
|
||||
// 自动创建自费账户
|
||||
Account newAccount = new Account();
|
||||
@@ -1626,9 +1731,10 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
newAccount.setName(AccountType.PERSONAL_CASH_ACCOUNT.getInfo());
|
||||
Long newAccountId = iAccountService.saveAccountByRegister(newAccount);
|
||||
adviceSaveDto.setAccountId(newAccountId);
|
||||
log.info("handService - 自动创建自费账户,newAccountId={}", newAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 🔧 Bug Fix: 确保practitionerId不为null
|
||||
if (adviceSaveDto.getPractitionerId() == null) {
|
||||
adviceSaveDto.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
|
||||
@@ -1677,8 +1783,14 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
// 普通诊疗医嘱
|
||||
serviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum());
|
||||
}
|
||||
|
||||
serviceRequest.setActivityId(adviceSaveDto.getAdviceDefinitionId());// 诊疗定义id
|
||||
|
||||
// 🔧 BugFix#385: 检查类型(adviceType=2)不走定价体系,activityId设置为0L占位
|
||||
// 与ExamApplyController保持一致,数据库有NOT NULL约束
|
||||
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == 2) {
|
||||
serviceRequest.setActivityId(0L);
|
||||
} else {
|
||||
serviceRequest.setActivityId(adviceSaveDto.getAdviceDefinitionId());// 诊疗定义id
|
||||
}
|
||||
serviceRequest.setPatientId(adviceSaveDto.getPatientId()); // 患者
|
||||
serviceRequest.setRequesterId(adviceSaveDto.getPractitionerId()); // 开方医生
|
||||
serviceRequest.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
|
||||
@@ -41,7 +41,9 @@ import com.openhis.web.paymentmanage.appservice.IChargeBillService;
|
||||
import com.openhis.web.paymentmanage.dto.*;
|
||||
import com.openhis.web.paymentmanage.mapper.ChargeBillMapper;
|
||||
import com.openhis.workflow.domain.ActivityDefinition;
|
||||
import com.openhis.workflow.domain.ServiceRequest;
|
||||
import com.openhis.workflow.service.IActivityDefinitionService;
|
||||
import com.openhis.workflow.service.IServiceRequestService;
|
||||
import com.openhis.yb.domain.ClinicSettle;
|
||||
import com.openhis.yb.domain.ClinicUnSettle;
|
||||
import com.openhis.yb.domain.InfoPerson;
|
||||
@@ -111,6 +113,8 @@ public class IChargeBillServiceImpl implements IChargeBillService {
|
||||
@Autowired
|
||||
private IActivityDefinitionService iActivityDefinitionService;
|
||||
@Autowired
|
||||
private IServiceRequestService iServiceRequestService;
|
||||
@Autowired
|
||||
private IPractitionerService iPractitionerService;
|
||||
@Autowired
|
||||
private IHealthcareServiceService iHealthcareServiceService;
|
||||
@@ -265,10 +269,31 @@ public class IChargeBillServiceImpl implements IChargeBillService {
|
||||
.setTotalPrice(chargeItem.getTotalPrice()).setQuantityUnit(chargeItem.getQuantityUnit())
|
||||
.setTotalVolume(device.getSize()).setQuantityValue(chargeItem.getQuantityValue());
|
||||
} else if (CommonConstants.TableName.WOR_ACTIVITY_DEFINITION.equals(chargeItem.getProductTable())) {
|
||||
ActivityDefinition activity = iActivityDefinitionService.getById(chargeItem.getProductId());
|
||||
chargeItemDetailVO.setDirClass(activity.getChrgitmLv() + "").setChargeItemName(activity.getName())
|
||||
.setTotalPrice(chargeItem.getTotalPrice()).setQuantityUnit(chargeItem.getQuantityUnit())
|
||||
.setTotalVolume("").setQuantityValue(chargeItem.getQuantityValue());
|
||||
// 🔧 BugFix#385: 检查申请创建的收费项 productId=0,需从 ServiceRequest.contentJson 获取项目名称
|
||||
if (chargeItem.getProductId() != null && chargeItem.getProductId() > 0) {
|
||||
ActivityDefinition activity = iActivityDefinitionService.getById(chargeItem.getProductId());
|
||||
chargeItemDetailVO.setDirClass(activity.getChrgitmLv() + "").setChargeItemName(activity.getName())
|
||||
.setTotalPrice(chargeItem.getTotalPrice()).setQuantityUnit(chargeItem.getQuantityUnit())
|
||||
.setTotalVolume("").setQuantityValue(chargeItem.getQuantityValue());
|
||||
} else {
|
||||
// productId=0 时,从关联的 ServiceRequest 获取项目名称
|
||||
ServiceRequest serviceRequest = iServiceRequestService.getById(chargeItem.getServiceId());
|
||||
String itemName = "未知项目";
|
||||
String dirClass = "3"; // 默认诊疗类
|
||||
if (serviceRequest != null && serviceRequest.getContentJson() != null) {
|
||||
try {
|
||||
JSONObject json = JSON.parseObject(serviceRequest.getContentJson());
|
||||
if (json.containsKey("adviceName")) {
|
||||
itemName = json.getString("adviceName");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析ServiceRequest.contentJson失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
chargeItemDetailVO.setDirClass(dirClass).setChargeItemName(itemName)
|
||||
.setTotalPrice(chargeItem.getTotalPrice()).setQuantityUnit(chargeItem.getQuantityUnit())
|
||||
.setTotalVolume("").setQuantityValue(chargeItem.getQuantityValue());
|
||||
}
|
||||
} else {
|
||||
HealthcareService healthcareService = iHealthcareServiceService.getById(chargeItem.getServiceId());
|
||||
chargeItemDetailVO.setDirClass("3").setChargeItemName(healthcareService.getName())
|
||||
@@ -347,7 +372,19 @@ public class IChargeBillServiceImpl implements IChargeBillService {
|
||||
|
||||
Long definitionId = chargeItem.getDefinitionId();
|
||||
|
||||
ChargeItemDefinition chargeItemDefinition = iChargeItemDefinitionService.getById(definitionId);
|
||||
// 🔧 BugFix#385: 检查申请创建的收费项 definition_id=0,chargeItemDefinition 会为 null
|
||||
ChargeItemDefinition chargeItemDefinition = null;
|
||||
if (definitionId != null && definitionId > 0) {
|
||||
chargeItemDefinition = iChargeItemDefinitionService.getById(definitionId);
|
||||
}
|
||||
|
||||
// 当 definitionId=0 或 chargeItemDefinition 为 null 时,跳过医保分类统计
|
||||
// 检查类项目默认归类为"检查费"
|
||||
if (chargeItemDefinition == null) {
|
||||
// 检查申请的收费项,归类为检查费(03)
|
||||
sum03 = sum03.add(chargeItem.getTotalPrice());
|
||||
continue;
|
||||
}
|
||||
|
||||
YbMedChrgItmType medChrgItmType
|
||||
= YbMedChrgItmType.getByCode(Integer.parseInt(chargeItemDefinition.getYbType()));
|
||||
|
||||
@@ -6,6 +6,7 @@ package com.openhis.web.paymentmanage.appservice.impl;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
@@ -241,14 +242,21 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
||||
.collect(Collectors.toList());
|
||||
// account去重
|
||||
List<Long> distinctAccountIdList = accountIdList.stream().distinct().collect(Collectors.toList());
|
||||
// 检查是否存在accountId为null的收费项
|
||||
// 检查是否存在accountId为null或0的收费项
|
||||
long nullAccountIdCount = chargeItemList.stream()
|
||||
.map(ChargeItem::getAccountId)
|
||||
.filter(Objects::isNull)
|
||||
.count();
|
||||
long zeroAccountIdCount = chargeItemList.stream()
|
||||
.map(ChargeItem::getAccountId)
|
||||
.filter(id -> id != null && id == 0L)
|
||||
.count();
|
||||
if (nullAccountIdCount > 0) {
|
||||
throw new ServiceException("部分收费项缺少账户信息,请检查收费项数据");
|
||||
}
|
||||
if (zeroAccountIdCount > 0) {
|
||||
throw new ServiceException("部分收费项账户ID为0(无效),请检查收费项数据或重新创建检查申请");
|
||||
}
|
||||
if (distinctAccountIdList.isEmpty()) {
|
||||
throw new ServiceException("未找到有效的账户信息");
|
||||
}
|
||||
@@ -257,15 +265,66 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
||||
// 在挂号费等场景下可能因数据不一致导致查不到,引发误报
|
||||
List<Account> accountList = iAccountService.list(new LambdaQueryWrapper<Account>()
|
||||
.in(Account::getId, distinctAccountIdList));
|
||||
|
||||
// 🔧 Bug Fix: 处理账户不存在的情况(历史数据修复)
|
||||
if (accountList.size() != distinctAccountIdList.size()) {
|
||||
// 部分账户查不到时,记录警告日志,并校验是否每个收费项都能找到对应账户
|
||||
Set<Long> foundAccountIds = accountList.stream().map(Account::getId).collect(Collectors.toSet());
|
||||
List<Long> missingAccountIds = distinctAccountIdList.stream()
|
||||
.filter(id -> !foundAccountIds.contains(id)).collect(Collectors.toList());
|
||||
if (accountList.isEmpty()) {
|
||||
throw new ServiceException("未查询到任何账户信息,encounterId:" + prePaymentDto.getEncounterId()
|
||||
+ ",期望accountId列表:" + distinctAccountIdList);
|
||||
|
||||
logger.warn("预结算发现部分账户不存在,missingAccountIds={},将自动修复收费项的accountId",
|
||||
missingAccountIds);
|
||||
|
||||
// 获取或创建有效的自费账户
|
||||
Account selfAccount = iAccountService.getSelfAccount(prePaymentDto.getEncounterId());
|
||||
if (selfAccount == null) {
|
||||
// 自动创建自费账户
|
||||
Account newAccount = new Account();
|
||||
newAccount.setPatientId(chargeItemList.get(0).getPatientId());
|
||||
newAccount.setEncounterId(prePaymentDto.getEncounterId());
|
||||
newAccount.setContractNo(CommonConstants.BusinessName.DEFAULT_CONTRACT_NO);
|
||||
newAccount.setTypeCode(AccountType.PERSONAL_CASH_ACCOUNT.getCode());
|
||||
newAccount.setBalanceAmount(BigDecimal.ZERO);
|
||||
newAccount.setStatusEnum(AccountStatus.ACTIVE.getValue());
|
||||
newAccount.setEncounterFlag(Whether.YES.getValue());
|
||||
newAccount.setName(AccountType.PERSONAL_CASH_ACCOUNT.getInfo());
|
||||
Long newAccountId = iAccountService.saveAccountByRegister(newAccount);
|
||||
selfAccount = iAccountService.getById(newAccountId);
|
||||
logger.info("预结算自动创建自费账户,newAccountId={}", newAccountId);
|
||||
}
|
||||
|
||||
// 修复收费项的 accountId
|
||||
for (Long missingAccountId : missingAccountIds) {
|
||||
// 找到使用该无效 accountId 的收费项
|
||||
List<ChargeItem> affectedChargeItems = chargeItemList.stream()
|
||||
.filter(ci -> ci.getAccountId() != null && ci.getAccountId().equals(missingAccountId))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ChargeItem ci : affectedChargeItems) {
|
||||
LambdaUpdateWrapper<ChargeItem> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(ChargeItem::getId, ci.getId())
|
||||
.set(ChargeItem::getAccountId, selfAccount.getId());
|
||||
iChargeItemService.update(updateWrapper);
|
||||
logger.info("预结算修复收费项accountId,chargeItemId={},oldAccountId={},newAccountId={}",
|
||||
ci.getId(), missingAccountId, selfAccount.getId());
|
||||
|
||||
// 更新本地对象的 accountId,以便后续处理使用正确的值
|
||||
ci.setAccountId(selfAccount.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 重新查询账户列表
|
||||
accountList = iAccountService.list(new LambdaQueryWrapper<Account>()
|
||||
.eq(Account::getId, selfAccount.getId()));
|
||||
|
||||
// 重新构建 accountIdList(已修复)
|
||||
distinctAccountIdList = chargeItemList.stream()
|
||||
.map(ChargeItem::getAccountId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
logger.info("预结算账户修复完成,最终使用accountId={}", selfAccount.getId());
|
||||
}
|
||||
|
||||
// 账户id,对应的账单列表
|
||||
|
||||
@@ -94,18 +94,21 @@
|
||||
T8.contract_name,
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.surgery_name
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
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,
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN NULL
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
WHEN T1.context_enum = #{activity} THEN T2.yb_no
|
||||
WHEN T1.context_enum = #{medication} THEN T3.yb_no
|
||||
WHEN T1.context_enum = #{device} THEN T4.yb_no
|
||||
END AS yb_no,
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.id
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN 0
|
||||
WHEN T1.context_enum = #{activity} THEN T2.id
|
||||
WHEN T1.context_enum = #{medication} THEN T3.id
|
||||
WHEN T1.context_enum = #{device} THEN T4.id
|
||||
@@ -205,18 +208,21 @@
|
||||
T8.contract_name,
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.surgery_name
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
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,
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN NULL
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN NULL
|
||||
WHEN T1.context_enum = #{activity} THEN T2.yb_no
|
||||
WHEN T1.context_enum = #{medication} THEN T3.yb_no
|
||||
WHEN T1.context_enum = #{device} THEN T4.yb_no
|
||||
END AS yb_no,
|
||||
CASE
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_table = 'cli_surgery' THEN T9.id
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN 0
|
||||
WHEN T1.context_enum = #{activity} THEN T2.id
|
||||
WHEN T1.context_enum = #{medication} THEN T3.id
|
||||
WHEN T1.context_enum = #{device} THEN T4.id
|
||||
|
||||
@@ -630,6 +630,8 @@
|
||||
T3.service_table = #{WOR_DEVICE_REQUEST}
|
||||
LEFT JOIN adm_location AS al ON al.ID = T1.perform_location AND al.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0' AND T1.generate_source_enum = #{generateSourceEnum}
|
||||
-- 🔧 Bug Fix: 排除基于其他医嘱生成的执行记录
|
||||
AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL)
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
@@ -686,6 +688,10 @@
|
||||
WHERE T1.delete_flag = '0' AND T1.generate_source_enum = #{generateSourceEnum}
|
||||
AND T1.parent_id IS NULL
|
||||
AND T1.refund_service_id IS NULL
|
||||
-- 🔧 Bug Fix: 排除基于药品请求生成的执行记录(输液、皮试),但保留检查/检验申请单创建的原始医嘱
|
||||
-- based_on_table='med_medication_request' → 输液/皮试执行记录,应排除
|
||||
-- based_on_table='exam_apply'/'lab_apply' → 申请单原始医嘱,应保留
|
||||
AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL OR T1.based_on_table NOT IN ('med_medication_request', 'med_medication_dispense'))
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.openhis.workflow.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
@@ -64,6 +66,7 @@ public class ServiceRequest extends HisBaseEntity {
|
||||
private Integer performFlag;
|
||||
|
||||
/** 诊疗定义id */
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS)
|
||||
private Long activityId;
|
||||
|
||||
/** 数量 */
|
||||
|
||||
Reference in New Issue
Block a user