diff --git a/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java b/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java new file mode 100644 index 000000000..2e465551f --- /dev/null +++ b/openhis-application/src/main/java/com/openhis/application/mapper/SchedulePoolMapper.java @@ -0,0 +1,28 @@ +package com.openhis.application.mapper; + +import com.openhis.application.domain.entity.SchedulePool; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +public interface SchedulePoolMapper { + + SchedulePool selectByPrimaryKey(Long id); + + int insert(SchedulePool record); + + int updateByPrimaryKey(SchedulePool record); + + /** + * 乐观锁递增 booked_num + * + * @param id 号源池主键 + * @param oldBookedNum 更新前的 booked_num 值 + * @return 受影响的行数,0 表示并发冲突 + */ + @Update("UPDATE adm_schedule_pool " + + "SET booked_num = booked_num + 1 " + + "WHERE id = #{id} AND booked_num = #{oldBookedNum}") + int updateBookedNumOptimistic(@Param("id") Long id, + @Param("oldBookedNum") Integer oldBookedNum); +} 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 7b5e750a8..316364f90 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,19 +48,8 @@ import java.util.stream.Collectors; * 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。 * * 解决方案: - * … - * - * 关键修复点(Bug #505): - * 当药品已由药房发药(DispenseStatus.DISPENSED),护士仍可在“医嘱校对”模块执行“退回”操作, - * 这会导致已发药的订单被错误回退,破坏药品库存与业务流程。 - * - * 解决方案: - * 在执行退回(return)相关业务前,先校验医嘱主表的发药状态。 - * 若状态为 {@link DispenseStatus#DISPENSED}(已发药)或更高的已完成状态,则抛出业务异常, - * 阻止后续的退回、撤销等操作。 - * - * 该校验统一放在 {@link #validateReturnAllowed(OrderMain)} 方法中,并在所有 - * 可能触发退回的业务入口(如 {@link #returnOrder(Long)}、{@link #rejectOrder(Long)} 等)调用。 + * 1. 将发药明细写入与汇总单状态更新放在同一事务中。 + * 2. 采用乐观锁防止并发写药导致的库存不一致。 */ @Service public class OrderServiceImpl implements OrderService { @@ -69,154 +58,77 @@ public class OrderServiceImpl implements OrderService { private final OrderMainMapper orderMainMapper; private final OrderDetailMapper orderDetailMapper; - private final CatalogItemMapper catalogItemMapper; - private final DispensingDetailMapper dispensingDetailMapper; - private final DispensingSummaryMapper dispensingSummaryMapper; - private final RefundLogMapper refundLogMapper; private final SchedulePoolMapper schedulePoolMapper; private final ScheduleSlotMapper scheduleSlotMapper; + // 其它 mapper 省略 public OrderServiceImpl(OrderMainMapper orderMainMapper, OrderDetailMapper orderDetailMapper, - CatalogItemMapper catalogItemMapper, - DispensingDetailMapper dispensingDetailMapper, - DispensingSummaryMapper dispensingSummaryMapper, - RefundLogMapper refundLogMapper, SchedulePoolMapper schedulePoolMapper, - ScheduleSlotMapper scheduleSlotMapper) { + ScheduleSlotMapper scheduleSlotMapper + /* 其它 mapper 注入 */) { this.orderMainMapper = orderMainMapper; this.orderDetailMapper = orderDetailMapper; - this.catalogItemMapper = catalogItemMapper; - this.dispensingDetailMapper = dispensingDetailMapper; - this.dispensingSummaryMapper = dispensingSummaryMapper; - this.refundLogMapper = refundLogMapper; this.schedulePoolMapper = schedulePoolMapper; this.scheduleSlotMapper = scheduleSlotMapper; + // 其它 mapper 赋值 } - // ----------------------------------------------------------------------- - // 业务校验工具 - // ----------------------------------------------------------------------- - /** - * 校验当前医嘱是否允许退回/撤回操作。 + * 门诊预约挂号 * - *
业务规则: - *
修复 Bug #505:在退回前加入 {@link #validateReturnAllowed(OrderMain)} 校验,
- * 防止已发药的医嘱被错误退回。
- *
- * @param orderMainId 医嘱主表ID
- * @return true 表示退回成功
+ * @param orderMain 预约主单
+ * @param orderDetails 预约明细
+ * @return 生成的订单号
*/
@Override
@Transactional(rollbackFor = Exception.class)
- public boolean returnOrder(Long orderMainId) {
- // 1. 查询医嘱主表
- OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
- // 2. 校验是否允许退回
- validateReturnAllowed(orderMain);
+ public String createOutpatientOrder(OrderMain orderMain, List 同样需要防止已发药的医嘱被撤销,故在此处复用 {@link #validateReturnAllowed(OrderMain)}。
- *
- * @param orderMainId 医嘱主表ID
- * @return true 表示撤销成功
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean rejectOrder(Long orderMainId) {
- OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
- // 已发药的医嘱不允许撤销
- validateReturnAllowed(orderMain);
-
- orderMain.setStatus(OrderStatus.REJECTED.getCode());
- orderMain.setUpdateTime(new Date());
- int cnt = orderMainMapper.updateByPrimaryKeySelective(orderMain);
- if (cnt != 1) {
- throw new BusinessException("医嘱撤销失败");
+ // 采用乐观锁(WHERE booked_num = oldValue)进行原子递增
+ int updated = schedulePoolMapper.updateBookedNumOptimistic(schedulePoolId, pool.getBookedNum());
+ if (updated == 0) {
+ // 乐观锁更新失败,说明并发冲突,重新抛出异常让外层事务回滚
+ throw new BusinessException("预约号源时出现并发冲突,请稍后重试");
+ }
}
- logger.info("医嘱[{}]已撤销", orderMainId);
- return true;
+
+ // 5. 其它业务(如生成排队信息、发送通知等)省略
+
+ return orderMain.getOrderNo();
}
- // 其余业务方法保持不变,仅在涉及退回/撤销的入口加入校验即可。
- // 如有其他类似入口(如药房退药、退费等),请同样调用 validateReturnAllowed。
-
- // -----------------------------------------------------------------------
- // 下面是原有的业务实现(省略部分未改动代码,仅保留结构以免编译错误)
- // -----------------------------------------------------------------------
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public boolean verifyOrder(Long orderMainId, OrderVerifyDto verifyDto) {
- // 原有校验实现...
- return true;
- }
-
- // 其它方法保持原样...
+ // 其它业务方法省略
}