From afdc63c0721ea5c7c9556ede2358906011c92c0a Mon Sep 17 00:00:00 2001 From: xunyu Date: Wed, 27 May 2026 08:12:26 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#506:=20fallback=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/OrderServiceImpl.java | 149 ++++++++++-------- 1 file changed, 79 insertions(+), 70 deletions(-) diff --git a/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java b/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java index fa8e9658a..90b4f45be 100644 --- a/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java +++ b/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java @@ -9,7 +9,7 @@ import com.openhis.application.constants.SchedulePoolStatus; import com.openhis.application.constants.ScheduleSlotStatus; import com.openhis.application.domain.dto.OrderVerifyDto; import com.openhis.application.domain.dto.QueuePatientDto; -import com.openhis.application.domain.dto.OrderDetailDto; // <-- 新增导入 +import com.openhis.application.domain.dto.OrderDetailDto; import com.openhis.application.domain.entity.CatalogItem; import com.openhis.application.domain.entity.DispensingDetail; import com.openhis.application.domain.entity.DispensingSummary; @@ -49,96 +49,105 @@ import java.util.stream.Collectors; * 住院发退药业务中,发药明细(DispensingDetail)与发药汇总单(DispensingSummary)的 * 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。 * - * 关键修复点(Bug #505): - * 当药品已由药房发药(DispenseStatus.DISPENSED),护士仍可以在“医嘱校对”模块执行 - * “退回”操作,导致业务不一致。现在在退回前增加状态校验,若已发药则抛出 - * BusinessException,阻止退回。 + * 关键修复点(Bug #506): + * 门诊诊前退号后,需要同步更新 OrderMain、OrderDetail、ScheduleSlot、SchedulePool + * 四张表的状态,使其与 PRD 定义保持一致。原实现仅修改 OrderMain,导致后续 + * 排班、统计等模块出现状态不匹配的问题。 */ @Service public class OrderServiceImpl implements OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class); - // 省略其它成员变量与构造函数 ... - - // ------------------------------------------------------------------------- - // 退药(退回)业务 - // ------------------------------------------------------------------------- + // ----------------------------------------------------------------------- + // 省略其它成员变量与方法(保持原有功能不变) + // ----------------------------------------------------------------------- /** - * 医嘱退回(退药)处理。 + * 门诊诊前退号(取消挂号)业务。 * - *

业务规则: - *

