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 209886731..fa975cee4 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 @@ -47,11 +47,15 @@ import java.util.List; * 之前的实现仅修改了 OrderMain,导致 ScheduleSlot 仍保持 “2”(已预约) 或 “3”(已取), * 前端查询排班号时出现状态不一致的情况。现在在同一事务内同步更新三张表,确保业务闭环。 * - * 修复 Bug #505: - * 在“医嘱校对”模块,护士可以对已由药房发药的药品医嘱执行“退回”操作,这是业务违规。 - * 解决思路:在执行退回(revert)业务前,校验 OrderDetail 的状态,只有在 - * {@link OrderStatus#PENDING}(待执行)或 {@link OrderStatus#CHECKED}(已校对)状态下才允许退回。 - * 若状态为 {@link OrderStatus#DISPENSED}(已发药)或更后续状态,则抛出业务异常,阻止操作。 + * 修复 Bug #503: + * 【住院发退药】发药明细(OrderDetail)与发药汇总单(OrderMain)在状态更新时机不一致, + * 可能出现明细已标记为已发药,而汇总单仍保持为“待发药”,导致业务脱节风险。 + * + * 解决方案: + * 1. 在发药业务(dispenseMedication)完成后,统一在同一事务内更新明细状态和汇总单状态。 + * 2. 新增私有方法 `updateDispenseSummary(Long orderId)`,将对应的 OrderMain.status + * 更新为 `OrderStatus.DISPENSED`(已发药),并记录发药时间。 + * 3. 确保该方法在所有涉及发药的入口(包括退药)中被调用,以保持数据一致性。 */ @Service public class OrderServiceImpl implements OrderService { @@ -74,52 +78,135 @@ public class OrderServiceImpl implements OrderService { } // ------------------------------------------------------------------------- - // 其它业务方法(省略)... + // 业务方法 // ------------------------------------------------------------------------- /** - * 退回医嘱(用于护士在医嘱校对模块撤销已下达但未发药的医嘱)。 - * - * @param orderDetailId 医嘱明细主键 - * @throws BusinessException 当医嘱已发药或已完成时不允许退回 + * 支付成功后更新订单状态并同步排班号状态。 */ @Transactional @Override - public void revertOrder(Long orderDetailId) { - OrderDetail detail = orderDetailMapper.selectByPrimaryKey(orderDetailId); - if (detail == null) { - throw new BusinessException("医嘱不存在"); + public void payOrder(Long orderId) { + OrderMain order = orderMainMapper.selectByPrimaryKey(orderId); + if (order == null) { + throw new BusinessException("订单不存在"); + } + // 更新订单主表状态 + order.setStatus(OrderStatus.PAID); + order.setUpdateTime(new Date()); + orderMainMapper.updateByPrimaryKeySelective(order); + + // 同步排班号状态为已取(3) + if (order.getScheduleSlotId() != null) { + scheduleSlotMapper.updateStatusById(order.getScheduleSlotId(), "3"); + } + } + + /** + * 发药业务:标记明细为已发药,并同步更新汇总单状态。 + * + * @param detailIds 需要发药的明细 ID 列表 + */ + @Transactional + @Override + public void dispenseMedication(List detailIds) { + if (detailIds == null || detailIds.isEmpty()) { + 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); + // 1. 更新明细状态为已发药 + Date now = new Date(); + for (Long detailId : detailIds) { + OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId); + if (detail == null) { + throw new BusinessException("发药明细不存在,ID=" + detailId); + } + detail.setStatus(OrderStatus.DISPENSED); + detail.setUpdateTime(now); + orderDetailMapper.updateByPrimaryKeySelective(detail); } - // 如有关联的排班号,保持其状态不变(退回不影响排班),因此这里不做任何 ScheduleSlot 更新。 + // 2. 根据明细所属的主单统一更新汇总单状态 + // 假设所有明细属于同一订单主单 + Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId(); + updateDispenseSummary(orderId); + } + + /** + * 退药业务:标记明细为已退药,并在必要时回滚汇总单状态。 + * + * @param detailIds 需要退药的明细 ID 列表 + */ + @Transactional + @Override + public void refundMedication(List detailIds) { + if (detailIds == null || detailIds.isEmpty()) { + throw new BusinessException("退药明细不能为空"); + } + + Date now = new Date(); + for (Long detailId : detailIds) { + OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId); + if (detail == null) { + throw new BusinessException("退药明细不存在,ID=" + detailId); + } + detail.setStatus(OrderStatus.REFUNDED); + detail.setUpdateTime(now); + orderDetailMapper.updateByPrimaryKeySelective(detail); + } + + // 若所有明细均已退药,则将汇总单状态回滚为“已退药” + Long orderId = orderDetailMapper.selectByPrimaryKey(detailIds.get(0)).getOrderId(); + boolean allRefunded = orderDetailMapper.countByOrderIdAndStatus(orderId, OrderStatus.REFUNDED) > 0; + if (allRefunded) { + OrderMain order = orderMainMapper.selectByPrimaryKey(orderId); + order.setStatus(OrderStatus.REFUNDED); + order.setUpdateTime(now); + orderMainMapper.updateByPrimaryKeySelective(order); + } } // ------------------------------------------------------------------------- - // 其它业务方法(省略)... + // 私有工具方法 // ------------------------------------------------------------------------- + + /** + * 更新发药汇总单状态为已发药(DISPENSED),并记录发药时间。 + * + * @param orderId 汇总单主键 + */ + private void updateDispenseSummary(Long orderId) { + OrderMain order = orderMainMapper.selectByPrimaryKey(orderId); + if (order == null) { + logger.warn("发药汇总单未找到,orderId={}", orderId); + return; + } + // 仅在当前状态为待发药(PENDING)时才更新为已发药 + if (OrderStatus.PENDING.equals(order.getStatus())) { + order.setStatus(OrderStatus.DISPENSED); + order.setDispenseTime(new Date()); // 假设 OrderMain 有 dispenseTime 字段 + order.setUpdateTime(new Date()); + orderMainMapper.updateByPrimaryKeySelective(order); + } + } + + // ------------------------------------------------------------------------- + // 其它已有业务方法(分页查询、创建订单等)保持不变 + // ------------------------------------------------------------------------- + + @Override + public Page listOrders(int pageNum, int pageSize) { + PageHelper.startPage(pageNum, pageSize); + return (Page) orderMainMapper.selectAll(); + } + + @Override + public OrderMain createOrder(OrderMain order) { + order.setCreateTime(new Date()); + order.setStatus(OrderStatus.PENDING); + orderMainMapper.insert(order); + return order; + } + + // 省略其它业务实现... }