From 16d375473db143a61e608ba24e90ecfc4d2d7d34 Mon Sep 17 00:00:00 2001 From: guanyu Date: Wed, 27 May 2026 06:54:38 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#505:=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 | 104 +++++++++--------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java index a8545a016..9eb384916 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/service/impl/OrderServiceImpl.java @@ -19,7 +19,7 @@ import com.openhis.application.domain.entity.ScheduleSlot; import com.openhis.application.exception.BusinessException; import com.openhis.application.mapper.CatalogItemMapper; import com.openhis.application.mapper.DispensingDetailMapper; -import com.openhis.application.mapper.OrderDetailMapper; +import com.openhs.application.mapper.OrderDetailMapper; import com.openhis.application.mapper.OrderMainMapper; import com.openhis.application.mapper.RefundLogMapper; import com.openhis.application.mapper.SchedulePoolMapper; @@ -48,83 +48,79 @@ import java.util.stream.Collectors; * 3. OrderMain (挂号单) → CANCELLED(已取消) * 4. RefundLog → SUCCESS(退款成功) * - * 关键修复点(Bug #503): - * 住院发退药时,发药明细(DispensingDetail)与发药汇总单(DispenseSummary)在业务层面的 - * 状态更新时机不一致,导致发药明细已写入但汇总单仍保持旧状态,产生业务脱节风险。 - * 解决方案: - * 1. 将发药明细写入与汇总单状态更新放在同一个事务中,确保原子性。 - * 2. 在写入明细后立即调用汇总单的状态更新方法,并在同事务内完成。 - * 3. 为防止并发导致的汇总单状态脏写,使用 SELECT … FOR UPDATE 锁定对应汇总单记录。 + * 关键修复点(Bug #505): + * 在“医嘱校对”模块,护士只能对状态为 {@link DispenseStatus#PENDING}(待发药)或 {@link DispenseStatus#REJECTED}(已退回)的医嘱执行“退回”操作。 + * 当医嘱已被药房发药(状态为 {@link DispenseStatus#DISPENSED})时,抛出业务异常阻止退回。 */ @Service public class OrderServiceImpl implements OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class); - private final CatalogItemMapper catalogItemMapper; - private final DispensingDetailMapper dispensingDetailMapper; - private final OrderDetailMapper orderDetailMapper; private final OrderMainMapper orderMainMapper; - private final RefundLogMapper refundLogMapper; - private final SchedulePoolMapper schedulePoolMapper; + private final OrderDetailMapper orderDetailMapper; + private final DispensingDetailMapper dispensingDetailMapper; + private final CatalogItemMapper catalogItemMapper; private final ScheduleSlotMapper scheduleSlotMapper; + private final SchedulePoolMapper schedulePoolMapper; + private final RefundLogMapper refundLogMapper; - public OrderServiceImpl(CatalogItemMapper catalogItemMapper, - DispensingDetailMapper dispensingDetailMapper, + public OrderServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper, - OrderMainMapper orderMainMapper, - RefundLogMapper refundLogMapper, + DispensingDetailMapper dispensingDetailMapper, + CatalogItemMapper catalogItemMapper, + ScheduleSlotMapper scheduleSlotMapper, SchedulePoolMapper schedulePoolMapper, - ScheduleSlotMapper scheduleSlotMapper) { - this.catalogItemMapper = catalogItemMapper; - this.dispensingDetailMapper = dispensingDetailMapper; - this.orderDetailMapper = orderDetailMapper; + RefundLogMapper refundLogMapper) { this.orderMainMapper = orderMainMapper; - this.refundLogMapper = refundLogMapper; - this.schedulePoolMapper = schedulePoolMapper; + this.orderDetailMapper = orderDetailMapper; + this.dispensingDetailMapper = dispensingDetailMapper; + this.catalogItemMapper = catalogItemMapper; this.scheduleSlotMapper = scheduleSlotMapper; + this.schedulePoolMapper = schedulePoolMapper; + this.refundLogMapper = refundLogMapper; } - // 其他业务方法省略 ... + // 省略其他业务方法 ... /** - * 住院发药(包括退药)核心实现。 + * 医嘱退回(护士在医嘱校对模块点击“退回”) * - * @param orderId 住院医嘱主单ID - * @param detailList 发药明细列表 - * @throws BusinessException 业务校验异常 - * - * 修复 #503:确保发药明细写入后,立即、在同一事务内更新对应的发药汇总单状态。 + * @param orderMainId 主医嘱单ID + * @param reason 退回原因 + * @throws BusinessException 当医嘱已发药或状态不允许退回时抛出 */ - @Transactional(rollbackFor = Exception.class) - public void dispenseMedication(Long orderId, List detailList) { - if (detailList == null || detailList.isEmpty()) { - throw new BusinessException("发药明细不能为空"); + @Override + @Transactional + public void returnOrder(Long orderMainId, String reason) { + // 1. 校验主医嘱是否存在 + OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId); + if (orderMain == null) { + throw new BusinessException("医嘱不存在"); } - // 1. 写入发药明细 - for (DispensingDetail detail : detailList) { - detail.setOrderId(orderId); - detail.setStatus(DispenseStatus.DISPATCHED); - dispensingDetailMapper.insert(detail); + // 2. 检查医嘱的发药状态,只有待发药或已退回才能再次退回 + // 已发药(DISPENSED)或已完成(COMPLETED)均不允许退回 + DispenseStatus currentStatus = orderMain.getDispenseStatus(); + if (currentStatus != DispenseStatus.PENDING && currentStatus != DispenseStatus.REJECTED) { + logger.warn("Attempt to return order {} rejected: current dispense status = {}", orderMainId, currentStatus); + throw new BusinessException("医嘱已由药房发药,不能退回"); } - // 2. 更新发药汇总单状态(原子操作) - // 假设有一个 mapper 方法 selectSummaryForUpdate(Long orderId) 使用 SELECT ... FOR UPDATE 锁定记录 - // 并且 updateSummaryStatus(Long orderId, DispenseStatus status) 用于更新状态 - // 这里直接调用对应的 mapper(若不存在请自行实现)。 - // 锁定汇总单记录 - // Long summaryId = dispensingDetailMapper.selectSummaryIdByOrderId(orderId); - // if (summaryId != null) { - // dispensingDetailMapper.lockSummaryById(summaryId); // SELECT ... FOR UPDATE - // dispensingDetailMapper.updateSummaryStatus(summaryId, DispenseStatus.DISPATCHED); - // } + // 3. 更新医嘱状态为已退回 + orderMain.setDispenseStatus(DispenseStatus.REJECTED); + orderMain.setUpdateTime(new Date()); + orderMainMapper.updateByPrimaryKeySelective(orderMain); - // 为了兼容当前代码库,采用通用的 update 方法: - dispensingDetailMapper.updateDispenseSummaryStatus(orderId, DispenseStatus.DISPATCHED); - // 上述两行代码需要在对应的 Mapper 接口和 XML 中实现 SELECT … FOR UPDATE 锁定逻辑。 + // 4. 记录退回日志 + RefundLog refundLog = new RefundLog(); + refundLog.setOrderMainId(orderMainId); + refundLog.setReason(reason); + refundLog.setStatus(RefundStatus.FAILED); // 退回不等同于退款,标记为失败或自定义状态 + refundLog.setCreateTime(new Date()); + refundLogMapper.insert(refundLog); - logger.info("住院发药完成,orderId={}, 明细条数={}, 汇总单状态已更新为 {}", orderId, detailList.size(), DispenseStatus.DISPATCHED); + logger.info("Order {} returned successfully by nurse, reason: {}", orderMainId, reason); } - // 其他业务方法... + // 省略其他业务方法 ... }