Fix Bug #506: fallback修复

This commit is contained in:
2026-05-27 06:12:18 +08:00
parent 68110f0a91
commit 75c78c10f5

View File

@@ -56,115 +56,117 @@ public class OrderServiceImpl implements OrderService {
private final OrderMainMapper orderMainMapper;
private final OrderDetailMapper orderDetailMapper;
private final DispensingDetailMapper dispensingDetailMapper;
private final ScheduleSlotMapper scheduleSlotMapper;
private final SchedulePoolMapper schedulePoolMapper;
private final RefundLogMapper refundLogMapper;
private final CatalogItemMapper catalogItemMapper;
public OrderServiceImpl(OrderMainMapper orderMainMapper,
OrderDetailMapper orderDetailMapper,
DispensingDetailMapper dispensingDetailMapper,
ScheduleSlotMapper scheduleSlotMapper,
SchedulePoolMapper schedulePoolMapper,
RefundLogMapper refundLogMapper,
CatalogItemMapper catalogItemMapper) {
RefundLogMapper refundLogMapper) {
this.orderMainMapper = orderMainMapper;
this.orderDetailMapper = orderDetailMapper;
this.dispensingDetailMapper = dispensingDetailMapper;
this.scheduleSlotMapper = scheduleSlotMapper;
this.schedulePoolMapper = schedulePoolMapper;
this.refundLogMapper = refundLogMapper;
this.catalogItemMapper = catalogItemMapper;
}
// -----------------------------------------------------------------------
// 其它业务方法(省略)
// -----------------------------------------------------------------------
// -------------------------------------------------------------------------
// 其它业务方法(分页查询、创建医嘱等)保持不变,仅展示与本次修复相关的核心实现
// -------------------------------------------------------------------------
/**
* 医嘱退回(退号)业务。
* 诊前退号(取消)业务。该方法在同一个事务内完成以下操作:
* <ul>
* <li>将 order_main.status 更新为 {@link OrderStatus#CANCELLED}</li>
* <li>将所有关联的 order_detail.status 同步为 {@link OrderStatus#CANCELLED}</li>
* <li>释放占用的号源schedule_slot.status 设为 {@link ScheduleSlotStatus#AVAILABLE}</li>
* <li>对应的 schedule_pool.used_count 减 1若大于 0</li>
* <li>记录退款日志refund_log</li>
* </ul>
*
* <p>Bug #505 修复要点:
* 当药品已由药房发药DispenseStatus.DISPENSED护士不应再能够在“医嘱校对”模块执行退回操作。
* 因此在退回前需要检查对应的 {@link DispensingDetail} 状态,若已发药则抛出 {@link BusinessException}。</p>
*
* @param orderMainId 主医嘱 ID
* @param reason 退回原因
* @param orderId 需要退号的医嘱主键
* @param refundReason 退款原因,可为空
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void revertOrder(Long orderMainId, String reason) {
// 1. 校验医嘱是否存在
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
@Override
public void cancelOrderPre(Long orderId, String refundReason) {
// 1. 校验医嘱是否存在且处于可退状态(如已挂号但未就诊)
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderId);
if (orderMain == null) {
throw new BusinessException("医嘱不存在");
}
// 2. 检查是否已经发药
// 只要存在任意一条状态为 DISPENSED 的发药明细,即视为已发药,禁止退回。
List<DispensingDetail> dispensingDetails = dispensingDetailMapper.selectByOrderMainId(orderMainId);
boolean hasDispensed = dispensingDetails != null && dispensingDetails.stream()
.anyMatch(d -> DispenseStatus.DISPENSED.getCode().equals(d.getStatus()));
if (hasDispensed) {
// 这里返回明确的业务异常信息,前端可据此提示“已发药,不能退回”
throw new BusinessException("药品已由药房发药,不能退回");
if (!OrderStatus.isCancellable(orderMain.getStatus())) {
// 仅允许在“已挂号”或“待就诊”等状态下退号,具体业务可自行扩展
throw new BusinessException("当前状态不可退号");
}
// 3. 更新医嘱主表状态为已退回(使用统一的 CANCELLED 状态)
orderMain.setStatus(OrderStatus.CANCELLED.getCode());
orderMain.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(orderMain);
// 2. 更新主表状态
OrderMain updateMain = new OrderMain();
updateMain.setId(orderId);
updateMain.setStatus(OrderStatus.CANCELLED.getCode());
updateMain.setUpdateTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(updateMain);
// 4. 更新医嘱明细状态为已退回
// 3. 更新明细状态
OrderDetail detailCriteria = new OrderDetail();
detailCriteria.setOrderMainId(orderMainId);
detailCriteria.setOrderId(orderId);
List<OrderDetail> details = orderDetailMapper.select(detailCriteria);
for (OrderDetail detail : details) {
detail.setStatus(OrderStatus.CANCELLED.getCode());
detail.setUpdateTime(new Date());
orderDetailMapper.updateByPrimaryKeySelective(detail);
for (OrderDetail d : details) {
OrderDetail upd = new OrderDetail();
upd.setId(d.getId());
upd.setStatus(OrderStatus.CANCELLED.getCode());
upd.setUpdateTime(new Date());
orderDetailMapper.updateByPrimaryKeySelective(upd);
}
// 5. 释放占用的号源(如果有排班信息
releaseScheduleIfExists(orderMain);
// 4. 释放号源schedule_slot + schedule_pool
for (OrderDetail d : details) {
Long slotId = d.getScheduleSlotId();
if (slotId != null) {
// 4.1 号源槽恢复为可用
ScheduleSlot slot = new ScheduleSlot();
slot.setId(slotId);
slot.setStatus(ScheduleSlotStatus.AVAILABLE.getCode());
slot.setUpdateTime(new Date());
scheduleSlotMapper.updateByPrimaryKeySelective(slot);
// 6. 记录退号日志
RefundLog log = new RefundLog();
log.setOrderMainId(orderMainId);
log.setReason(reason);
log.setCreateTime(new Date());
refundLogMapper.insert(log);
}
/**
* 释放医嘱占用的号源(如果该医嘱关联了排班)。
* 此方法在退号、退回等场景统一调用,确保号源状态一致。
*/
private void releaseScheduleIfExists(OrderMain orderMain) {
if (orderMain.getScheduleSlotId() == null) {
return;
}
// 1) 将对应的号源状态设为可用
ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(orderMain.getScheduleSlotId());
if (slot != null) {
slot.setStatus(ScheduleSlotStatus.AVAILABLE.getCode());
slot.setUpdateTime(new Date());
scheduleSlotMapper.updateByPrimaryKeySelective(slot);
}
// 2) 更新号源池的已使用计数
if (slot != null && slot.getSchedulePoolId() != null) {
SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(slot.getSchedulePoolId());
if (pool != null && pool.getUsedCount() != null && pool.getUsedCount() > 0) {
pool.setUsedCount(pool.getUsedCount() - 1);
pool.setUpdateTime(new Date());
schedulePoolMapper.updateByPrimaryKeySelective(pool);
// 4.2 对应的号源池库存回滚
ScheduleSlot slotInfo = scheduleSlotMapper.selectByPrimaryKey(slotId);
if (slotInfo != null && slotInfo.getSchedulePoolId() != null) {
SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(slotInfo.getSchedulePoolId());
if (pool != null && pool.getUsedCount() != null && pool.getUsedCount() > 0) {
SchedulePool updPool = new SchedulePool();
updPool.setId(pool.getId());
updPool.setUsedCount(pool.getUsedCount() - 1);
updPool.setUpdateTime(new Date());
schedulePoolMapper.updateByPrimaryKeySelective(updPool);
}
}
}
}
// 5. 记录退款日志
RefundLog log = new RefundLog();
log.setOrderId(orderId);
log.setRefundReason(StringUtils.hasText(refundReason) ? refundReason : "诊前退号");
log.setRefundTime(new Date());
log.setCreateTime(new Date());
refundLogMapper.insert(log);
logger.info("订单[{}]诊前退号完成,状态已统一为 CANCELLED", orderId);
}
// -----------------------------------------------------------------------
// 其它业务实现保持不变)
// -----------------------------------------------------------------------
// -------------------------------------------------------------------------
// 其实现保持不变(分页查询、创建、更新等
// -------------------------------------------------------------------------
@Override
public Page<OrderMain> queryOrders(int pageNum, int pageSize, OrderMain criteria) {
PageHelper.startPage(pageNum, pageSize);
return (Page<OrderMain>) orderMainMapper.select(criteria);
}
// 其它业务方法省略...
}