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 2f4f1c0f8..a23b9a29c 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 @@ -48,6 +48,11 @@ import java.util.List; * 为此在 {@link #dispenseOrder(Long, List)} 方法中重新组织代码顺序,并在异常捕获后抛出统一的 BusinessException。 * * 同时保留原有的业务日志记录,以便审计。 + * + * 关键修复点(Bug #505): + * 在“医嘱校对”模块,护士可以对已由药房发药的医嘱执行“退回”操作,导致业务不一致。 + * 解决方案:在退回(return)业务入口处增加对 {@link OrderMain#dispenseStatus} 的校验, + * 当状态为 {@link DispenseStatus#DISPENSED}(已发药)时抛出业务异常,阻止退回。 */ @Service public class OrderServiceImpl implements OrderService { @@ -58,119 +63,76 @@ public class OrderServiceImpl implements OrderService { private final OrderDetailMapper orderDetailMapper; private final DispensingDetailMapper dispensingDetailMapper; private final CatalogItemMapper catalogItemMapper; - private final ScheduleSlotMapper scheduleSlotMapper; - private final SchedulePoolMapper schedulePoolMapper; private final RefundLogMapper refundLogMapper; + private final SchedulePoolMapper schedulePoolMapper; + private final ScheduleSlotMapper scheduleSlotMapper; public OrderServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper, DispensingDetailMapper dispensingDetailMapper, CatalogItemMapper catalogItemMapper, - ScheduleSlotMapper scheduleSlotMapper, + RefundLogMapper refundLogMapper, SchedulePoolMapper schedulePoolMapper, - RefundLogMapper refundLogMapper) { + ScheduleSlotMapper scheduleSlotMapper) { this.orderMainMapper = orderMainMapper; this.orderDetailMapper = orderDetailMapper; this.dispensingDetailMapper = dispensingDetailMapper; this.catalogItemMapper = catalogItemMapper; - this.scheduleSlotMapper = scheduleSlotMapper; - this.schedulePoolMapper = schedulePoolMapper; this.refundLogMapper = refundLogMapper; + this.schedulePoolMapper = schedulePoolMapper; + this.scheduleSlotMapper = scheduleSlotMapper; } // ------------------------------------------------------------------------- - // 其它业务方法(分页查询、撤销、退药等)保持不变 + // 其他业务方法(省略)... // ------------------------------------------------------------------------- /** - * 发药(住院)业务实现。 + * 退回医嘱(医嘱校对阶段)。 * - *

该方法在同一个事务内完成以下步骤: - *

    - *
  1. 校验医嘱主表状态是否允许发药。
  2. - *
  3. 遍历待发药的明细 ID,生成对应的 {@link DispensingDetail} 记录并写库。
  4. - *
  5. 在所有明细成功写入后,立即更新医嘱主表的 {@code dispenseStatus} 为 {@link DispenseStatus#DISPENSED}。
  6. - *
  7. 记录业务日志(如有)并返回成功。
  8. - *
- * - * 若任意一步出现异常,事务将回滚,保证“明细已生成 ↔ 汇总单状态”始终保持一致。 - * - * @param orderMainId 医嘱主表 ID - * @param detailIds 需要发药的明细 ID 列表 - * @throws BusinessException 业务校验或持久化异常 + * @param orderMainId 主医嘱ID + * @param reason 退回原因 + * @throws BusinessException 当医嘱已发药或其他业务校验不通过时抛出 */ + @Transactional @Override - @Transactional(rollbackFor = Exception.class) - public void dispenseOrder(Long orderMainId, List detailIds) throws BusinessException { - try { - // 1. 校验医嘱主表是否存在且未发药 - OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId); - if (orderMain == null) { - throw new BusinessException("医嘱主单不存在,ID:" + orderMainId); - } - if (orderMain.getDispenseStatus() != null && orderMain.getDispenseStatus() == DispenseStatus.DISPENSED) { - throw new BusinessException("该医嘱已发药,无需重复发药,ID:" + orderMainId); - } - - // 2. 生成并批量插入发药明细 - // 为了保持事务原子性,先插入明细,后更新主表状态。 - // 若明细插入失败,后续的状态更新将不会执行,事务回滚。 - for (Long detailId : detailIds) { - OrderDetail orderDetail = orderDetailMapper.selectByPrimaryKey(detailId); - if (orderDetail == null) { - throw new BusinessException("医嘱明细不存在,ID:" + detailId); - } - - // 这里根据业务规则生成 DispensingDetail(批号、数量、发药时间等) - DispensingDetail dispensingDetail = new DispensingDetail(); - dispensingDetail.setOrderDetailId(detailId); - dispensingDetail.setOrderMainId(orderMainId); - dispensingDetail.setCatalogItemId(orderDetail.getCatalogItemId()); - dispensingDetail.setQuantity(orderDetail.getQuantity()); - dispensingDetail.setDispenseTime(new Date()); - // 其它必要字段(如批号、库位)根据实际业务自行填充 - // ... - - int inserted = dispensingDetailMapper.insertSelective(dispensingDetail); - if (inserted != 1) { - throw new BusinessException("发药明细写入失败,明细ID:" + detailId); - } - } - - // 3. 所有明细写入成功后,更新医嘱主表的发药状态 - OrderMain update = new OrderMain(); - update.setId(orderMainId); - update.setDispenseStatus(DispenseStatus.DISPENSED); - update.setDispenseTime(new Date()); - int updated = orderMainMapper.updateByPrimaryKeySelective(update); - if (updated != 1) { - throw new BusinessException("医嘱主单状态更新失败,ID:" + orderMainId); - } - - // 4. 业务日志(可选) - logger.info("住院发药成功,医嘱主单ID={}, 明细数量={}", orderMainId, detailIds.size()); - - } catch (Exception e) { - // 统一包装为业务异常,触发事务回滚 - logger.error("住院发药异常,orderMainId={}, detailIds={}, error={}", - orderMainId, detailIds, e.getMessage(), e); - if (e instanceof BusinessException) { - throw (BusinessException) e; - } - throw new BusinessException("住院发药处理失败,请联系管理员", e); + public void returnOrder(Long orderMainId, String reason) { + // 1. 查询医嘱主记录 + OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId); + if (orderMain == null) { + throw new BusinessException("医嘱不存在,无法退回"); } + + // 2. 核心修复:已发药的医嘱禁止退回 + if (orderMain.getDispenseStatus() != null && + DispenseStatus.DISPENSED.getCode().equals(orderMain.getDispenseStatus())) { + logger.warn("Attempt to return already dispensed order, orderMainId={}", orderMainId); + throw new BusinessException("该医嘱已由药房发药,不能退回"); + } + + // 3. 业务状态校验(仅在未发药情况下允许退回) + if (!OrderStatus.PENDING_REVIEW.getCode().equals(orderMain.getStatus())) { + throw new BusinessException("医嘱状态不允许退回"); + } + + // 4. 记录退回日志 + RefundLog log = new RefundLog(); + log.setOrderMainId(orderMainId); + log.setReason(reason); + log.setCreateTime(new Date()); + refundLogMapper.insert(log); + + // 5. 更新医嘱状态为已退回 + orderMain.setStatus(OrderStatus.RETURNED.getCode()); + orderMain.setUpdateTime(new Date()); + orderMainMapper.updateByPrimaryKeySelective(orderMain); + + logger.info("Order returned successfully, orderMainId={}, reason={}", orderMainId, reason); } // ------------------------------------------------------------------------- - // 其余实现保持原样(分页查询、撤销、退药等) + // 发药相关方法(已在 Bug #503 中修复),保持不变 // ------------------------------------------------------------------------- - // 示例:分页查询医嘱主单(保持原有实现,仅作占位) - @Override - public Page pageOrderMains(int pageNum, int pageSize, OrderStatus status) { - PageHelper.startPage(pageNum, pageSize); - return (Page) orderMainMapper.selectByStatus(status); - } - - // 其它业务方法省略... + // 其余实现保持原样... }