diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java index 3db1ca2b..8be9cc0c 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientRegistrationAppServiceImpl.java @@ -22,6 +22,8 @@ import com.openhis.common.enums.ybenums.YbPayment; import com.openhis.common.utils.EnumUtils; import com.openhis.common.utils.HisPageUtils; import com.openhis.common.utils.HisQueryUtils; +import com.openhis.appointmentmanage.domain.SchedulePool; +import com.openhis.appointmentmanage.domain.ScheduleSlot; import com.openhis.appointmentmanage.mapper.SchedulePoolMapper; import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper; import com.openhis.clinical.domain.Order; @@ -52,6 +54,7 @@ import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; @@ -111,6 +114,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra @Resource SchedulePoolMapper schedulePoolMapper; + @Resource + com.openhis.document.service.IEmrService iEmrService; + /** * 门诊挂号 - 查询患者信息 * @@ -256,14 +262,24 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra * @return 结果 */ @Override + @Transactional(rollbackFor = Exception.class) public R returnRegister(CancelRegPaymentDto cancelRegPaymentDto) { Encounter byId = iEncounterService.getById(cancelRegPaymentDto.getEncounterId()); + if (byId == null) { + return R.fail(null, "就诊记录不存在"); + } if (EncounterStatus.CANCELLED.getValue().equals(byId.getStatusEnum())) { return R.fail(null, "该患者已经退号,请勿重复退号"); } // 只有待诊状态才能退号 if (!EncounterStatus.PLANNED.getValue().equals(byId.getStatusEnum())) { - return R.fail(null, "该患者医生已接诊,不能退号!"); + return R.fail(null, "该患者已开始就诊,不能退号!"); + } + + // 诊前退号检查:病历、费用明细、班段时间 + R checkResult = checkPreConsultationRefund(byId); + if (checkResult != null) { + return checkResult; } iEncounterService.returnRegister(cancelRegPaymentDto.getEncounterId()); // 查询账户信息 @@ -317,6 +333,149 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra return R.ok(paymentRecon, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"退号"})); } + /** + * 诊前退号检查 + * 检查项:病历记录、费用明细、当日就诊、班段结束时间 + * + * @param encounter 就诊记录 + * @return null 表示通过检查,否则返回失败原因 + */ + private R checkPreConsultationRefund(Encounter encounter) { + Long encounterId = encounter.getId(); + + // 当日时间范围:今天 00:00:00 到 明天 00:00:00 + LocalDate today = LocalDate.now(); + LocalDateTime todayStart = today.atStartOfDay(); + LocalDateTime tomorrowStart = today.plusDays(1).atStartOfDay(); + Date todayStartDate = Date.from(todayStart.atZone(ZoneId.systemDefault()).toInstant()); + Date tomorrowStartDate = Date.from(tomorrowStart.atZone(ZoneId.systemDefault()).toInstant()); + + // 1. 检查是否有当日病历记录(医生已写病历则不能退号) + // 只检查当天的病历,避免误判历史数据 + // 条件:(recordTime在当天范围内) OR (recordTime为空 AND createTime在当天范围内) + long emrCount = iEmrService.count(new LambdaQueryWrapper() + .eq(com.openhis.document.domain.Emr::getEncounterId, encounterId) + .and(wrapper -> wrapper + .and(w -> w + .ge(com.openhis.document.domain.Emr::getRecordTime, todayStartDate) + .lt(com.openhis.document.domain.Emr::getRecordTime, tomorrowStartDate) + ) + .or() + .and(w -> w + .isNull(com.openhis.document.domain.Emr::getRecordTime) + .ge(com.openhis.document.domain.Emr::getCreateTime, todayStartDate) + .lt(com.openhis.document.domain.Emr::getCreateTime, tomorrowStartDate) + ) + )); + if (emrCount > 0) { + return R.fail(null, "该患者已有病历记录,不能退号!"); + } + + // 2. 检查是否有当日费用明细(除挂号费外的其他费用) + // 只检查当天的费用明细,避免误判历史数据 + // 条件:(occurrenceTime在当天范围内) OR (occurrenceTime为空 AND createTime在当天范围内) + long chargeItemCount = iChargeItemService.count(new LambdaQueryWrapper() + .eq(ChargeItem::getEncounterId, encounterId) + .ne(ChargeItem::getContextEnum, ChargeItemContext.REGISTER.getValue()) + .ne(ChargeItem::getStatusEnum, ChargeItemStatus.REFUNDED.getValue()) + .and(wrapper -> wrapper + .and(w -> w + .ge(ChargeItem::getOccurrenceTime, todayStartDate) + .lt(ChargeItem::getOccurrenceTime, tomorrowStartDate) + ) + .or() + .and(w -> w + .isNull(ChargeItem::getOccurrenceTime) + .ge(ChargeItem::getCreateTime, todayStartDate) + .lt(ChargeItem::getCreateTime, tomorrowStartDate) + ) + )); + if (chargeItemCount > 0) { + return R.fail(null, "该患者已产生诊疗费用,不能退号!"); + } + + // 3. 检查是否当日就诊(防止隔日财务封账) + if (encounter.getCreateTime() != null) { + LocalDate encounterDate = encounter.getCreateTime().toInstant() + .atZone(ZoneId.systemDefault()).toLocalDate(); + if (encounterDate.isBefore(today)) { + return R.fail(null, "非当日就诊记录,不能退号!"); + } + } + + // 4. 检查班段是否已结束(通过预约订单获取班段信息) + R shiftCheckResult = checkShiftEnded(encounter); + if (shiftCheckResult != null) { + return shiftCheckResult; + } + + return null; // 检查通过 + } + + /** + * 检查班段是否已结束 + * 截止时间 = 班段结束时间 + * + * @param encounter 就诊记录 + * @return null 表示通过检查,否则返回失败原因 + */ + private R checkShiftEnded(Encounter encounter) { + try { + // 通过患者、科室、日期查找关联的预约订单 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(Order::getPatientId, encounter.getPatientId()) + .in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED, + CommonConstants.AppointmentOrderStatus.CHECKED_IN) + .orderByDesc(Order::getUpdateTime) + .orderByDesc(Order::getCreateTime) + .last("LIMIT 1"); + + if (encounter.getOrganizationId() != null) { + queryWrapper.eq(Order::getDepartmentId, encounter.getOrganizationId()); + } + if (encounter.getTenantId() != null) { + queryWrapper.eq(Order::getTenantId, encounter.getTenantId()); + } + if (encounter.getCreateTime() != null) { + LocalDate encounterDate = encounter.getCreateTime().toInstant() + .atZone(ZoneId.systemDefault()).toLocalDate(); + Date startOfDay = Date.from(encounterDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + Date nextDayStart = Date.from(encounterDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); + queryWrapper.ge(Order::getAppointmentDate, startOfDay) + .lt(Order::getAppointmentDate, nextDayStart); + } + + Order appointmentOrder = orderService.getOne(queryWrapper, false); + if (appointmentOrder == null || appointmentOrder.getSlotId() == null) { + // 没有关联的预约订单,跳过班段检查(非预约挂号的场景) + return null; + } + + // 获取号源槽位 + ScheduleSlot slot = scheduleSlotMapper.selectById(appointmentOrder.getSlotId()); + if (slot == null || slot.getPoolId() == null) { + return null; + } + + // 获取号源池(班段信息) + SchedulePool pool = schedulePoolMapper.selectById(slot.getPoolId()); + if (pool == null || pool.getEndTime() == null) { + return null; + } + // 检查当前时间是否已过班段结束时间 + LocalTime now = LocalTime.now(); + if (now.isAfter(pool.getEndTime())) { + return R.fail(null, "当前班段已结束,不能退号!"); + } + + return null; + } catch (Exception e) { + log.warn("检查班段结束时间失败, encounterId={}", encounter.getId(), e); + // 异常情况下允许退号,避免阻断正常业务 + return null; + } + } + /** * 查询当日就诊数据 * diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml index 90fc30f7..cef460a4 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationLabApplyMapper.xml @@ -41,11 +41,18 @@ + 从明细表聚合项目名称和金额-->