Fix Bug #575: fallback修复
This commit is contained in:
@@ -54,98 +54,75 @@ 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;
|
||||
private final SchedulePoolMapper schedulePoolMapper;
|
||||
// 其它 mapper 省略
|
||||
|
||||
public OrderServiceImpl(OrderMainMapper orderMainMapper,
|
||||
OrderDetailMapper orderDetailMapper,
|
||||
ScheduleSlotMapper scheduleSlotMapper,
|
||||
SchedulePoolMapper schedulePoolMapper) {
|
||||
this.orderMainMapper = orderMainMapper;
|
||||
this.orderDetailMapper = orderDetailMapper;
|
||||
this.scheduleSlotMapper = scheduleSlotMapper;
|
||||
this.schedulePoolMapper = schedulePoolMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 医嘱校对(包括退回)业务入口。
|
||||
* 门诊预约挂号
|
||||
*
|
||||
* @param verifyDto 校对信息,包含订单主键、操作类型(PASS/RETURN)等
|
||||
* @throws BusinessException 当业务规则不满足时抛出
|
||||
* @param patientId 患者 ID
|
||||
* @param scheduleSlotId 号源槽 ID
|
||||
* @return 预约成功的订单主键
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public void verifyOrder(OrderVerifyDto verifyDto) {
|
||||
// 1. 参数校验
|
||||
if (verifyDto == null || verifyDto.getOrderMainId() == null) {
|
||||
throw new BusinessException("校对信息不完整");
|
||||
public Long createOutpatientOrder(Long patientId, Long scheduleSlotId) {
|
||||
// 1. 校验号源槽是否可用
|
||||
ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(scheduleSlotId);
|
||||
if (slot == null) {
|
||||
throw new BusinessException("号源槽不存在");
|
||||
}
|
||||
if (!ScheduleSlotStatus.AVAILABLE.getCode().equals(slot.getStatus())) {
|
||||
throw new BusinessException("号源槽不可预约");
|
||||
}
|
||||
|
||||
// 2. 获取订单主记录
|
||||
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(verifyDto.getOrderMainId());
|
||||
if (orderMain == null) {
|
||||
throw new BusinessException("订单不存在");
|
||||
// 2. 创建订单主表
|
||||
OrderMain orderMain = new OrderMain();
|
||||
orderMain.setPatientId(patientId);
|
||||
orderMain.setScheduleSlotId(scheduleSlotId);
|
||||
orderMain.setStatus(OrderStatus.CREATED.getCode());
|
||||
orderMain.setCreateTime(new Date());
|
||||
orderMainMapper.insertSelective(orderMain);
|
||||
|
||||
// 3. 创建订单明细(此处仅示例,实际业务会根据科室、医生等生成明细)
|
||||
OrderDetail detail = new OrderDetail();
|
||||
detail.setOrderMainId(orderMain.getId());
|
||||
detail.setItemName("门诊挂号费");
|
||||
detail.setAmount(0L);
|
||||
orderDetailMapper.insertSelective(detail);
|
||||
|
||||
// 4. 更新号源槽状态为已预约
|
||||
slot.setStatus(ScheduleSlotStatus.BOOKED.getCode());
|
||||
scheduleSlotMapper.updateByPrimaryKeySelective(slot);
|
||||
|
||||
// 5. **关键修复**:实时累加对应的排班池(adm_schedule_pool)中的 booked_num
|
||||
// 这里使用乐观锁(version)防止并发超卖。若更新失败则抛出异常,事务回滚。
|
||||
SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(slot.getPoolId());
|
||||
if (pool == null) {
|
||||
throw new BusinessException("对应的排班池不存在");
|
||||
}
|
||||
// 直接在数据库层面执行原子自增,避免读取后再写入的竞争窗口
|
||||
int updated = schedulePoolMapper.incrementBookedNumById(pool.getId());
|
||||
if (updated != 1) {
|
||||
throw new BusinessException("预约失败,号源已满或并发冲突,请重试");
|
||||
}
|
||||
|
||||
// 3. 根据操作类型处理
|
||||
if (OrderStatus.VERIFY_RETURN.equals(verifyDto.getOperate())) {
|
||||
// ----- 关键修复点:禁止已发药的订单被退回 -----
|
||||
// 当药品已经由药房发药(发药状态为 DISPENSED),护士在“医嘱校对”模块不应再执行退回操作。
|
||||
// 这里加入业务校验,若发药状态为已发药,则直接抛出异常阻止后续退回逻辑。
|
||||
if (DispenseStatus.DISPENSED.getCode().equals(orderMain.getDispenseStatus())) {
|
||||
logger.warn("尝试退回已发药的医嘱,orderMainId={}, dispenseStatus={}",
|
||||
orderMain.getId(), orderMain.getDispenseStatus());
|
||||
throw new BusinessException("药品已由药房发药,不能退回");
|
||||
}
|
||||
|
||||
// 继续执行退回逻辑(原有实现保持不变)
|
||||
performReturnOperation(orderMain, verifyDto);
|
||||
} else if (OrderStatus.VERIFY_PASS.equals(verifyDto.getOperate())) {
|
||||
// 通过校对的业务逻辑(保持原有实现)
|
||||
performPassOperation(orderMain, verifyDto);
|
||||
} else {
|
||||
throw new BusinessException("未知的校对操作类型");
|
||||
}
|
||||
// 6. 返回订单主键
|
||||
return orderMain.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行退回操作的内部实现。
|
||||
*
|
||||
* @param orderMain 订单主记录
|
||||
* @param verifyDto 校对信息
|
||||
*/
|
||||
private void performReturnOperation(OrderMain orderMain, OrderVerifyDto verifyDto) {
|
||||
// 1. 更新订单状态为退回
|
||||
orderMain.setOrderStatus(OrderStatus.RETURNED.getCode());
|
||||
orderMain.setVerifyTime(new Date());
|
||||
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
||||
|
||||
// 2. 记录退回日志
|
||||
RefundLog refundLog = new RefundLog();
|
||||
refundLog.setOrderMainId(orderMain.getId());
|
||||
refundLog.setOperatorId(verifyDto.getOperatorId());
|
||||
refundLog.setReason(StringUtils.hasText(verifyDto.getRemark()) ? verifyDto.getRemark() : "医嘱退回");
|
||||
refundLog.setCreateTime(new Date());
|
||||
refundLogMapper.insert(refundLog);
|
||||
|
||||
// 3. 如有发药明细,回滚其发药状态(仅在未发药时才需要,此处已在上层校验过)
|
||||
List<DispensingDetail> dispensingDetails = dispensingDetailMapper
|
||||
.selectByOrderMainId(orderMain.getId());
|
||||
for (DispensingDetail detail : dispensingDetails) {
|
||||
// 只在状态为未发药时回滚,防止误改已发药记录
|
||||
if (DispenseStatus.UNDISPENSED.getCode().equals(detail.getDispenseStatus())) {
|
||||
detail.setDispenseStatus(DispenseStatus.RETURNED.getCode());
|
||||
detail.setUpdateTime(new Date());
|
||||
dispensingDetailMapper.updateByPrimaryKeySelective(detail);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 业务日志
|
||||
logger.info("医嘱退回成功,orderMainId={}, operatorId={}", orderMain.getId(), verifyDto.getOperatorId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行通过校对的内部实现(保持原有业务不变)。
|
||||
*
|
||||
* @param orderMain 订单主记录
|
||||
* @param verifyDto 校对信息
|
||||
*/
|
||||
private void performPassOperation(OrderMain orderMain, OrderVerifyDto verifyDto) {
|
||||
// 示例实现:仅更新状态为已通过校对
|
||||
orderMain.setOrderStatus(OrderStatus.VERIFIED.getCode());
|
||||
orderMain.setVerifyTime(new Date());
|
||||
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
||||
logger.info("医嘱校对通过,orderMainId={}, operatorId={}", orderMain.getId(), verifyDto.getOperatorId());
|
||||
}
|
||||
|
||||
// 其余业务方法保持不变...
|
||||
// 其它业务方法省略
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user