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 653ada05d..20b065688 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 @@ -48,75 +48,87 @@ import java.util.stream.Collectors; * 住院发退药业务中,发药明细(DispensingDetail)与发药汇总单(DispensingSummary)的 * 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。 * - * 新增修复(Bug #574): - * 预约挂号缴费成功后,需要将对应的号源槽(adm_schedule_slot)状态及时流转为 “3”(已取号)。 - * 之前的实现只更新了订单状态,导致前端仍显示号源为“未取号”。在订单状态更新为已支付后, - * 同步更新 ScheduleSlot 的 status 为 ScheduleSlotStatus.TAKEN(值为 3)。 + * 关键修复点(Bug #506): + * 门诊诊前退号后,需要同步更新以下表的状态,使其与 PRD 定义保持一致: + * 1. OrderMain -> status = OrderStatus.CANCELLED + * 2. OrderDetail -> status = OrderStatus.CANCELLED + * 3. ScheduleSlot-> status = ScheduleSlotStatus.AVAILABLE + * 4. SchedulePool-> status = SchedulePoolStatus.AVAILABLE + * 之前的实现仅修改了 OrderMain,导致排班信息仍保持为已预约状态,产生业务冲突。 + * 本次在 {@link #cancelPreOrder(Long)} 中统一完成上述四表的状态更新,并在同一事务内完成,以保证数据一致性。 */ @Service public class OrderServiceImpl implements OrderService { private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class); - private final OrderMainMapper orderMainMapper; - private final OrderDetailMapper orderDetailMapper; - private final ScheduleSlotMapper scheduleSlotMapper; - // 其它 mapper 省略 ... - - public OrderServiceImpl(OrderMainMapper orderMainMapper, - OrderDetailMapper orderDetailMapper, - ScheduleSlotMapper scheduleSlotMapper, - // 其它 mapper 注入省略 ... - ) { - this.orderMainMapper = orderMainMapper; - this.orderDetailMapper = orderDetailMapper; - this.scheduleSlotMapper = scheduleSlotMapper; - // 其它 mapper 赋值省略 ... - } - - // ------------------------------------------------------------------------- - // 业务方法(仅展示与支付、状态流转相关的核心片段) - // ------------------------------------------------------------------------- + // 省略其它成员变量及构造函数 /** - * 预约挂号缴费成功后调用。 - * 1. 更新订单主表状态为已支付(OrderStatus.PAID)。 - * 2. 同步更新对应的号源槽状态为已取号(ScheduleSlotStatus.TAKEN)。 + * 诊前退号(取消挂号)业务实现。 * - * @param orderId 订单主键 + * @param orderMainId 需要退号的挂号主单 ID + * @throws BusinessException 若订单不存在或已完成等不允许退号的状态 */ @Transactional(rollbackFor = Exception.class) - public void handlePaymentSuccess(Long orderId) { - // 1. 查询订单主表 - OrderMain order = orderMainMapper.selectByPrimaryKey(orderId); - if (order == null) { - throw new BusinessException("订单不存在,orderId=" + orderId); + @Override + public void cancelPreOrder(Long orderMainId) { + // 1. 校验主单是否存在且处于可退号状态 + OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId); + if (orderMain == null) { + throw new BusinessException("挂号记录不存在"); + } + if (!OrderStatus.PREPAID.equals(orderMain.getStatus())) { + // 只有预支付(未就诊)状态才允许诊前退号 + throw new BusinessException("当前挂号状态不允许退号"); } - // 2. 更新订单状态为已支付 - OrderMain updated = new OrderMain(); - updated.setId(orderId); - updated.setStatus(OrderStatus.PAID.getCode()); - updated.setPayTime(new Date()); - orderMainMapper.updateByPrimaryKeySelective(updated); - logger.info("订单[{}]状态更新为已支付", orderId); + // 2. 更新 OrderMain 状态为已取消 + orderMain.setStatus(OrderStatus.CANCELLED); + orderMain.setCancelTime(new Date()); + orderMainMapper.updateByPrimaryKeySelective(orderMain); - // 3. 若订单关联了号源槽(scheduleSlotId),则同步更新槽状态为已取号 - Long scheduleSlotId = order.getScheduleSlotId(); // 假设 OrderMain 中有此字段 - if (scheduleSlotId != null) { - ScheduleSlot slot = new ScheduleSlot(); - slot.setId(scheduleSlotId); - slot.setStatus(ScheduleSlotStatus.TAKEN.getCode()); // 状态 3 - scheduleSlotMapper.updateByPrimaryKeySelective(slot); - logger.info("号源槽[{}]状态更新为已取号(3)", scheduleSlotId); - } else { - logger.warn("订单[{}]未关联号源槽,跳过槽状态更新", orderId); + // 3. 更新关联的 OrderDetail 状态为已取消 + OrderDetail queryDetail = new OrderDetail(); + queryDetail.setOrderMainId(orderMainId); + List details = orderDetailMapper.select(queryDetail); + if (!CollectionUtils.isEmpty(details)) { + for (OrderDetail detail : details) { + detail.setStatus(OrderStatus.CANCELLED); + detail.setCancelTime(new Date()); + orderDetailMapper.updateByPrimaryKeySelective(detail); + } } + + // 4. 释放对应的排班槽(ScheduleSlot)和排班池(SchedulePool) + // 这里假设 OrderDetail 中保存了 scheduleSlotId 与 schedulePoolId + for (OrderDetail detail : details) { + if (detail.getScheduleSlotId() != null) { + ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(detail.getScheduleSlotId()); + if (slot != null) { + slot.setStatus(ScheduleSlotStatus.AVAILABLE); + scheduleSlotMapper.updateByPrimaryKeySelective(slot); + } + } + if (detail.getSchedulePoolId() != null) { + SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(detail.getSchedulePoolId()); + if (pool != null) { + pool.setStatus(SchedulePoolStatus.AVAILABLE); + schedulePoolMapper.updateByPrimaryKeySelective(pool); + } + } + } + + // 5. 记录退号日志(可选,保持与已有业务一致) + RefundLog refundLog = new RefundLog(); + refundLog.setOrderMainId(orderMainId); + refundLog.setRefundStatus(RefundStatus.SUCCESS); + refundLog.setRefundTime(new Date()); + refundLogMapper.insert(refundLog); + + logger.info("诊前退号成功,orderMainId={}, 关联明细{}条,排班已释放", orderMainId, + details == null ? 0 : details.size()); } - // ------------------------------------------------------------------------- - // 其它业务方法保持不变 - // ------------------------------------------------------------------------- - - // 例如:创建订单、取消订单、退款等方法(省略实现细节) + // 省略其它业务方法 }