fix(ops): 修复队列脏数据导致工单永远排队无法派发
问题:autoCompleteOrder 直接调用 orderStateMachine.forceTransition, 绕过责任链导致队列记录残留 WAITING,autoDispatchNext 反复命中脏数据 失败,人员状态永远 BUSY,新工单全部 ENQUEUE_ONLY 形成死循环。 修复: 1. SecurityOrderServiceImpl 所有状态操作统一走 OrderLifecycleManager, 移除对 OrderStateMachine 的直接依赖 2. autoDispatchNext 增加循环遍历 + 工单状态校验,跳过并清理非 QUEUED 的脏队列记录,增加 maxSkip=50 防护上限 3. forceComplete 返回值校验,失败时抛异常而非静默继续 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,6 @@ import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import com.viewsh.module.ops.enums.OperatorTypeEnum;
|
||||
import com.viewsh.module.ops.enums.OrderQueueStatusEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import com.viewsh.module.ops.infrastructure.log.annotation.BusinessLog;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.LogScope;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.LogType;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -76,17 +73,6 @@ public class DispatchEngineImpl implements DispatchEngine {
|
||||
// ==================== 核心调度方法 ====================
|
||||
|
||||
@Override
|
||||
@BusinessLog(
|
||||
type = LogType.ORDER_DISPATCHED,
|
||||
scope = LogScope.ORDER,
|
||||
description = "工单自动派发",
|
||||
includeParams = true,
|
||||
includeResult = true,
|
||||
result = "#result.success",
|
||||
params = {"#context.orderId", "#context.businessType", "#context.priority"},
|
||||
targetId = "#context.orderId",
|
||||
targetType = "order"
|
||||
)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DispatchResult dispatch(OrderDispatchContext context) {
|
||||
log.info("开始调度工单: orderId={}, businessType={}, areaId={}, priority={}",
|
||||
@@ -202,33 +188,60 @@ public class DispatchEngineImpl implements DispatchEngine {
|
||||
|
||||
if (waitingTasks.isEmpty()) {
|
||||
log.info("无等待任务,执行人变为空闲: assigneeId={}", assigneeId);
|
||||
// 发布事件,由业务层更新执行人状态
|
||||
return DispatchResult.success("无等待工单,执行人保持空闲", assigneeId);
|
||||
}
|
||||
|
||||
// 动态总分重排后,派发得分最低的等待工单
|
||||
OrderQueueDTO nextTask = waitingTasks.get(0);
|
||||
log.info("派发下一单: queueId={}, orderId={}, score={}, floorDiff={}, waitMinutes={}",
|
||||
nextTask.getId(), nextTask.getOpsOrderId(), nextTask.getQueueScore(),
|
||||
nextTask.getFloorDiff(), nextTask.getWaitMinutes());
|
||||
// 遍历等待任务列表,跳过不可派发的脏数据
|
||||
int maxSkip = 50;
|
||||
int skipped = 0;
|
||||
for (OrderQueueDTO nextTask : waitingTasks) {
|
||||
// 防御性校验:检查工单实际状态是否仍为 QUEUED
|
||||
OpsOrderDO order = orderMapper.selectById(nextTask.getOpsOrderId());
|
||||
if (order == null || !WorkOrderStatusEnum.QUEUED.getStatus().equals(order.getStatus())) {
|
||||
// 工单已不在 QUEUED 状态(可能被 forceTransition 直接完成),清理脏队列记录
|
||||
log.warn("跳过不可派发的队列记录: queueId={}, orderId={}, orderStatus={}",
|
||||
nextTask.getId(), nextTask.getOpsOrderId(),
|
||||
order != null ? order.getStatus() : "NOT_FOUND");
|
||||
try {
|
||||
orderQueueService.updateStatus(nextTask.getId(), OrderQueueStatusEnum.REMOVED);
|
||||
} catch (Exception e) {
|
||||
log.error("清理脏队列记录失败: queueId={}", nextTask.getId(), e);
|
||||
}
|
||||
skipped++;
|
||||
if (skipped >= maxSkip) {
|
||||
log.error("跳过脏数据超过上限,终止: assigneeId={}, skipped={}", assigneeId, skipped);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(nextTask.getOpsOrderId())
|
||||
.targetStatus(WorkOrderStatusEnum.DISPATCHED)
|
||||
.queueId(nextTask.getId())
|
||||
.assigneeId(assigneeId)
|
||||
.operatorType(OperatorTypeEnum.SYSTEM)
|
||||
.operatorId(assigneeId)
|
||||
.reason("等待队列动态重排后自动派发")
|
||||
.build();
|
||||
log.info("派发下一单: queueId={}, orderId={}, score={}, floorDiff={}, waitMinutes={}",
|
||||
nextTask.getId(), nextTask.getOpsOrderId(), nextTask.getQueueScore(),
|
||||
nextTask.getFloorDiff(), nextTask.getWaitMinutes());
|
||||
|
||||
OrderTransitionResult result = orderLifecycleManager.transition(request);
|
||||
OrderTransitionRequest request = OrderTransitionRequest.builder()
|
||||
.orderId(nextTask.getOpsOrderId())
|
||||
.targetStatus(WorkOrderStatusEnum.DISPATCHED)
|
||||
.queueId(nextTask.getId())
|
||||
.assigneeId(assigneeId)
|
||||
.operatorType(OperatorTypeEnum.SYSTEM)
|
||||
.operatorId(assigneeId)
|
||||
.reason("等待队列动态重排后自动派发")
|
||||
.build();
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return DispatchResult.success("已按队列总分派发下一单", assigneeId);
|
||||
} else {
|
||||
return DispatchResult.fail("按队列总分派发下一单失败: " + result.getMessage());
|
||||
OrderTransitionResult result = orderLifecycleManager.transition(request);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return DispatchResult.success("已按队列总分派发下一单", assigneeId);
|
||||
} else {
|
||||
log.warn("派发下一单失败,尝试下一条: orderId={}, error={}",
|
||||
nextTask.getOpsOrderId(), result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 所有等待任务都不可派发
|
||||
log.info("所有等待任务均不可派发,执行人变为空闲: assigneeId={}", assigneeId);
|
||||
return DispatchResult.success("无可派发工单,执行人保持空闲", assigneeId);
|
||||
}
|
||||
|
||||
// ==================== 策略注册 ====================
|
||||
|
||||
Reference in New Issue
Block a user