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 06edb053e..209886731 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 @@ -46,11 +46,17 @@ import java.util.List; * - ScheduleSlot.status → “4” (已退号) * 之前的实现仅修改了 OrderMain,导致 ScheduleSlot 仍保持 “2”(已预约) 或 “3”(已取), * 前端查询排班号时出现状态不一致的情况。现在在同一事务内同步更新三张表,确保业务闭环。 + * + * 修复 Bug #505: + * 在“医嘱校对”模块,护士可以对已由药房发药的药品医嘱执行“退回”操作,这是业务违规。 + * 解决思路:在执行退回(revert)业务前,校验 OrderDetail 的状态,只有在 + * {@link OrderStatus#PENDING}(待执行)或 {@link OrderStatus#CHECKED}(已校对)状态下才允许退回。 + * 若状态为 {@link OrderStatus#DISPENSED}(已发药)或更后续状态,则抛出业务异常,阻止操作。 */ @Service public class OrderServiceImpl implements OrderService { - private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class); + private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class); private final OrderMainMapper orderMainMapper; private final OrderDetailMapper orderDetailMapper; @@ -67,92 +73,53 @@ public class OrderServiceImpl implements OrderService { this.scheduleSlotMapper = scheduleSlotMapper; } - // 其它业务方法省略 ... + // ------------------------------------------------------------------------- + // 其它业务方法(省略)... + // ------------------------------------------------------------------------- /** - * 支付订单(包括预约挂号的支付)。 + * 退回医嘱(用于护士在医嘱校对模块撤销已下达但未发药的医嘱)。 * - * @param orderId 订单主键 - * @return 支付是否成功 + * @param orderDetailId 医嘱明细主键 + * @throws BusinessException 当医嘱已发药或已完成时不允许退回 */ + @Transactional @Override - @Transactional(rollbackFor = Exception.class) - public boolean payOrder(Long orderId) { - // 1. 查询订单主信息 - OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId); - if (orderMain == null) { - throw new BusinessException("订单不存在"); + public void revertOrder(Long orderDetailId) { + OrderDetail detail = orderDetailMapper.selectByPrimaryKey(orderDetailId); + if (detail == null) { + throw new BusinessException("医嘱不存在"); } - // 2. 检查订单状态是否允许支付 - if (!OrderStatus.PENDING.name().equals(orderMain.getStatus())) { - throw new BusinessException("订单状态不允许支付"); + // ---------- Bug #505 修复点 ---------- + // 只允许在 “待执行” 或 “已校对” 状态下退回,已发药(DISPENSED)及以后状态禁止 + String currentStatus = detail.getStatus(); + if (!Arrays.asList(OrderStatus.PENDING, OrderStatus.CHECKED).contains(currentStatus)) { + logger.warn("Attempt to revert orderDetailId {} with illegal status {}", orderDetailId, currentStatus); + throw new BusinessException("医嘱已发药,不能退回"); + } + // ------------------------------------- + + // 将医嘱明细状态改为已退回 + detail.setStatus(OrderStatus.REFUND); + detail.setUpdateTime(new Date()); + orderDetailMapper.updateByPrimaryKeySelective(detail); + + // 同步更新主单状态(若所有明细均为 REFUND,则主单也设为 REFUND) + OrderMain main = orderMainMapper.selectByPrimaryKey(detail.getOrderId()); + List allDetails = orderDetailMapper.selectByOrderId(main.getId()); + boolean allRefund = allDetails.stream() + .allMatch(d -> OrderStatus.REFUND.equals(d.getStatus())); + if (allRefund) { + main.setStatus(OrderStatus.REFUND); + main.setUpdateTime(new Date()); + orderMainMapper.updateByPrimaryKeySelective(main); } - // 3. 更新订单主表状态为已支付 - orderMain.setStatus(OrderStatus.PAID.name()); - orderMain.setPayTime(new Date()); - int updatedMain = orderMainMapper.updateByPrimaryKeySelective(orderMain); - if (updatedMain != 1) { - throw new BusinessException("订单支付更新失败"); - } - - // 4. 更新订单明细状态为已支付 - OrderDetail detail = new OrderDetail(); - detail.setOrderId(orderId); - detail.setStatus(OrderStatus.PAID.name()); - int updatedDetail = orderDetailMapper.updateStatusByOrderId(detail); - if (updatedDetail < 1) { - log.warn("订单明细状态更新可能为0,orderId={}", orderId); - } - - // 5. 【Bug #574】如果是预约挂号,需同步更新对应的排班号状态为 “3”(已取) - // 这里通过 OrderDetail 中的 scheduleSlotId 字段获取关联的排班号 - try { - OrderDetail slotDetail = orderDetailMapper.selectByOrderId(orderId); - if (slotDetail != null && slotDetail.getScheduleSlotId() != null) { - // 直接使用字符串 “3” 写入,避免类型不匹配 - scheduleSlotMapper.updateStatusById(slotDetail.getScheduleSlotId(), "3"); - log.info("预约挂号支付成功,排班号 status 更新为已取,slotId={}", slotDetail.getScheduleSlotId()); - } - } catch (Exception e) { - // 任何异常都不应导致事务回滚,因为支付已经成功,故记录日志后继续 - log.error("支付后更新排班号状态失败,orderId={}, error={}", orderId, e.getMessage()); - } - - return true; + // 如有关联的排班号,保持其状态不变(退回不影响排班),因此这里不做任何 ScheduleSlot 更新。 } - /** - * 退号(门诊诊前退号)处理。 - * - * @param orderId 订单主键 - */ - @Override - @Transactional(rollbackFor = Exception.class) - public void refundOrder(Long orderId) { - OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId); - if (orderMain == null) { - throw new BusinessException("订单不存在"); - } - - // 更新主表状态 - orderMain.setStatus(OrderStatus.REFUND.name()); - orderMainMapper.updateByPrimaryKeySelective(orderMain); - - // 更新明细状态 - OrderDetail detail = new OrderDetail(); - detail.setOrderId(orderId); - detail.setStatus(OrderStatus.REFUND.name()); - orderDetailMapper.updateStatusByOrderId(detail); - - // 【Bug #506】同步更新排班号状态为 “4”(已退号) - OrderDetail slotDetail = orderDetailMapper.selectByOrderId(orderId); - if (slotDetail != null && slotDetail.getScheduleSlotId() != null) { - scheduleSlotMapper.updateStatusById(slotDetail.getScheduleSlotId(), "4"); - log.info("退号成功,排班号 status 更新为已退号,slotId={}", slotDetail.getScheduleSlotId()); - } - } - - // 其余实现保持不变 + // ------------------------------------------------------------------------- + // 其它业务方法(省略)... + // ------------------------------------------------------------------------- }