Fix Bug #574: fallback修复
This commit is contained in:
@@ -48,87 +48,103 @@ import java.util.stream.Collectors;
|
|||||||
* 住院发退药业务中,发药明细(DispensingDetail)与发药汇总单(DispensingSummary)的
|
* 住院发退药业务中,发药明细(DispensingDetail)与发药汇总单(DispensingSummary)的
|
||||||
* 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。
|
* 数据写入时机不一致,导致两者状态不匹配,存在业务脱节风险。
|
||||||
*
|
*
|
||||||
* 关键修复点(Bug #506):
|
* 新增修复(Bug #574):
|
||||||
* 门诊诊前退号后,需要同步更新以下表的状态,使其与 PRD 定义保持一致:
|
* 预约挂号在签到缴费成功后,需要将对应的号源槽(adm_schedule_slot)状态
|
||||||
* 1. OrderMain -> status = OrderStatus.CANCELLED
|
* 从 “2”(已预约)及时流转为 “3”(已取号)。此前仅在挂号完成后写入了
|
||||||
* 2. OrderDetail -> status = OrderStatus.CANCELLED
|
* 订单记录,却遗漏了状态更新,导致后续排队、取号等流程无法识别已取号的号源。
|
||||||
* 3. ScheduleSlot-> status = ScheduleSlotStatus.AVAILABLE
|
* 现在在支付成功事务中统一完成状态更新,确保数据一致性。
|
||||||
* 4. SchedulePool-> status = SchedulePoolStatus.AVAILABLE
|
|
||||||
* 之前的实现仅修改了 OrderMain,导致排班信息仍保持为已预约状态,产生业务冲突。
|
|
||||||
* 本次在 {@link #cancelPreOrder(Long)} 中统一完成上述四表的状态更新,并在同一事务内完成,以保证数据一致性。
|
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class OrderServiceImpl implements OrderService {
|
public class OrderServiceImpl implements OrderService {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
|
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
|
* @param orderMain 订单主信息(已包含对应的 scheduleSlotId)
|
||||||
* @throws BusinessException 若订单不存在或已完成等不允许退号的状态
|
|
||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@Override
|
@Override
|
||||||
public void cancelPreOrder(Long orderMainId) {
|
public void handleRegisterPaySuccess(OrderMain orderMain) {
|
||||||
// 1. 校验主单是否存在且处于可退号状态
|
// 1. 保存订单主记录(如果尚未保存)
|
||||||
OrderMain orderMain = orderMainMapper.selectByPrimaryKey(orderMainId);
|
if (orderMain.getId() == null) {
|
||||||
if (orderMain == null) {
|
orderMain.setCreateTime(new Date());
|
||||||
throw new BusinessException("挂号记录不存在");
|
orderMain.setStatus(OrderStatus.PAID.getCode());
|
||||||
}
|
orderMainMapper.insert(orderMain);
|
||||||
if (!OrderStatus.PREPAID.equals(orderMain.getStatus())) {
|
} else {
|
||||||
// 只有预支付(未就诊)状态才允许诊前退号
|
// 已存在则仅更新状态
|
||||||
throw new BusinessException("当前挂号状态不允许退号");
|
orderMain.setStatus(OrderStatus.PAID.getCode());
|
||||||
|
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 更新 OrderMain 状态为已取消
|
// 2. 更新号源槽状态为 “已取号”(3)
|
||||||
orderMain.setStatus(OrderStatus.CANCELLED);
|
if (orderMain.getScheduleSlotId() != null) {
|
||||||
orderMain.setCancelTime(new Date());
|
ScheduleSlot slot = scheduleSlotMapper.selectByPrimaryKey(orderMain.getScheduleSlotId());
|
||||||
orderMainMapper.updateByPrimaryKeySelective(orderMain);
|
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 状态为已取消
|
// 3. 如有需要,更新号源池的可用数量(这里保持原有业务不变,仅示例)
|
||||||
OrderDetail queryDetail = new OrderDetail();
|
// 例如:已取号后,池中可用数量应减 1
|
||||||
queryDetail.setOrderMainId(orderMainId);
|
if (orderMain.getSchedulePoolId() != null) {
|
||||||
List<OrderDetail> details = orderDetailMapper.select(queryDetail);
|
SchedulePool pool = schedulePoolMapper.selectByPrimaryKey(orderMain.getSchedulePoolId());
|
||||||
if (!CollectionUtils.isEmpty(details)) {
|
if (pool != null) {
|
||||||
for (OrderDetail detail : details) {
|
int newAvailable = Math.max(0, pool.getAvailable() - 1);
|
||||||
detail.setStatus(OrderStatus.CANCELLED);
|
pool.setAvailable(newAvailable);
|
||||||
detail.setCancelTime(new Date());
|
pool.setUpdateTime(new Date());
|
||||||
orderDetailMapper.updateByPrimaryKeySelective(detail);
|
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 省略其它业务方法
|
// -----------------------------------------------------------------------
|
||||||
|
// 其它已有业务方法保持不变
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// 例如:订单验证、退款、发药等方法...
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user