refactor(ops): 服务层调整,统一使用设备ID
Some checks failed
Java CI with Maven / build (11) (push) Has been cancelled
Java CI with Maven / build (17) (push) Has been cancelled
Java CI with Maven / build (8) (push) Has been cancelled

- CleanOrderService: 将所有 cleanerId 参数改为 deviceId
- CleanOrderAutoCreateReqDTO: 新增 triggerData 字段
- EventPublishHandler: 添加 assigneeId/urgentOrderId 到 payload
- OrderQueueService: 修正注释说明 userId 实际为 deviceId
- EventDomain: 新增 AUDIT 域
- 更新测试用例适配接口变更

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-01-25 18:23:37 +08:00
parent 373f379b01
commit a8d3fc84d2
7 changed files with 108 additions and 77 deletions

View File

@@ -8,6 +8,8 @@ import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
/**
* 保洁自动工单创建请求 DTO
*
@@ -44,4 +46,7 @@ public class CleanOrderAutoCreateReqDTO extends OpsOrderCreateReqDTO {
@Schema(description = "触发设备Key冗余便于查询")
private String triggerDeviceKey;
@Schema(description = "触发数据(如客流值、阈值等,用于后续处理)")
private Map<String, Object> triggerData;
}

View File

@@ -59,24 +59,24 @@ public interface CleanOrderService {
// ========== 保洁特有的状态转换 ==========
/**
* 保洁员按键确认工单
* 设备按键确认工单
* 状态转换DISPATCHED → CONFIRMED
* 同时更新保洁员状态IDLE → BUSY关键确认后才变为忙碌
* 设备状态保持 BUSY在 DISPATCHED 时已设置
*
* @param orderId 工单ID
* @param cleanerId 保洁员ID
* @param orderId 工单ID
* @param deviceId 工牌设备ID
*/
void confirmOrder(Long orderId, Long cleanerId);
void confirmOrder(Long orderId, Long deviceId);
/**
* 感知信标,开始作业
* 状态转换CONFIRMED → ARRIVED
*
* @param orderId 工单ID
* @param cleanerId 保洁员ID
* @param beaconId 信标ID
* @param orderId 工单ID
* @param deviceId 工牌设备ID
* @param beaconId 信标ID
*/
void startWorkingOnBeacon(Long orderId, Long cleanerId, Long beaconId);
void startWorkingOnBeacon(Long orderId, Long deviceId, Long beaconId);
/**
* 丢失信号,自动完成
@@ -84,11 +84,11 @@ public interface CleanOrderService {
* 完成后自动推送队列中的下一个任务
*
* @param orderId 工单ID
* @param cleanerId 保洁员ID
* @param beaconId 信标ID
* @param lostSeconds 丢失信号时长(秒)
* @param deviceId 工牌设备ID
* @param beaconId 信标ID
* @param lostSeconds 丢失信号时长(秒)
*/
void autoCompleteOnSignalLost(Long orderId, Long cleanerId, Long beaconId, Integer lostSeconds);
void autoCompleteOnSignalLost(Long orderId, Long deviceId, Long beaconId, Integer lostSeconds);
// ========== 任务自动切换 ==========
@@ -96,37 +96,38 @@ public interface CleanOrderService {
* 任务完成后,自动推送队列中的下一个任务
*
* @param completedOrderId 已完成的工单ID
* @param cleanerId 保洁员ID
* @param deviceId 工牌设备ID
*/
void autoDispatchNextOrder(Long completedOrderId, Long cleanerId);
void autoDispatchNextOrder(Long completedOrderId, Long deviceId);
// ========== 语音播报(支持去重合并) ==========
/**
* 语音播报:空闲保洁员收到新工单
* 语音播报:空闲设备收到新工单
*
* @param cleanerId 保洁员ID
* @param deviceId 工牌设备ID
*/
void playVoiceForNewOrder(Long cleanerId);
void playVoiceForNewOrder(Long deviceId);
/**
* 语音播报:忙碌保洁员收到待办(支持去重合并)
* 语音播报:忙碌设备收到待办(支持去重合并)
* 例如:"新增3项待办您共有5个待办工单"
*
* @param cleanerId 保洁员ID
* @param deviceId 工牌设备ID
* @param queueCount 当前待办数量
*/
void playVoiceForQueuedOrder(Long cleanerId, int queueCount);
void playVoiceForQueuedOrder(Long deviceId, int queueCount);
/**
* 语音播报:任务完成后自动推送下一个
* 例如:"待办工单总数2个第一位待办工单xxx"
*
* @param cleanerId 保洁员ID
* @param deviceId 工牌设备ID
* @param queueCount 待办数量
* @param nextTaskTitle 下一个任务标题
*/
void playVoiceForNextTask(Long cleanerId, int queueCount, String nextTaskTitle);
void playVoiceForNextTask(Long deviceId, int queueCount, String nextTaskTitle);
// ========== 优先级管理 ==========

View File

@@ -4,7 +4,6 @@ import com.viewsh.module.ops.api.queue.OrderQueueDTO;
import com.viewsh.module.ops.api.queue.OrderQueueService;
import com.viewsh.module.ops.core.dispatch.DispatchEngine;
import com.viewsh.module.ops.core.dispatch.model.DispatchResult;
import com.viewsh.module.ops.core.dispatch.model.OrderDispatchContext;
import com.viewsh.module.ops.core.event.OrderCreatedEvent;
import com.viewsh.module.ops.core.event.OrderEventPublisher;
import com.viewsh.module.ops.core.lifecycle.OrderLifecycleManager;
@@ -120,7 +119,7 @@ public class CleanOrderServiceImpl implements CleanOrderService {
log.info("创建自动保洁工单成功: orderId={}, orderCode={}, expectedDuration={}分钟, triggerSource={}",
orderId, orderCode, createReq.getExpectedDuration(), createReq.getTriggerSource());
// 5. 发布工单创建事件,由 CleanOrderEventListener 触发调度
// 5. 发布工单创建事件
OrderCreatedEvent event = OrderCreatedEvent.builder()
.orderId(orderId)
.orderType("CLEAN")
@@ -132,7 +131,14 @@ public class CleanOrderServiceImpl implements CleanOrderService {
.build()
.addPayload("isAuto", true)
.addPayload("expectedDuration", createReq.getExpectedDuration())
.addPayload("triggerSource", createReq.getTriggerSource());
.addPayload("triggerSource", createReq.getTriggerSource())
.addPayload("triggerDeviceId", createReq.getTriggerDeviceId())
.addPayload("triggerDeviceKey", createReq.getTriggerDeviceKey());
// 添加触发数据(用于客流计数器重置等后续处理)
if (createReq.getTriggerData() != null && !createReq.getTriggerData().isEmpty()) {
event.getPayload().put("triggerData", createReq.getTriggerData());
}
orderEventPublisher.publishOrderCreated(event);
@@ -143,28 +149,28 @@ public class CleanOrderServiceImpl implements CleanOrderService {
@Override
@Transactional(rollbackFor = Exception.class)
public void enqueueOrderOnly(Long orderId, Long cleanerId) {
public void enqueueOrderOnly(Long orderId, Long deviceId) {
// 使用生命周期管理器入队
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.assigneeId(cleanerId)
.assigneeId(deviceId)
.operatorType(OperatorTypeEnum.SYSTEM)
.operatorId(cleanerId)
.operatorId(deviceId)
.reason("执行人忙碌,任务入队")
.build();
orderLifecycleManager.enqueue(request);
log.info("工单已入队: orderId={}, cleanerId={}", orderId, cleanerId);
log.info("工单已入队: orderId={}, deviceId={}", orderId, deviceId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void enqueueAndDispatch(Long orderId, Long cleanerId) {
enqueueAndDispatch(orderId, cleanerId, false);
public void enqueueAndDispatch(Long orderId, Long deviceId) {
enqueueAndDispatch(orderId, deviceId, false);
}
@Transactional(rollbackFor = Exception.class)
public void enqueueAndDispatch(Long orderId, Long cleanerId, boolean isUrgent) {
public void enqueueAndDispatch(Long orderId, Long deviceId, boolean isUrgent) {
// 查询工单
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
@@ -174,25 +180,25 @@ public class CleanOrderServiceImpl implements CleanOrderService {
if (isUrgent) {
// P0紧急任务使用紧急插队
DispatchResult result = dispatchEngine.urgentInterrupt(orderId, cleanerId);
DispatchResult result = dispatchEngine.urgentInterrupt(orderId, deviceId);
log.warn("P0紧急派单结果: orderId={}, success={}", orderId, result.isSuccess());
} else {
// 普通任务:正常派单
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.targetStatus(WorkOrderStatusEnum.DISPATCHED)
.assigneeId(cleanerId)
.assigneeId(deviceId)
.operatorType(OperatorTypeEnum.SYSTEM)
.operatorId(cleanerId)
.operatorId(deviceId)
.reason("自动派单")
.build();
orderLifecycleManager.dispatch(request);
log.info("工单已派发: orderId={}, cleanerId={}", orderId, cleanerId);
log.info("工单已派发: orderId={}, deviceId={}", orderId, deviceId);
}
// 语音播报
cleanOrderEventListener.sendNewOrderNotification(cleanerId, orderId);
cleanOrderEventListener.sendNewOrderNotification(deviceId, orderId);
}
@Override
@@ -266,35 +272,35 @@ public class CleanOrderServiceImpl implements CleanOrderService {
@Override
@Transactional(rollbackFor = Exception.class)
public void confirmOrder(Long orderId, Long cleanerId) {
log.info("保洁员确认工单: orderId={}, cleanerId={}", orderId, cleanerId);
public void confirmOrder(Long orderId, Long deviceId) {
log.info("设备确认工单: orderId={}, deviceId={}", orderId, deviceId);
// 使用生命周期管理器转换状态
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.targetStatus(WorkOrderStatusEnum.CONFIRMED)
.assigneeId(cleanerId)
.assigneeId(deviceId)
.operatorType(OperatorTypeEnum.CLEANER)
.operatorId(cleanerId)
.operatorId(deviceId)
.reason("确认工单")
.build();
orderLifecycleManager.transition(request);
// 注意:保洁员状态更新由 CleanOrderEventListener 处理
// 注意:设备状态更新由 BadgeDeviceStatusServiceImpl 处理
}
@Override
@Transactional(rollbackFor = Exception.class)
public void startWorkingOnBeacon(Long orderId, Long cleanerId, Long beaconId) {
log.info("感知信标,开始作业: orderId={}, cleanerId={}, beaconId={}", orderId, cleanerId, beaconId);
public void startWorkingOnBeacon(Long orderId, Long deviceId, Long beaconId) {
log.info("感知信标,开始作业: orderId={}, deviceId={}, beaconId={}", orderId, deviceId, beaconId);
// 使用生命周期管理器转换状态
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.targetStatus(WorkOrderStatusEnum.ARRIVED)
.assigneeId(cleanerId)
.assigneeId(deviceId)
.operatorType(OperatorTypeEnum.SYSTEM)
.operatorId(cleanerId)
.operatorId(deviceId)
.reason("感知信标,开始作业")
.build();
request.putPayload("beaconId", beaconId);
@@ -307,17 +313,17 @@ public class CleanOrderServiceImpl implements CleanOrderService {
@Override
@Transactional(rollbackFor = Exception.class)
public void autoCompleteOnSignalLost(Long orderId, Long cleanerId, Long beaconId, Integer lostSeconds) {
log.info("丢失信号,自动完成工单: orderId={}, cleanerId={}, lostSeconds={}秒",
orderId, cleanerId, lostSeconds);
public void autoCompleteOnSignalLost(Long orderId, Long deviceId, Long beaconId, Integer lostSeconds) {
log.info("丢失信号,自动完成工单: orderId={}, deviceId={}, lostSeconds={}秒",
orderId, deviceId, lostSeconds);
// 使用生命周期管理器转换状态
OrderTransitionRequest request = OrderTransitionRequest.builder()
.orderId(orderId)
.targetStatus(WorkOrderStatusEnum.COMPLETED)
.assigneeId(cleanerId)
.assigneeId(deviceId)
.operatorType(OperatorTypeEnum.SYSTEM)
.operatorId(cleanerId)
.operatorId(deviceId)
.reason("丢失信号,自动完成")
.build();
request.putPayload("lostSeconds", lostSeconds);
@@ -329,40 +335,39 @@ public class CleanOrderServiceImpl implements CleanOrderService {
recordCompletedTime(orderId);
// 自动推送下一个任务
dispatchEngine.autoDispatchNext(orderId, cleanerId);
dispatchEngine.autoDispatchNext(orderId, deviceId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void autoDispatchNextOrder(Long completedOrderId, Long cleanerId) {
log.info("任务完成后自动调度下一个: completedOrderId={}, cleanerId={}", completedOrderId, cleanerId);
public void autoDispatchNextOrder(Long completedOrderId, Long deviceId) {
log.info("任务完成后自动调度下一个: completedOrderId={}, deviceId={}", completedOrderId, deviceId);
// 使用新的调度引擎自动推送下一个
DispatchResult result = dispatchEngine.autoDispatchNext(completedOrderId, cleanerId);
// 使用调度引擎自动推送下一个
DispatchResult result = dispatchEngine.autoDispatchNext(completedOrderId, deviceId);
if (result.isSuccess()) {
log.info("已自动推送下一个任务: cleanerId={}", cleanerId);
log.info("已自动推送下一个任务: deviceId={}", deviceId);
} else {
log.info("无等待任务,保洁员变空闲: cleanerId={}", cleanerId);
// 状态更新由 CleanOrderEventListener 处理
log.info("无等待任务,设备变空闲: deviceId={}", deviceId);
}
}
// ==================== 语音播报(委托给 EventHandler====================
@Override
public void playVoiceForNewOrder(Long cleanerId) {
cleanOrderEventListener.sendNewOrderNotification(cleanerId, null);
public void playVoiceForNewOrder(Long deviceId) {
cleanOrderEventListener.sendNewOrderNotification(deviceId, null);
}
@Override
public void playVoiceForQueuedOrder(Long cleanerId, int queueCount) {
cleanOrderEventListener.sendQueuedOrderNotification(cleanerId, queueCount);
public void playVoiceForQueuedOrder(Long deviceId, int queueCount) {
cleanOrderEventListener.sendQueuedOrderNotification(deviceId, queueCount);
}
@Override
public void playVoiceForNextTask(Long cleanerId, int queueCount, String nextTaskTitle) {
cleanOrderEventListener.sendNextTaskNotification(cleanerId, queueCount, nextTaskTitle);
public void playVoiceForNextTask(Long deviceId, int queueCount, String nextTaskTitle) {
cleanOrderEventListener.sendNextTaskNotification(deviceId, queueCount, nextTaskTitle);
}
// ==================== 作业时长计算 ====================

View File

@@ -18,6 +18,7 @@ import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.module.ops.environment.integration.listener.CleanOrderEventListener;
import com.viewsh.module.ops.environment.service.cleanorder.CleanOrderServiceImpl;
import com.viewsh.module.ops.environment.service.voice.VoiceBroadcastService;
import com.viewsh.module.ops.environment.service.badge.BadgeDeviceStatusService;
import com.viewsh.module.ops.infrastructure.code.OrderCodeGenerator;
import com.viewsh.module.ops.infrastructure.id.OrderIdGenerator;
import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder;
@@ -89,6 +90,10 @@ public class CleanOrderEndToEndTest {
private ValueOperations<String, String> valueOperations;
@Mock
private VoiceBroadcastService voiceBroadcastService;
@Mock
private BadgeDeviceStatusService badgeDeviceStatusService;
@Mock
private EventLogRecorder eventLogRecorder;
@@ -245,7 +250,8 @@ public class CleanOrderEndToEndTest {
@Test
void testAV01_BeaconAutoArrive() throws Exception {
injectField(arriveEventHandler, "orderLifecycleManager", orderLifecycleManager);
injectField(arriveEventHandler, "badgeDeviceStatusService", badgeDeviceStatusService);
// 准备工单:状态 DISPATCHED
Long orderId = 1001L;
OpsOrderDO order = OpsOrderDO.builder()
@@ -283,9 +289,10 @@ public class CleanOrderEndToEndTest {
assertEquals(orderId, req.getOrderId());
assertEquals(WorkOrderStatusEnum.ARRIVED, req.getTargetStatus());
assertTrue(req.getReason().contains("自动到岗确认"));
// 3. 验证 Redis 缓存更新
verify(valueOperations).set(contains("ops:clean:device:order:5001"), anyString(), anyLong(), any(TimeUnit.class));
// 3. 验证设备状态服务更新工单信息
verify(badgeDeviceStatusService).setCurrentOrderInfo(eq(5001L), eq(1001L),
eq(WorkOrderStatusEnum.ARRIVED.getStatus()), eq(101L), eq("F0:C8:60:1D:10:BB"));
}
@Test
@@ -315,6 +322,7 @@ public class CleanOrderEndToEndTest {
void testCP01_SignalLossAutoComplete() throws Exception {
injectField(completeEventHandler, "orderLifecycleManager", orderLifecycleManager);
injectField(completeEventHandler, "cleanOrderService", cleanOrderService);
injectField(completeEventHandler, "badgeDeviceStatusService", badgeDeviceStatusService);
injectField(cleanOrderService, "dispatchEngine", dispatchEngine);
// 准备工单:状态 ARRIVED
@@ -333,7 +341,7 @@ public class CleanOrderEndToEndTest {
"\"triggerSource\":\"IOT_SIGNAL_LOSS\"," +
"\"triggerData\":{\"durationMs\":1800000}" +
"}";
// 模拟 autoDispatchNext 调用成功
when(dispatchEngine.autoDispatchNext(eq(orderId), eq(2001L)))
.thenReturn(DispatchResult.success("Success", 2001L));
@@ -345,8 +353,8 @@ public class CleanOrderEndToEndTest {
// 1. 验证调用了 completeOrder
verify(orderLifecycleManager).completeOrder(eq(orderId), eq(null), contains("信号丢失超时"));
// 2. 验证清理了 Redis 缓存
verify(stringRedisTemplate).delete(contains("ops:clean:device:order:5001"));
// 2. 验证清理了设备工单缓存
verify(badgeDeviceStatusService).clearCurrentOrder(5001L);
// 3. 验证触发了自动调度下一单
verify(dispatchEngine).autoDispatchNext(eq(orderId), eq(2001L));

View File

@@ -8,7 +8,7 @@ import java.util.List;
/**
* 工单队列管理服务接口
* 提供工单队列的核心管理功能,包括入队、出队、状态变更、优先级调整等
*
* @deprecated userId 参数实际存储的是工牌设备ID
* @author lzh
*/
public interface OrderQueueService {
@@ -18,7 +18,7 @@ public interface OrderQueueService {
* 将工单加入派单队列,等待派单
*
* @param opsOrderId 工单ID
* @param userId 执行人员ID保洁员
* @param userId 执行人员ID保洁员/工牌设备ID
* @param priority 优先级
* @param queueIndex 队列顺序(可选,用于同优先级排序)
* @return 队列记录ID

View File

@@ -33,6 +33,18 @@ public class EventPublishHandler extends TransitionHandler {
log.debug("事件发布处理器: orderId={}, {} -> {}", context.getOrder().getId(), oldStatus, targetStatus);
try {
// 构建事件 payload包含 assigneeId
java.util.Map<String, Object> payload = request.getPayload() != null
? new java.util.HashMap<>(request.getPayload())
: new java.util.HashMap<>();
if (request.getAssigneeId() != null) {
payload.put("assigneeId", request.getAssigneeId());
}
// 添加 urgentOrderIdP0打断场景
if (request.getUrgentOrderId() != null) {
payload.put("urgentOrderId", request.getUrgentOrderId());
}
// 发布状态变更事件
OrderStateChangedEvent event = OrderStateChangedEvent.builder()
.orderId(context.getOrder().getId())
@@ -44,7 +56,7 @@ public class EventPublishHandler extends TransitionHandler {
.operatorType(request.getOperatorType())
.eventTime(java.time.LocalDateTime.now())
.remark(request.getReason())
.payload(request.getPayload())
.payload(payload)
.build();
orderEventPublisher.publishStateChanged(event);

View File

@@ -25,7 +25,7 @@ public enum EventDomain {
TRAFFIC("traffic", "客流"),
/**
* 设备域 - 设备控制、TTS<EFBFBD><EFBFBD>震动等
* 设备域 - 设备控制、TTS震动等
*/
DEVICE("device", "设备"),