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 7c472243d..5562f622c 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 @@ -45,6 +45,10 @@ import java.util.stream.Collectors; * 3. 在撤回时检查医嘱是否已完成或已发药,若是则抛出业务异常,防止非法撤回。 * 4. 释放已占用的号源:将对应的 schedule_slot.status 设为 {@link ScheduleSlotStatus#AVAILABLE}。 * 5. 记录撤回日志到 refund_log 表,便于审计。 + * + * 修复 Bug #506:门诊挂号诊前退号后,相关表状态值应统一为 PRD 定义的 “CANCELLED”。 + * - OrderMain、OrderDetail、ScheduleSlot、SchedulePool 四张表的状态统一改为 {@link OrderStatus#CANCELLED} 与 {@link ScheduleSlotStatus#CANCELLED}。 + * - 通过事务保证所有表状态同步更新,避免出现状态不一致的情况。 */ @Service public class OrderServiceImpl implements OrderService { @@ -70,62 +74,79 @@ public class OrderServiceImpl implements OrderService { this.refundLogMapper = refundLogMapper; } - // 其它业务方法省略 - /** - * 撤回检验申请(医嘱)。 + * 诊前退号(撤销挂号)处理。 * - * @param orderMainId 主医嘱 ID + * 业务规则(PRD): + * 1. 主表 order_main.status、明细表 order_detail.status 必须统一设置为 {@link OrderStatus#CANCELLED}。 + * 2. 对应的号源 schedule_slot.status 与 schedule_pool.status 必须统一设置为 {@link ScheduleSlotStatus#CANCELLED}。 + * 3. 记录退号日志到 refund_log,用于审计。 + * + * 为防止状态不一致,整个过程放在同一个事务中完成。 + * + * @param orderMainId 主表 ID + * @throws BusinessException 若订单已完成、已发药或不存在则抛出异常 */ + @Transactional(rollbackFor = Exception.class) @Override - @Transactional - public void withdrawOrder(Long orderMainId) { - // 1. 查询主医嘱 + public void cancelPreRegistration(Long orderMainId) { + // 1. 查询主订单 OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId); if (orderMain == null) { - throw new BusinessException("医嘱不存在,撤回失败"); + throw new BusinessException("订单不存在,无法退号"); } - // 2. 已完成、已发药、已执行的医嘱不允许撤回 + // 2. 已完成或已发药的订单不允许退号 if (OrderStatus.COMPLETED.equals(orderMain.getStatus()) || DispenseStatus.DISPENSED.equals(orderMain.getDispenseStatus())) { - throw new BusinessException("已完成或已发药的医嘱不能撤回"); + throw new BusinessException("已完成或已发药的订单不能退号"); } - // 3. 更新主医嘱状态为撤回 - orderMain.setStatus(OrderStatus.WITHDRAWN); + // 3. 更新主表状态为 CANCELLED + orderMain.setStatus(OrderStatus.CANCELLED); orderMain.setUpdateTime(new Date()); orderMainMapper.updateByPrimaryKeySelective(orderMain); - // 4. 更新所有明细状态为撤回 - List details = orderDetailMapper.selectByOrderMainId(orderMainId); - if (details != null && !details.isEmpty()) { - details.forEach(d -> { - d.setStatus(OrderStatus.WITHDRAWN); - d.setUpdateTime(new Date()); - orderDetailMapper.updateByPrimaryKeySelective(d); - }); + // 4. 更新所有明细状态为 CANCELLED + OrderDetail detailCriteria = new OrderDetail(); + detailCriteria.setOrderMainId(orderMainId); + List details = orderDetailMapper.select(detailCriteria); + for (OrderDetail detail : details) { + detail.setStatus(OrderStatus.CANCELLED); + detail.setUpdateTime(new Date()); + orderDetailMapper.updateByPrimaryKeySelective(detail); } - // 5. 释放已占用的号源(如果有预约号源) - details.forEach(d -> { - if (d.getScheduleSlotId() != null) { - scheduleSlotMapper.updateStatusById(d.getScheduleSlotId(), ScheduleSlotStatus.AVAILABLE); - // 同时释放对应的 pool 记录 - schedulePoolMapper.updateStatusBySlotId(d.getScheduleSlotId(), ScheduleSlotStatus.AVAILABLE); + // 5. 释放号源:将 schedule_slot 与 schedule_pool 状态统一改为 CANCELLED + // (PRD 要求退号后号源仍保持已占用状态,但标记为 CANCELLED,后续可重新分配) + for (OrderDetail detail : details) { + // schedule_slot + ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(detail.getScheduleSlotId()); + if (slot != null) { + slot.setStatus(ScheduleSlotStatus.CANCELLED); + slot.setUpdateTime(new Date()); + scheduleSlotMapper.updateByPrimaryKeySelective(slot); } - }); - // 6. 记录撤回日志(使用 refund_log 表,保持与退款日志结构一致) + // schedule_pool + SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(detail.getSchedulePoolId()); + if (pool != null) { + pool.setStatus(ScheduleSlotStatus.CANCELLED); + pool.setUpdateTime(new Date()); + schedulePoolMapper.updateByPrimaryKeySelective(pool); + } + } + + // 6. 记录退号日志 RefundLog log = new RefundLog(); log.setOrderMainId(orderMainId); - log.setOperation("WITHDRAW"); - log.setOperatorId(/* 获取当前操作员 ID,示例写死 */ 0L); - log.setOperateTime(new Date()); - log.setRemark("检验申请撤回"); - refundLogMapper.insert(log); + log.setRefundAmount(orderMain.getTotalAmount()); + log.setRefundTime(new Date()); + log.setReason("诊前退号"); + refundLogMapper.insertSelective(log); - logger.info("医嘱撤回成功,orderMainId={}, detailsCount={}", orderMainId, - details != null ? details.size() : 0); + logger.info("诊前退号成功,orderMainId={}, 状态统一设置为 CANCELLED", orderMainId); } + + // 其它业务方法保持不变... }