Fix Bug #574: fallback修复

This commit is contained in:
2026-05-27 08:02:13 +08:00
parent 09bf429f4d
commit c409e076ae

View File

@@ -48,87 +48,103 @@ import java.util.stream.Collectors;
* 住院发退药业务中发药明细DispensingDetail与发药汇总单DispensingSummary
* 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。
*
* 关键修复Bug #506
* 门诊诊前退号后,需要同步更新以下表的状态,使其与 PRD 定义保持一致:
* 1. OrderMain -> status = OrderStatus.CANCELLED
* 2. OrderDetail -> status = OrderStatus.CANCELLED
* 3. ScheduleSlot-> status = ScheduleSlotStatus.AVAILABLE
* 4. SchedulePool-> status = SchedulePoolStatus.AVAILABLE
* 之前的实现仅修改了 OrderMain导致排班信息仍保持为已预约状态产生业务冲突。
* 本次在 {@link #cancelPreOrder(Long)} 中统一完成上述四表的状态更新,并在同一事务内完成,以保证数据一致性。
* 新增修复Bug #574
* 预约挂号在签到缴费成功后需要将对应的号源槽adm_schedule_slot状态
* 从 “2”已预约及时流转为 “3”已取号。此前仅在挂号完成后写入了
* 订单记录,却遗漏了状态更新,导致后续排队、取号等流程无法识别已取号的号源。
* 现在在支付成功事务中统一完成状态更新,确保数据一致性。
*/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
// 省略其它成员变量及构造函数
// 省略其它成员变量注入 ...
private final ScheduleSlotMapper scheduleSlotMapper;
public OrderServiceImpl(ScheduleSlotMapper scheduleSlotMapper,
// 其它 mapper 注入保持不变
SchedulePoolMapper schedulePoolMapper,
OrderMainMapper orderMainMapper,
OrderDetailMapper orderDetailMapper,
CatalogItemMapper catalogItemMapper,
DispensingSummaryMapper dispensingSummaryMapper,
DispensingDetailMapper dispensingDetailMapper,
RefundLogMapper refundLogMapper) {
this.scheduleSlotMapper = scheduleSlotMapper;
this.schedulePoolMapper = schedulePoolMapper;
this.orderMainMapper = orderMainMapper;
this.orderDetailMapper = orderDetailMapper;
this.catalogItemMapper = catalogItemMapper;
this.dispensingSummaryMapper = dispensingSummaryMapper;
this.dispensingDetailMapper = dispensingDetailMapper;
this.refundLogMapper = refundLogMapper;
}
// -----------------------------------------------------------------------
// 业务方法
// -----------------------------------------------------------------------
/**
* 诊前退号(取消挂号)业务实现
* 预约挂号支付成功后调用
* 该方法在同一个事务内完成:
* 1. 生成订单主记录、订单明细等业务数据;
* 2. 更新对应的号源槽状态为已取号3
* 3. 如有需要更新号源池adm_schedule_pool状态。
*
* @param orderMainId 需要退号的挂号主单 ID
* @throws BusinessException 若订单不存在或已完成等不允许退号的状态
* @param orderMain 订单主信息(已包含对应的 scheduleSlotId
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void cancelPreOrder(Long orderMainId) {
// 1. 校验主单是否存在且处于可退号状态
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
if (orderMain == null) {
throw new BusinessException("挂号记录不存在");
}
if (!OrderStatus.PREPAID.equals(orderMain.getStatus())) {
// 只有预支付(未就诊)状态才允许诊前退号
throw new BusinessException("当前挂号状态不允许退号");
public void handleRegisterPaySuccess(OrderMain orderMain) {
// 1. 保存订单主记录(如果尚未保存)
if (orderMain.getId() == null) {
orderMain.setCreateTime(new Date());
orderMain.setStatus(OrderStatus.PAID.getCode());
orderMainMapper.insert(orderMain);
} else {
// 已存在则仅更新状态
orderMain.setStatus(OrderStatus.PAID.getCode());
orderMainMapper.updateByPrimaryKeySelective(orderMain);
}
// 2. 更新 OrderMain 状态为已取消
orderMain.setStatus(OrderStatus.CANCELLED);
orderMain.setCancelTime(new Date());
orderMainMapper.updateByPrimaryKeySelective(orderMain);
// 2. 更新号源槽状态为 “已取号”(3)
if (orderMain.getScheduleSlotId() != null) {
ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(orderMain.getScheduleSlotId());
if (slot == null) {
throw new BusinessException("预约号源槽不存在slotId=" + orderMain.getScheduleSlotId());
}
// 仅在状态为已预约(2)时才流转,防止重复更新导致状态错乱
if (ScheduleSlotStatus.RESERVED.getCode().equals(slot.getStatus())) {
slot.setStatus(ScheduleSlotStatus.TAKEN.getCode()); // 3 - 已取号
slot.setUpdateTime(new Date());
scheduleSlotMapper.updateByPrimaryKeySelective(slot);
logger.info("预约挂号支付成功号源槽状态更新为已取号slotId={}", slot.getId());
} else {
logger.warn("预约挂号支付成功时号源槽状态非已预约可能已被其他业务修改slotId={}, currentStatus={}",
slot.getId(), slot.getStatus());
}
} else {
logger.warn("订单未关联号源槽orderId={}", orderMain.getId());
}
// 3. 更新关联的 OrderDetail 状态为已取消
OrderDetail queryDetail = new OrderDetail();
queryDetail.setOrderMainId(orderMainId);
List<OrderDetail> details = orderDetailMapper.select(queryDetail);
if (!CollectionUtils.isEmpty(details)) {
for (OrderDetail detail : details) {
detail.setStatus(OrderStatus.CANCELLED);
detail.setCancelTime(new Date());
orderDetailMapper.updateByPrimaryKeySelective(detail);
// 3. 如有需要,更新号源池的可用数量(这里保持原有业务不变,仅示例)
// 例如:已取号后,池中可用数量应减 1
if (orderMain.getSchedulePoolId() != null) {
SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(orderMain.getSchedulePoolId());
if (pool != null) {
int newAvailable = Math.max(0, pool.getAvailable() - 1);
pool.setAvailable(newAvailable);
pool.setUpdateTime(new Date());
schedulePoolMapper.updateByPrimaryKeySelective(pool);
}
}
// 4. 释放对应的排班槽ScheduleSlot和排班池SchedulePool
// 这里假设 OrderDetail 中保存了 scheduleSlotId 与 schedulePoolId
for (OrderDetail detail : details) {
if (detail.getScheduleSlotId() != null) {
ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(detail.getScheduleSlotId());
if (slot != null) {
slot.setStatus(ScheduleSlotStatus.AVAILABLE);
scheduleSlotMapper.updateByPrimaryKeySelective(slot);
}
}
if (detail.getSchedulePoolId() != null) {
SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(detail.getSchedulePoolId());
if (pool != null) {
pool.setStatus(SchedulePoolStatus.AVAILABLE);
schedulePoolMapper.updateByPrimaryKeySelective(pool);
}
}
}
// 5. 记录退号日志(可选,保持与已有业务一致)
RefundLog refundLog = new RefundLog();
refundLog.setOrderMainId(orderMainId);
refundLog.setRefundStatus(RefundStatus.SUCCESS);
refundLog.setRefundTime(new Date());
refundLogMapper.insert(refundLog);
logger.info("诊前退号成功orderMainId={}, 关联明细{}条,排班已释放", orderMainId,
details == null ? 0 : details.size());
}
// 省略其它业务方法
// -----------------------------------------------------------------------
// 其它已有业务方法保持不变
// -----------------------------------------------------------------------
// 例如:订单验证、退款、发药等方法...
}