feat(ops): 重构派单队列评分逻辑,支持楼层差与等待老化综合排序
- 新增 QueueScoreCalculator/QueueScoreContext/QueueScoreResult,统一按优先级分 + 楼层差分 - 等待老化分计算队列总分,并将 PRIORITY_WEIGHT 调整为 1500 - OrderQueueService 新增 rebuildWaitingTasksByUserId 接口,OrderQueueServiceEnhanced 支持按执行人重算 WAITING 队列、以当前执行工单楼层为基准动态重排,并在事务提交后同步刷新 Redis - RedisOrderQueueServiceImpl 支持持久化 baseFloorNo、targetFloorNo、floorDiff、waitMinutes、scoreUpdateTime 等评分明细,清队列时同时清理关联 Hash,避免脏数据残留 - DispatchEngineImpl、CleanerPriorityScheduleStrategy、BadgeDeviceScheduleStrategy 调整为非抢占式派单:P0 忙碌时仅入队等待,空闲时直接派发,自动派单前按总分重排并派发下一单 - CleanOrderServiceImpl 取消 P0 自动打断链路,升级到 P0 后仅重算等待队列并发送通知;补充 QueueScoreCalculatorTest、OrderQueueServiceEnhancedTest、CleanerPriorityScheduleStrategyTest、CleanOrderEndToEndTest 覆盖新行为
This commit is contained in:
@@ -9,10 +9,13 @@ import com.viewsh.module.ops.core.event.OrderEventPublisher;
|
||||
import com.viewsh.module.ops.core.lifecycle.OrderLifecycleManager;
|
||||
import com.viewsh.module.ops.core.lifecycle.model.OrderTransitionRequest;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO;
|
||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import com.viewsh.module.ops.enums.PriorityEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.workorder.OpsOrderCleanExtDO;
|
||||
import com.viewsh.module.ops.environment.dal.redis.TrafficActiveOrderRedisDAO;
|
||||
import com.viewsh.module.ops.environment.dal.mysql.workorder.OpsOrderCleanExtMapper;
|
||||
import com.viewsh.module.ops.environment.integration.consumer.*;
|
||||
import com.viewsh.framework.common.pojo.CommonResult;
|
||||
@@ -73,6 +76,8 @@ public class CleanOrderEndToEndTest {
|
||||
@Mock
|
||||
private OpsOrderMapper opsOrderMapper;
|
||||
@Mock
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
@Mock
|
||||
private OpsOrderCleanExtMapper cleanExtMapper;
|
||||
@Mock
|
||||
private OrderIdGenerator orderIdGenerator;
|
||||
@@ -94,6 +99,8 @@ public class CleanOrderEndToEndTest {
|
||||
private ValueOperations<String, String> valueOperations;
|
||||
@Mock
|
||||
private VoiceBroadcastService voiceBroadcastService;
|
||||
@Mock
|
||||
private TrafficActiveOrderRedisDAO trafficActiveOrderRedisDAO;
|
||||
|
||||
@Mock
|
||||
private BadgeDeviceStatusService badgeDeviceStatusService;
|
||||
@@ -149,6 +156,7 @@ public class CleanOrderEndToEndTest {
|
||||
|
||||
// 注入 CleanOrderEventListener
|
||||
injectField(cleanOrderService, "cleanOrderEventListener", cleanOrderEventListener);
|
||||
injectField(cleanOrderService, "opsBusAreaMapper", opsBusAreaMapper);
|
||||
|
||||
// 注入 CleanOrderAuditEventHandler 依赖
|
||||
injectField(auditEventHandler, "eventLogRecorder", eventLogRecorder);
|
||||
@@ -156,10 +164,18 @@ public class CleanOrderEndToEndTest {
|
||||
injectField(auditEventHandler, "opsOrderMapper", opsOrderMapper);
|
||||
injectField(auditEventHandler, "stringRedisTemplate", stringRedisTemplate);
|
||||
injectField(auditEventHandler, "objectMapper", objectMapper);
|
||||
injectField(createEventHandler, "trafficActiveOrderRedisDAO", trafficActiveOrderRedisDAO);
|
||||
|
||||
// Stub IotDeviceControlApi for resetTrafficCounter
|
||||
lenient().when(iotDeviceControlApi.resetTrafficCounter(any()))
|
||||
.thenReturn(CommonResult.success(true));
|
||||
lenient().when(opsBusAreaMapper.selectById(anyLong()))
|
||||
.thenAnswer(i -> OpsBusAreaDO.builder()
|
||||
.id(i.getArgument(0))
|
||||
.areaName("测试区域")
|
||||
.parentPath(null)
|
||||
.floorNo(1)
|
||||
.build());
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
@@ -338,7 +354,7 @@ public class CleanOrderEndToEndTest {
|
||||
verify(eventLogRecorder).record(any());
|
||||
|
||||
// 2. TTS sent (orderId can be null for TTS_REQUEST events)
|
||||
verify(voiceBroadcastService).broadcastInOrder(eq(5001L), contains("请回到作业区域"), eq((Long) null));
|
||||
verify(voiceBroadcastService).broadcastDirect(eq(5001L), contains("请回到作业区域"), eq(9), eq((Long) null));
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
@@ -378,9 +394,6 @@ public class CleanOrderEndToEndTest {
|
||||
|
||||
when(opsOrderMapper.selectById(orderId)).thenReturn(order);
|
||||
when(orderQueueService.getByOpsOrderId(orderId)).thenReturn(queueDTO);
|
||||
when(dispatchEngine.urgentInterrupt(eq(orderId), eq(2001L)))
|
||||
.thenReturn(DispatchResult.success("Success", 2001L));
|
||||
|
||||
// Execute
|
||||
boolean result = cleanOrderService.upgradePriorityToP0(orderId, "Manual Upgrade");
|
||||
|
||||
@@ -392,7 +405,7 @@ public class CleanOrderEndToEndTest {
|
||||
assertEquals(PriorityEnum.P0.getPriority(), orderCaptor.getValue().getPriority());
|
||||
|
||||
verify(orderQueueService).adjustPriority(eq(500L), eq(PriorityEnum.P0), anyString());
|
||||
verify(dispatchEngine).urgentInterrupt(orderId, 2001L);
|
||||
verify(orderQueueService).rebuildWaitingTasksByUserId(2001L, order.getAreaId());
|
||||
verify(cleanOrderEventListener).sendPriorityUpgradeNotification(eq(2001L), eq("WO-P2"), eq(orderId));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class CleanerPriorityScheduleStrategyTest {
|
||||
private com.viewsh.module.ops.core.dispatch.DispatchEngine dispatchEngine;
|
||||
|
||||
@Test
|
||||
void testDecide_P0_Interrupt() {
|
||||
void testDecide_P0_EnqueueOnlyWhenBusy() {
|
||||
// Setup
|
||||
OpsCleanerStatusDO c1 = new OpsCleanerStatusDO();
|
||||
c1.setUserId(1L);
|
||||
@@ -54,8 +54,7 @@ class CleanerPriorityScheduleStrategyTest {
|
||||
DispatchDecision decision = strategy.decide(context);
|
||||
|
||||
// Verify
|
||||
assertEquals(DispatchPath.INTERRUPT_AND_DISPATCH, decision.getPath());
|
||||
assertEquals(500L, decision.getInterruptedOrderId());
|
||||
assertEquals(DispatchPath.ENQUEUE_ONLY, decision.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user