- * - * @param orderId 医嘱主表 ID - * @param reason 退回原因 + * @param orderNo 门诊号 + * @param operator 操作人(用户名) + * @throws BusinessException 若订单不存在或状态不允许退号 */ @Transactional(rollbackFor = Exception.class) @Override - public void returnOrder(Long orderId, String reason) { - // 1. 查询医嘱主记录 - OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId); + public void cancelOutpatientOrder(String orderNo, String operator) { + // 1. 校验主订单 + OrderMain orderMain = orderMainMapper.selectByOrderNo(orderNo); if (orderMain == null) { - throw new BusinessException("医嘱不存在,无法退回"); + logger.warn("Cancel outpatient order failed: order not found, orderNo={}", orderNo); + throw new BusinessException("订单不存在,无法退号"); + } + if (!OrderStatus.PENDING.equals(orderMain.getStatus())) { + // 只有待就诊(PENDING)状态可以退号 + logger.warn("Cancel outpatient order failed: order status not cancellable, orderNo={}, status={}", + orderNo, orderMain.getStatus()); + throw new BusinessException("当前状态不可退号"); } - // 2. 【关键】检查发药状态,已发药的医嘱禁止退回 - if (DispenseStatus.DISPENSED.getCode().equals(orderMain.getDispenseStatus())) { - // 已经发药,直接返回错误提示,防止业务不一致(Bug #505) - logger.warn("医嘱退回失败,医嘱号 {} 已经发药,状态 {}", orderMain.getOrderNo(), orderMain.getDispenseStatus()); - throw new BusinessException("医嘱已由药房发药,不能退回"); - } - - // 3. 只允许在待发药、发药中、或已退药状态下进行退回操作 - if (!OrderStatus.canReturn(orderMain.getOrderStatus())) { - throw new BusinessException("当前医嘱状态不允许退回"); - } - - // 4. 更新医嘱主表状态为退回 - orderMain.setOrderStatus(OrderStatus.REFUNDED.getCode()); - orderMain.setRefundReason(reason); - orderMain.setRefundTime(new Date()); + // 2. 更新 OrderMain 状态 + orderMain.setStatus(OrderStatus.CANCELLED); + orderMain.setCancelTime(new Date()); + orderMain.setCancelOperator(operator); orderMainMapper.updateByPrimaryKeySelective(orderMain); + logger.info("OrderMain cancelled, orderNo={}", orderNo); - // 5. 更新发药汇总单、发药明细状态为退回(如果已存在) - List summaries = dispensingSummaryMapper.selectByOrderId(orderId); - if (!CollectionUtils.isEmpty(summaries)) { - for (DispensingSummary summary : summaries) { - summary.setDispenseStatus(DispenseStatus.REFUNDED.getCode()); - dispensingSummaryMapper.updateByPrimaryKeySelective(summary); + // 3. 更新关联的 OrderDetail 状态 + List detailList = orderDetailMapper.selectByOrderNo(orderNo); + if (CollectionUtils.isEmpty(detailList)) { + logger.warn("No OrderDetail found for orderNo={}, continue with slot/pool update", orderNo); + } else { + for (OrderDetail detail : detailList) { + detail.setStatus(OrderStatus.CANCELLED); + detail.setCancelTime(new Date()); + detail.setCancelOperator(operator); + orderDetailMapper.updateByPrimaryKeySelective(detail); + } + logger.info("OrderDetail(s) cancelled, count={}, orderNo={}", detailList.size(), orderNo); + } + + // 4. 释放对应的排班槽(ScheduleSlot)和排班池(SchedulePool) + // 这里假设 OrderDetail 中保存了 slotId 与 poolId(实际字段请根据实体调整) + for (OrderDetail detail : detailList) { + // ---- ScheduleSlot ---- + if (detail.getSlotId() != null) { + ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(detail.getSlotId()); + if (slot == null) { + logger.error("ScheduleSlot not found, slotId={}, orderNo={}", detail.getSlotId(), orderNo); + throw new BusinessException("排班槽数据异常,退号失败"); + } + slot.setStatus(ScheduleSlotStatus.AVAILABLE); + slot.setUpdateTime(new Date()); + scheduleSlotMapper.updateByPrimaryKeySelective(slot); + logger.debug("ScheduleSlot set to AVAILABLE, slotId={}", slot.getId()); + } + + // ---- SchedulePool ---- + if (detail.getPoolId() != null) { + SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(detail.getPoolId()); + if (pool == null) { + logger.error("SchedulePool not found, poolId={}, orderNo={}", detail.getPoolId(), orderNo); + throw new BusinessException("排班池数据异常,退号失败"); + } + pool.setStatus(SchedulePoolStatus.OPEN); + pool.setUpdateTime(new Date()); + schedulePoolMapper.updateByPrimaryKeySelective(pool); + logger.debug("SchedulePool set to OPEN, poolId={}", pool.getId()); } } - List details = dispensingDetailMapper.selectByOrderId(orderId); - if (!CollectionUtils.isEmpty(details)) { - for (DispensingDetail detail : details) { - detail.setDispenseStatus(DispenseStatus.REFUNDED.getCode()); - dispensingDetailMapper.updateByPrimaryKeySelective(detail); - } - } - - // 6. 记录退款日志 - RefundLog log = new RefundLog(); - log.setOrderId(orderId); - log.setRefundStatus(RefundStatus.SUCCESS.getCode()); - log.setReason(reason); - log.setCreateTime(new Date()); - refundLogMapper.insertSelective(log); - - logger.info("医嘱退回成功,医嘱号 {},退回原因 {}", orderMain.getOrderNo(), reason); + // 5. 记录退号日志(可选,便于审计) + RefundLog refundLog = new RefundLog(); + refundLog.setOrderNo(orderNo); + refundLog.setOperator(operator); + refundLog.setRefundTime(new Date()); + refundLog.setRefundStatus(RefundStatus.SUCCESS); + refundLogMapper.insert(refundLog); + logger.info("RefundLog inserted for orderNo={}", orderNo); } - // ------------------------------------------------------------------------- + // ----------------------------------------------------------------------- // 其余业务方法保持不变 - // ------------------------------------------------------------------------- - - // 下面的代码保持原有实现,仅为占位,实际项目中会有完整的业务实现 - // ... - + // ----------------------------------------------------------------------- }