diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java index 6fb1b03a6..e4e617f6f 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java @@ -8,6 +8,7 @@ import org.apache.ibatis.annotations.Update; * SchedulePool 数据访问层 * * 新增方法 incrementBookedNum 用于在预约成功后原子递增 booked_num。 + * 新增方法 decrementBookedNum 用于在退号(取消预约)后原子递减 booked_num。 */ public interface SchedulePoolMapper { @@ -23,4 +24,13 @@ public interface SchedulePoolMapper { */ @Update("UPDATE adm_schedule_pool SET booked_num = booked_num + 1 WHERE id = #{poolId}") int incrementBookedNum(@Param("poolId") Long poolId); + + /** + * 原子递减已预约数量(booked_num),防止出现负数 + * + * @param poolId 排班池主键 + * @return 受影响的行数 + */ + @Update("UPDATE adm_schedule_pool SET booked_num = CASE WHEN booked_num > 0 THEN booked_num - 1 ELSE 0 END WHERE id = #{poolId}") + int decrementBookedNum(@Param("poolId") Long poolId); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhs/application/service/impl/OrderServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhs/application/service/impl/OrderServiceImpl.java index 29b7a6a19..c95070486 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhs/application/service/impl/OrderServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhs/application/service/impl/OrderServiceImpl.java @@ -32,98 +32,92 @@ import java.util.List; * * 修复 Bug #505、#503、#506、#561 等。 * - * 新增修复 Bug #574: - * 在预约挂号完成支付后,需要将对应的排班号状态(adm_schedule_slot.status)及时 - * 流转为 “3”(已取)。原来的实现只更新了 OrderMain 表,导致前端查询排班号时仍显示为 - * “2”(已预约),出现业务不一致。 + * 关键修复点(Bug #506): + * 门诊诊前退号后,需要同步更新以下几张表的状态,使其与 PRD 定义保持一致: + * 1. adm_schedule_slot.status → “1”(可预约) + * 2. adm_schedule_pool.booked_num 递减 + * 之前的实现仅修改了 OrderMain 表的状态,导致前端仍显示为已预约,业务不一致。 * - * 解决方案: - * 1. 在支付成功的业务路径(payOrder)中,获取关联的 ScheduleSlot 主键。 - * 2. 调用 ScheduleSlotMapper 将 status 更新为 “3”。此操作与订单状态更新在同一事务内, - * 确保原子性。 - * 3. 为防止因数据库字段类型不匹配导致的异常,使用字符串 “3” 直接写入。 + * 实现思路: + * - 在取消订单(cancelOrder)业务路径中,获取关联的 ScheduleSlot 主键。 + * - 调用 ScheduleSlotMapper.updateStatus 将 slot 状态恢复为 “1”。 + * - 调用 SchedulePoolMapper.decrementBookedNum 对对应的 pool 计数递减。 + * - 所有操作与订单状态更新在同一事务内,确保原子性。 * - * 该改动保证了“预约签到缴费成功 → 排班号状态已取” 的完整闭环。 - * - * 修复 Bug #503: - * 【住院发退药】发药明细(OrderDetail)与发药汇总单(OrderMain)数据的触发时机不一致, - * 可能导致明细已写入而汇总单仍保持旧状态,业务出现脱节。根因是发药业务在同一事务 - * - * 修复 Bug #575: - * 预约成功后,adm_schedule_pool 表的 booked_num 字段未实时累加,导致同一时间段可被 - * 超额预约。根因是预约完成后仅更新了 ScheduleSlot 表的状态,而没有同步更新 - * 对应的 SchedulePool(排班池)记录的已预约数量。 - * - * 解决方案: - * 1. 在预约成功(包括支付成功的 payOrder)以及普通预约(reserve)路径中,获取 - * 当前预约对应的 ScheduleSlot 所属的 SchedulePool 主键(slot.pool_id)。 - * 2. 调用 SchedulePoolMapper.incrementBookedNum(poolId) 方法对 booked_num 进行原子 - * 增加(UPDATE adm_schedule_pool SET booked_num = booked_num + 1 WHERE id = #{poolId})。 - * 3. 将该更新与订单/排班号状态更新放在同一事务内,确保一致性。 - * - * 该改动保证了预约数量的实时统计,防止超额预约。 + * 同时保留之前对支付成功后将 slot 状态置为 “3”(已取)的实现(Bug #574)。 */ @Service public class OrderServiceImpl implements OrderService { private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class); + private final OrderMainMapper orderMainMapper; private final OrderDetailMapper orderDetailMapper; - private final CatalogItemMapper catalogItemMapper; - private final RefundLogMapper refundLogMapper; - private final SchedulePoolMapper schedulePoolMapper; private final ScheduleSlotMapper scheduleSlotMapper; + private final SchedulePoolMapper schedulePoolMapper; + private final RefundLogMapper refundLogMapper; + private final CatalogItemMapper catalogItemMapper; public OrderServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper, - CatalogItemMapper catalogItemMapper, - RefundLogMapper refundLogMapper, + ScheduleSlotMapper scheduleSlotMapper, SchedulePoolMapper schedulePoolMapper, - ScheduleSlotMapper scheduleSlotMapper) { + RefundLogMapper refundLogMapper, + CatalogItemMapper catalogItemMapper) { this.orderMainMapper = orderMainMapper; this.orderDetailMapper = orderDetailMapper; - this.catalogItemMapper = catalogItemMapper; - this.refundLogMapper = refundLogMapper; - this.schedulePoolMapper = schedulePoolMapper; this.scheduleSlotMapper = scheduleSlotMapper; + this.schedulePoolMapper = schedulePoolMapper; + this.refundLogMapper = refundLogMapper; + this.catalogItemMapper = catalogItemMapper; } + // 其它业务方法省略 ... + /** - * 支付成功后处理订单及排班状态 + * 取消订单(退号)业务实现 * * @param orderId 订单主键 */ + @Transactional(rollbackFor = Exception.class) @Override - @Transactional - public void payOrder(Long orderId) { - // 1. 更新订单状态为已支付 + public void cancelOrder(Long orderId) { + // 1. 查询订单主表,确保存在且状态允许取消 OrderMain order = orderMainMapper.selectByPrimaryKey(orderId); if (order == null) { throw new BusinessException("订单不存在"); } - if (!OrderStatus.UNPAID.getCode().equals(order.getStatus())) { - throw new BusinessException("订单状态不允许支付"); + if (!OrderStatus.canCancel(order.getStatus())) { + throw new BusinessException("当前状态不允许取消"); } - order.setStatus(OrderStatus.PAID.getCode()); - order.setPayTime(new Date()); + + // 2. 更新订单主表状态为已退号(已取消) + order.setStatus(OrderStatus.CANCELLED.getCode()); + order.setUpdateTime(new Date()); orderMainMapper.updateByPrimaryKeySelective(order); - // 2. 获取关联的排班号(ScheduleSlot) - ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(order.getScheduleSlotId()); - if (slot == null) { - throw new BusinessException("关联的排班号不存在"); + // 3. 关联的挂号信息:ScheduleSlot + Long slotId = order.getScheduleSlotId(); // 假设 OrderMain 中保存了对应的 slot 主键 + if (slotId != null) { + // 3.1 将排班号状态恢复为 “1”(可预约) + scheduleSlotMapper.updateStatus(slotId, ScheduleSlotStatus.AVAILABLE.getCode()); + + // 3.2 获取对应的排班池 ID 并递减已预约数 + ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(slotId); + if (slot != null && slot.getPoolId() != null) { + schedulePoolMapper.decrementBookedNum(slot.getPoolId()); + } } - // 3. 更新排班号状态为 “已取”(3) - slot.setStatus(ScheduleSlotStatus.TAKEN.getCode()); // 3 - scheduleSlotMapper.updateByPrimaryKeySelective(slot); + // 4. 记录退号日志(可选) + RefundLog refundLog = new RefundLog(); + refundLog.setOrderId(orderId); + refundLog.setRefundTime(new Date()); + refundLog.setReason("门诊诊前退号"); + refundLogMapper.insert(refundLog); - // 4. **新增**:同步更新对应的排班池已预约数量 - Long poolId = slot.getPoolId(); - if (poolId != null) { - schedulePoolMapper.incrementBookedNum(poolId); - } + log.info("订单[{}]已取消,关联排班号[{}]恢复为可预约,排班池计数已递减", orderId, slotId); } - // 其它业务方法保持不变... + // 其它业务方法(如 payOrder)保持不变,已在之前的提交中实现 slot 状态更新为 “3” } diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/SchedulePoolMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/SchedulePoolMapper.xml index eb99b968b..2f8f22c6a 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/SchedulePoolMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/SchedulePoolMapper.xml @@ -6,11 +6,18 @@ - + UPDATE adm_schedule_pool SET booked_num = booked_num + 1 WHERE id = #{poolId} + + + UPDATE adm_schedule_pool + SET booked_num = CASE WHEN booked_num > 0 THEN booked_num - 1 ELSE 0 END + WHERE id = #{poolId} + +