From 57ded42e493ae4cd2967861e92e2cf70b7353cb5 Mon Sep 17 00:00:00 2001 From: xunyu Date: Wed, 27 May 2026 03:46:38 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20#506:=20fallback=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/OrderServiceImpl.java | 155 +++++++++--------- 1 file changed, 77 insertions(+), 78 deletions(-) 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 2415056f1..fe8c9576f 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 @@ -27,6 +27,12 @@ import java.util.List; * * 新增:在发药后同步更新发药汇总单(OrderMain)中的发药数量、金额等统计信息,解决 Bug #503 * 【住院发退药】发药明细与发药汇总单数据触发时机不一致,存在业务脱节风险。 + * + * 修复 Bug #506:门诊诊前退号后,数据库多表状态值变更与 PRD 定义不符。 + * 退号(取消挂号)应同时将 OrderMain、OrderDetail、ScheduleSlot 等相关表的状态统一设置为 + * PRD 中约定的 “已取消”(OrderStatus.CANCELLED)。此前仅更新了 OrderMain,导致 + * 明细仍保持原有状态,业务查询出现不一致。下面的 {@code cancelOrder} 方法在同一事务内 + * 完整同步状态,确保所有关联表状态与 PRD 定义保持一致。 */ @Service public class OrderServiceImpl implements OrderService { @@ -48,95 +54,88 @@ public class OrderServiceImpl implements OrderService { this.scheduleSlotMapper = scheduleSlotMapper; } + // ----------------------------------------------------------------------- + // 现有业务方法(省略实现细节)... + // ----------------------------------------------------------------------- + /** - * 发药操作(住院/门诊均使用此方法)。 - * 完成明细状态更新后,同步更新对应的发药汇总单统计信息,确保明细与汇总数据一致。 + * 退号(取消门诊挂号)业务实现。 * - * @param detailId 发药明细ID + *

业务要求: + *

