refactor(ops): 设备状态管理重构
- 删除 BadgeDeviceStatusEventHandler(职责不清) - 新建 BadgeDeviceStatusEventListener 专门处理设备状态变更 - 使用 BEFORE_COMMIT 阶段同步更新设备工单关联 - 修复 CANCELLED 处理:仅当取消当前工单时才更新设备状态 - 修复 P0 打断场景:检测 urgentOrderId,不修改设备状态 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,206 +0,0 @@
|
||||
package com.viewsh.module.ops.environment.integration.listener;
|
||||
|
||||
import com.viewsh.module.ops.environment.service.badge.BadgeDeviceStatusService;
|
||||
import com.viewsh.module.ops.core.event.OrderStateChangedEvent;
|
||||
import com.viewsh.module.ops.enums.BadgeDeviceStatusEnum;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
/**
|
||||
* 工牌设备状态事件处理器
|
||||
* <p>
|
||||
* 监听工单状态变更事件,同步更新工牌设备状态
|
||||
* <p>
|
||||
* 设计说明:
|
||||
* - 使用 Spring EventListener 监听工单状态变更事件
|
||||
* - 根据工单状态转换规则更新工牌设备状态
|
||||
* - 支持工单分配、开始、暂停、完成等场景
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BadgeDeviceStatusEventHandler {
|
||||
|
||||
@Resource
|
||||
private BadgeDeviceStatusService badgeDeviceStatusService;
|
||||
|
||||
/**
|
||||
* 处理工单状态变更事件
|
||||
* <p>
|
||||
* 状态映射规则:
|
||||
* - PENDING → ASSIGNED: 不影响设备状态(设备可能还在处理其他任务)
|
||||
* - ASSIGNED → ARRIVED: 设备状态 → BUSY(开始执行任务)
|
||||
* - ARRIVED → PAUSED: 设备状态 → PAUSED(任务暂停)
|
||||
* - PAUSED → ARRIVED/BUSY: 设备状态 → BUSY(任务恢复)
|
||||
* - ARRIVED → COMPLETED: 设备状态 → IDLE(任务完成,检查是否还有等待任务)
|
||||
* - 任意状态 → CANCELLED: 清除设备当前工单,检查是否还有等待任务
|
||||
*
|
||||
* @param event 工单状态变更事件
|
||||
*/
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
@Async("ops-task-executor")
|
||||
public void onOrderStateChanged(OrderStateChangedEvent event) {
|
||||
try {
|
||||
// 只处理保洁类型的工单
|
||||
if (!"CLEAN".equals(event.getOrderType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("[BadgeDeviceStatusEventHandler] 收到工单状态变更事件: orderId={}, {} → {}, operatorId={}",
|
||||
event.getOrderId(), event.getOldStatus(), event.getNewStatus(), event.getOperatorId());
|
||||
|
||||
// 获取设备ID(从 payload 或 operatorId 获取)
|
||||
Long deviceId = getDeviceIdFromEvent(event);
|
||||
if (deviceId == null) {
|
||||
log.debug("[BadgeDeviceStatusEventHandler] 无法获取设备ID,跳过处理: orderId={}", event.getOrderId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据状态转换更新设备状态
|
||||
handleStatusTransition(deviceId, event);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[BadgeDeviceStatusEventHandler] 处理工单状态变更事件失败: orderId={}", event.getOrderId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态转换更新设备状态
|
||||
*/
|
||||
private void handleStatusTransition(Long deviceId, OrderStateChangedEvent event) {
|
||||
WorkOrderStatusEnum newStatus = event.getNewStatus();
|
||||
|
||||
switch (newStatus) {
|
||||
case PENDING:
|
||||
// 工单已分配,不影响设备状态(设备可能还在处理其他任务)
|
||||
// 设置设备当前工单
|
||||
badgeDeviceStatusService.setCurrentOrder(deviceId, event.getOrderId());
|
||||
break;
|
||||
|
||||
case ARRIVED:
|
||||
// 保洁员已到岗,开始执行任务
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY,
|
||||
event.getOperatorId(), "工单开始执行: " + event.getOrderCode());
|
||||
badgeDeviceStatusService.setCurrentOrder(deviceId, event.getOrderId());
|
||||
break;
|
||||
|
||||
case PAUSED:
|
||||
// 任务暂停
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.PAUSED,
|
||||
event.getOperatorId(), event.getRemark());
|
||||
break;
|
||||
|
||||
case COMPLETED:
|
||||
// 任务完成
|
||||
badgeDeviceStatusService.clearCurrentOrder(deviceId);
|
||||
|
||||
// 检查是否还有等待任务,如果有则保持 BUSY,否则转为 IDLE
|
||||
checkAndUpdateDeviceStatusAfterCompletion(deviceId, event);
|
||||
break;
|
||||
|
||||
case CANCELLED:
|
||||
// 工单取消
|
||||
badgeDeviceStatusService.clearCurrentOrder(deviceId);
|
||||
|
||||
// 检查是否还有等待任务,如果有则保持 BUSY,否则转为 IDLE
|
||||
checkAndUpdateDeviceStatusAfterCompletion(deviceId, event);
|
||||
break;
|
||||
|
||||
default:
|
||||
log.debug("[BadgeDeviceStatusEventHandler] 不处理的状态: {}", newStatus);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成或取消后,检查并更新设备状态
|
||||
* <p>
|
||||
* 如果有等待任务,保持 BUSY 状态
|
||||
* 如果没有等待任务,转为 IDLE 状态
|
||||
*/
|
||||
private void checkAndUpdateDeviceStatusAfterCompletion(Long deviceId, OrderStateChangedEvent event) {
|
||||
// 检查是否还有等待任务(通过查询队列)
|
||||
// 这里简化处理,直接转为 IDLE
|
||||
// 实际业务中可能需要查询队列判断是否还有等待任务
|
||||
|
||||
// 检查是否是被P0打断的任务恢复
|
||||
boolean isResumeFromInterrupt = event.hasPayload("interruptReason")
|
||||
&& "RESUME_FROM_INTERRUPT".equals(event.getPayloadString("interruptReason"));
|
||||
|
||||
if (isResumeFromInterrupt) {
|
||||
// 恢复被中断的任务,保持 BUSY
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY,
|
||||
event.getOperatorId(), "恢复被中断的任务");
|
||||
} else {
|
||||
// 正常完成,转为 IDLE
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE,
|
||||
event.getOperatorId(), "任务完成,转为空闲");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从事件中获取设备ID
|
||||
* <p>
|
||||
* 优先级:
|
||||
* 1. payload.deviceId
|
||||
* 2. operatorId(如果 operatorType 是 DEVICE)
|
||||
* 3. 通过工单查询(需要额外的查询,暂不实现)
|
||||
*/
|
||||
private Long getDeviceIdFromEvent(OrderStateChangedEvent event) {
|
||||
// 优先从 payload 获取
|
||||
Long deviceId = event.getPayloadLong("deviceId");
|
||||
if (deviceId != null) {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
// TODO: 可以通过工单ID查询获取设备ID
|
||||
// 这需要注入 OrderService 或 OrderMapper
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 P0 紧急任务打断事件
|
||||
* <p>
|
||||
* 当 P0 任务需要打断当前任务时:
|
||||
* 1. 将被打断的设备状态转为 PAUSED
|
||||
* 2. 记录被打断的工单ID
|
||||
*
|
||||
* @param deviceId 被打断的设备ID
|
||||
* @param interruptedOrderId 被打断的工单ID
|
||||
* @param urgentOrderId P0紧急工单ID
|
||||
*/
|
||||
public void handleP0Interrupt(Long deviceId, Long interruptedOrderId, Long urgentOrderId) {
|
||||
log.info("[BadgeDeviceStatusEventHandler] P0紧急任务打断: deviceId={}, interruptedOrderId={}, urgentOrderId={}",
|
||||
deviceId, interruptedOrderId, urgentOrderId);
|
||||
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.PAUSED,
|
||||
null, "被P0紧急任务打断: " + urgentOrderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 P0 任务完成后的恢复事件
|
||||
* <p>
|
||||
* 当 P0 任务完成后,恢复被打断的任务:
|
||||
* 1. 将设备状态转为 BUSY
|
||||
* 2. 设置当前工单为被打断的工单
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param interruptedOrderId 被打断的工单ID
|
||||
*/
|
||||
public void handleP0Resume(Long deviceId, Long interruptedOrderId) {
|
||||
log.info("[BadgeDeviceStatusEventHandler] P0任务完成,恢复被中断任务: deviceId={}, interruptedOrderId={}",
|
||||
deviceId, interruptedOrderId);
|
||||
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY,
|
||||
null, "P0任务完成,恢复被中断的任务");
|
||||
badgeDeviceStatusService.setCurrentOrder(deviceId, interruptedOrderId);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.viewsh.module.ops.environment.integration.listener;
|
||||
|
||||
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
||||
import com.viewsh.module.ops.api.queue.OrderQueueService;
|
||||
import com.viewsh.module.ops.core.event.OrderStateChangedEvent;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
@@ -100,6 +101,7 @@ public class BadgeDeviceStatusEventListener {
|
||||
* 根据工单状态更新设备工牌关联
|
||||
*/
|
||||
private void handleOrderStatusTransition(Long deviceId, Long orderId, WorkOrderStatusEnum newStatus, OrderStateChangedEvent event) {
|
||||
var waitingTasks = orderQueueService.getWaitingTasksByUserId(deviceId);
|
||||
switch (newStatus) {
|
||||
case DISPATCHED:
|
||||
// 工单已推送到工牌,设置工单关联,设备状态转为 BUSY
|
||||
@@ -122,9 +124,18 @@ public class BadgeDeviceStatusEventListener {
|
||||
break;
|
||||
|
||||
case PAUSED:
|
||||
// 任务暂停,设备状态转为 PAUSED
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.PAUSED, null, "任务暂停");
|
||||
log.info("[BadgeDeviceStatusEventListener] 任务暂停,设备状态转为 PAUSED: deviceId={}", deviceId);
|
||||
// 检查是否是 P0 打断场景
|
||||
Long urgentOrderId = event.getPayloadLong("urgentOrderId");
|
||||
if (urgentOrderId != null) {
|
||||
// P0 打断场景:不修改设备状态,保持当前状态
|
||||
// 紧接着会有 P0 工单的 DISPATCHED 事件,将当前工单更新为 P0 工单
|
||||
log.info("[BadgeDeviceStatusEventListener] P0打断场景,工单已暂停,等待P0工单派发: pausedOrderId={}, urgentOrderId={}, deviceId={}",
|
||||
orderId, urgentOrderId, deviceId);
|
||||
} else {
|
||||
// 普通暂停场景:设备状态转为 PAUSED
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.PAUSED, null, "任务暂停");
|
||||
log.info("[BadgeDeviceStatusEventListener] 任务暂停,设备状态转为 PAUSED: deviceId={}", deviceId);
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPLETED:
|
||||
@@ -132,7 +143,6 @@ public class BadgeDeviceStatusEventListener {
|
||||
badgeDeviceStatusService.clearCurrentOrder(deviceId);
|
||||
|
||||
// 检查是否有等待任务,决定设备状态
|
||||
var waitingTasks = orderQueueService.getWaitingTasksByUserId(deviceId);
|
||||
if (waitingTasks != null && !waitingTasks.isEmpty()) {
|
||||
// 有等待任务,设备状态保持 BUSY(由后续 DISPATCHED 事件更新)
|
||||
log.info("[BadgeDeviceStatusEventListener] 任务完成,有{}个等待任务,设备保持 BUSY: deviceId={}",
|
||||
@@ -145,10 +155,29 @@ public class BadgeDeviceStatusEventListener {
|
||||
break;
|
||||
|
||||
case CANCELLED:
|
||||
// 工单取消,清除工单关联,设备状态转为 IDLE
|
||||
badgeDeviceStatusService.clearCurrentOrder(deviceId);
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE, null, "工单已取消");
|
||||
log.info("[BadgeDeviceStatusEventListener] 工单已取消,清除设备工单关联: deviceId={}", deviceId);
|
||||
// 检查被取消的工单是否是设备当前正在执行的工单
|
||||
BadgeDeviceStatusDTO deviceStatus = badgeDeviceStatusService.getBadgeStatus(deviceId);
|
||||
Long currentOrderId = deviceStatus != null ? deviceStatus.getCurrentOpsOrderId() : null;
|
||||
|
||||
if (orderId.equals(currentOrderId)) {
|
||||
// 取消的是当前正在执行的工单,清除工单关联
|
||||
badgeDeviceStatusService.clearCurrentOrder(deviceId);
|
||||
|
||||
// 检查是否有等待任务,决定设备状态
|
||||
if (waitingTasks != null && !waitingTasks.isEmpty()) {
|
||||
// 有等待任务,设备状态保持 BUSY(由后续 DISPATCHED 事件更新)
|
||||
log.info("[BadgeDeviceStatusEventListener] 当前工单已取消,有{}个等待任务,设备保持 BUSY: deviceId={}",
|
||||
waitingTasks.size(), deviceId);
|
||||
} else {
|
||||
// 无等待任务,设备状态转为 IDLE
|
||||
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE, null, "工单已取消");
|
||||
log.info("[BadgeDeviceStatusEventListener] 当前工单已取消,无等待任务,设备转为 IDLE: deviceId={}", deviceId);
|
||||
}
|
||||
} else {
|
||||
// 取消的不是当前工单(可能是队列中的等待任务),不需要修改设备状态
|
||||
log.debug("[BadgeDeviceStatusEventListener] 取消的工单非当前执行工单,跳过设备状态更新: cancelledOrderId={}, currentOrderId={}, deviceId={}",
|
||||
orderId, currentOrderId, deviceId);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -19,6 +19,16 @@ import java.util.stream.Collectors;
|
||||
* 工牌设备状态服务实现
|
||||
* <p>
|
||||
* 基于 Redis Hash 存储设备状态,Set 维护区域设备索引
|
||||
* <p>
|
||||
* 职责:
|
||||
* 1. 设备状态管理(IDLE/BUSY/PAUSED/OFFLINE)
|
||||
* 2. 设备与工单关联管理
|
||||
* 3. 区域设备索引管理
|
||||
* 4. 心跳超时检查
|
||||
* <p>
|
||||
* 设计说明:
|
||||
* - 状态变更事件由 {@link com.viewsh.module.ops.environment.integration.listener.BadgeDeviceStatusEventListener} 处理
|
||||
* - 本类只提供基础的服务方法
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user