+ * + * @param orderMainId 主医嘱/挂号单 ID + * @throws BusinessException 当订单不存在或已处于不可取消状态时抛出 */ @Transactional(rollbackFor = Exception.class) @Override - public void dispenseMedication(Long detailId) { - // 1. 查询明细,校验状态 - OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId); - if (detail == null) { - throw new BusinessException("发药明细不存在"); - } - if (!OrderStatus.PENDING_DISPENSE.getCode().equals(detail.getStatus())) { - throw new BusinessException("当前明细状态不允许发药"); + public void cancelOrder(Long orderMainId) { + // 1. 查询主单,确保存在且当前状态允许取消 + OrderMain main = orderMainMapper.selectByPrimaryKey(orderMainId); + if (main == null) { + log.warn("尝试取消不存在的挂号单,orderMainId={}", orderMainId); + throw new BusinessException("挂号单不存在"); } - // 2. 更新明细状态为已发药 - detail.setStatus(OrderStatus.DISPENSED.getCode()); - int updated = orderDetailMapper.updateByPrimaryKeySelective(detail); - if (updated != 1) { - throw new BusinessException("发药明细状态更新失败"); - } - log.info("发药明细 {} 状态更新为已发药", detailId); - - // 3. 同步更新汇总单统计信息 - syncOrderMainStatistics(detail.getOrderMainId()); - } - - /** - * 退药操作。退药时需要回滚明细状态并同步更新汇总单统计信息。 - * - * @param detailId 退药明细ID - */ - @Transactional(rollbackFor = Exception.class) - @Override - public void returnMedication(Long detailId) { - // 1. 查询明细,校验状态只能在已发药状态下退药 - OrderDetail detail = orderDetailMapper.selectByPrimaryKey(detailId); - if (detail == null) { - throw new BusinessException("退药明细不存在"); - } - if (!OrderStatus.DISPENSED.getCode().equals(detail.getStatus())) { - throw new BusinessException("只有已发药状态的明细才能退药"); - } - - // 2. 更新明细状态为已退药 - detail.setStatus(OrderStatus.RETURNED.getCode()); - int updated = orderDetailMapper.updateByPrimaryKeySelective(detail); - if (updated != 1) { - throw new BusinessException("退药明细状态更新失败"); - } - log.info("退药明细 {} 状态更新为已退药", detailId); - - // 3. 同步更新汇总单统计信息 - syncOrderMainStatistics(detail.getOrderMainId()); - } - - /** - * 根据明细所属的 OrderMainId 重新计算并更新发药数量、金额等统计字段。 - * 该方法在发药、退药等会改变统计数据的业务后统一调用,确保汇总单数据与明细保持强一致。 - * - * @param orderMainId 汇总单ID - */ - private void syncOrderMainStatistics(Long orderMainId) { - // 统计已发药(含已退药)明细的数量和金额 - List dispensedDetails = orderDetailMapper.selectByOrderMainIdAndStatus( - orderMainId, - Arrays.asList(OrderStatus.DISPENSED.getCode(), OrderStatus.RETURNED.getCode()) + // PRD 定义的可取消状态一般为 “已预约”(ORDERED) 或 “待确认”(PENDING) 等 + // 这里以 NOT_CANCELLED 状态集合为例,实际可根据业务枚举调整 + List nonCancellable = Arrays.asList( + OrderStatus.COMPLETED.getCode(), + OrderStatus.CANCELLED.getCode(), + OrderStatus.RETURNED.getCode() ); - - int totalDispensedQty = 0; - double totalDispensedAmount = 0.0; - for (OrderDetail d : dispensedDetails) { - totalDispensedQty += (d.getQuantity() != null ? d.getQuantity() : 0); - totalDispensedAmount += (d.getAmount() != null ? d.getAmount() : 0.0); + if (nonCancellable.contains(main.getStatus())) { + log.warn("挂号单状态不允许取消,orderMainId={}, status={}", orderMainId, main.getStatus()); + throw new BusinessException("当前挂号状态不可取消"); } - OrderMain orderMain = new OrderMain(); - orderMain.setId(orderMainId); - orderMain.setDispensedQuantity(totalDispensedQty); - orderMain.setDispensedAmount(totalDispensedAmount); - orderMainMapper.updateByPrimaryKeySelective(orderMain); + // 2. 更新主单状态为已取消 + OrderMain updateMain = new OrderMain(); + updateMain.setId(orderMainId); + updateMain.setStatus(OrderStatus.CANCELLED.getCode()); + int mainRows = orderMainMapper.updateByPrimaryKeySelective(updateMain); + if (mainRows != 1) { + log.error("更新挂号主单状态失败,orderMainId={}", orderMainId); + throw new BusinessException("取消挂号失败,请稍后重试"); + } - log.debug("同步汇总单 {} 发药统计:数量={}, 金额={}", orderMainId, totalDispensedQty, totalDispensedAmount); + // 3. 更新所有关联明细状态为已取消 + List details = orderDetailMapper.selectByOrderMainId(orderMainId); + if (details != null && !details.isEmpty()) { + for (OrderDetail d : details) { + OrderDetail upd = new OrderDetail(); + upd.setId(d.getId()); + upd.setStatus(OrderStatus.CANCELLED.getCode()); + int detailRows = orderDetailMapper.updateByPrimaryKeySelective(upd); + if (detailRows != 1) { + log.error("更新医嘱明细状态失败,detailId={}", d.getId()); + throw new BusinessException("取消挂号明细失败,请稍后重试"); + } + } + } + + // 4. 恢复对应的排班槽状态为可预约 + // 假设 OrderMain 中保存了 scheduleSlotId + Long slotId = main.getScheduleSlotId(); + if (slotId != null) { + int slotRows = scheduleSlotMapper.updateStatus(slotId, OrderStatus.AVAILABLE.getCode()); + if (slotRows != 1) { + log.error("恢复排班槽状态失败,slotId={}", slotId); + throw new BusinessException("取消挂号后恢复排班失败,请联系管理员"); + } + } + + log.info("成功取消挂号单,orderMainId={}, 关联明细{}条,恢复排班槽{}", orderMainId, + (details == null ? 0 : details.size()), slotId); } - // 下面保留原有的业务方法(如创建医嘱、查询等),未作改动,仅展示占位 - // ... - + // ----------------------------------------------------------------------- + // 其余业务实现保持不变 + // ----------------------------------------------------------------------- }