feat(ops): add confirm event DTO and update audit DTO
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package com.viewsh.module.ops.environment.integration.adapter;
|
||||
|
||||
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
||||
import com.viewsh.module.ops.core.dispatch.model.AssigneeStatus;
|
||||
import com.viewsh.module.ops.enums.BadgeDeviceStatusEnum;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 工牌设备状态适配器
|
||||
* <p>
|
||||
* 将 {@link BadgeDeviceStatusDTO} 适配为通用的 {@link AssigneeStatus} 接口
|
||||
* <p>
|
||||
* 设计说明:
|
||||
* - 适配器模式:将工牌设备状态对象转换为通用接口
|
||||
* - 设备即执行人:用工牌设备ID作为执行人ID参与调度
|
||||
* - 解耦设计:调度引擎通过通用接口访问设备状态,不依赖具体实现
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public class BadgeAssigneeStatusAdapter implements AssigneeStatus {
|
||||
|
||||
private final BadgeDeviceStatusDTO badgeStatus;
|
||||
private final Long waitingTaskCount;
|
||||
|
||||
public BadgeAssigneeStatusAdapter(BadgeDeviceStatusDTO badgeStatus) {
|
||||
this(badgeStatus, 0L);
|
||||
}
|
||||
|
||||
public BadgeAssigneeStatusAdapter(BadgeDeviceStatusDTO badgeStatus, Long waitingTaskCount) {
|
||||
this.badgeStatus = badgeStatus;
|
||||
this.waitingTaskCount = waitingTaskCount != null ? waitingTaskCount : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatus() {
|
||||
return badgeStatus != null && badgeStatus.getStatus() != null
|
||||
? badgeStatus.getStatus().getCode()
|
||||
: "offline";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdle() {
|
||||
return badgeStatus != null && badgeStatus.getStatus() == BadgeDeviceStatusEnum.IDLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBusy() {
|
||||
return badgeStatus != null && badgeStatus.getStatus() == BadgeDeviceStatusEnum.BUSY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return badgeStatus != null && badgeStatus.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCurrentTaskCount() {
|
||||
// 有正在执行的工单则返回1,否则返回0
|
||||
return badgeStatus != null && badgeStatus.hasCurrentOrder() ? 1L : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getWaitingTaskCount() {
|
||||
return waitingTaskCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAssigneeId() {
|
||||
// 工牌设备ID作为执行人ID
|
||||
return badgeStatus != null ? badgeStatus.getDeviceId() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAssigneeName() {
|
||||
// 工牌设备编码作为执行人名称
|
||||
return badgeStatus != null ? badgeStatus.getDeviceCode() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getAreaId() {
|
||||
return badgeStatus != null ? badgeStatus.getCurrentAreaId() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime getLastHeartbeatTime() {
|
||||
if (badgeStatus == null || badgeStatus.getLastHeartbeatTime() == null) {
|
||||
return null;
|
||||
}
|
||||
// 将时间戳转换为 LocalDateTime
|
||||
return LocalDateTime.ofInstant(
|
||||
java.time.Instant.ofEpochMilli(badgeStatus.getLastHeartbeatTime()),
|
||||
java.time.ZoneId.systemDefault()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getBatteryLevel() {
|
||||
return badgeStatus != null ? badgeStatus.getBatteryLevel() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getExtension(String key) {
|
||||
if (badgeStatus == null) {
|
||||
return null;
|
||||
}
|
||||
// 支持业务特定的扩展属性
|
||||
switch (key) {
|
||||
case "currentAreaName":
|
||||
return badgeStatus.getCurrentAreaName();
|
||||
case "currentOrderId":
|
||||
return badgeStatus.getCurrentOpsOrderId();
|
||||
case "statusChangeTime":
|
||||
return badgeStatus.getStatusChangeTime();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始的工牌设备状态对象
|
||||
*/
|
||||
public BadgeDeviceStatusDTO getBadgeStatus() {
|
||||
return badgeStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建适配器(静态工厂方法)
|
||||
*/
|
||||
public static BadgeAssigneeStatusAdapter of(BadgeDeviceStatusDTO badgeStatus) {
|
||||
return new BadgeAssigneeStatusAdapter(badgeStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建带等待任务数的适配器
|
||||
*/
|
||||
public static BadgeAssigneeStatusAdapter of(BadgeDeviceStatusDTO badgeStatus, Long waitingTaskCount) {
|
||||
return new BadgeAssigneeStatusAdapter(badgeStatus, waitingTaskCount);
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,13 @@ public class CleanOrderAuditEventDTO {
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 审计类型
|
||||
/**
|
||||
* 触发来源 (如 IOT_BUTTON_QUERY)
|
||||
*/
|
||||
private String triggerSource;
|
||||
|
||||
/**
|
||||
* 审计类型
|
||||
* <p>
|
||||
* - BEACON_ARRIVE_CONFIRMED: 蓝牙信标到岗确认
|
||||
* - BEACON_LEAVE_WARNING_SENT: 离开区域警告已发送
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.viewsh.module.ops.environment.integration.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 保洁工单确认事件 DTO
|
||||
* <p>
|
||||
* 由 IoT 模块发布,Ops 模块消费
|
||||
*
|
||||
* @author AI
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CleanOrderConfirmEventDTO extends BaseDeviceEventDTO {
|
||||
|
||||
/**
|
||||
* 工单ID
|
||||
*/
|
||||
@JsonProperty("orderId")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 区域ID
|
||||
*/
|
||||
@JsonProperty("areaId")
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
* 触发来源
|
||||
*/
|
||||
@JsonProperty("triggerSource")
|
||||
private String triggerSource;
|
||||
|
||||
/**
|
||||
* 按键ID
|
||||
*/
|
||||
@JsonProperty("buttonId")
|
||||
private Integer buttonId;
|
||||
|
||||
/**
|
||||
* 设备Key (IoT模块发来的字段名)
|
||||
*/
|
||||
@JsonProperty("deviceKey")
|
||||
private String deviceKey;
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.viewsh.module.ops.environment.integration.handler;
|
||||
|
||||
import com.viewsh.module.ops.core.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;
|
||||
|
||||
/**
|
||||
* 工牌设备状态事件处理器
|
||||
* <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 工单状态变更事件
|
||||
*/
|
||||
@EventListener
|
||||
@Async("opsExecutor")
|
||||
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 ASSIGNED:
|
||||
// 工单已分配,不影响设备状态(设备可能还在处理其他任务)
|
||||
// 设置设备当前工单
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.viewsh.module.ops.api.badge;
|
||||
|
||||
import com.viewsh.module.ops.enums.BadgeDeviceStatusEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 工牌设备状态 DTO
|
||||
* <p>
|
||||
* 用于跨模块传输工牌设备状态数据
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BadgeDeviceStatusDTO {
|
||||
|
||||
/**
|
||||
* 工牌设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 工牌设备编码
|
||||
*/
|
||||
private String deviceCode;
|
||||
|
||||
/**
|
||||
* 设备状态
|
||||
*/
|
||||
private BadgeDeviceStatusEnum status;
|
||||
|
||||
/**
|
||||
* 电量(0-100)
|
||||
*/
|
||||
private Integer batteryLevel;
|
||||
|
||||
/**
|
||||
* 当前所在区域ID
|
||||
*/
|
||||
private Long currentAreaId;
|
||||
|
||||
/**
|
||||
* 当前所在区域名称
|
||||
*/
|
||||
private String currentAreaName;
|
||||
|
||||
/**
|
||||
* 当前正在执行的工单ID
|
||||
*/
|
||||
private Long currentOpsOrderId;
|
||||
|
||||
/**
|
||||
* 最后心跳时间(时间戳)
|
||||
*/
|
||||
private Long lastHeartbeatTime;
|
||||
|
||||
/**
|
||||
* 状态变更时间
|
||||
*/
|
||||
private LocalDateTime statusChangeTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
// ========== 便捷方法 ==========
|
||||
|
||||
/**
|
||||
* 判断是否在线(非OFFLINE状态)
|
||||
*/
|
||||
public boolean isOnline() {
|
||||
return status != null && status.isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否可接新单
|
||||
*/
|
||||
public boolean canAcceptNewOrder() {
|
||||
return status != null && status.isCanAcceptNewOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否正在执行工单
|
||||
*/
|
||||
public boolean isBusy() {
|
||||
return status == BadgeDeviceStatusEnum.BUSY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已暂停
|
||||
*/
|
||||
public boolean isPaused() {
|
||||
return status == BadgeDeviceStatusEnum.PAUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有当前工单
|
||||
*/
|
||||
public boolean hasCurrentOrder() {
|
||||
return currentOpsOrderId != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态代码
|
||||
*/
|
||||
public String getStatusCode() {
|
||||
return status != null ? status.getCode() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断心跳是否超时(超过指定分钟数)
|
||||
*
|
||||
* @param thresholdMinutes 超时阈值(分钟)
|
||||
* @return 是否超时
|
||||
*/
|
||||
public boolean isHeartbeatTimeout(int thresholdMinutes) {
|
||||
if (lastHeartbeatTime == null) {
|
||||
return true;
|
||||
}
|
||||
long thresholdMillis = System.currentTimeMillis() - (thresholdMinutes * 60L * 1000L);
|
||||
return lastHeartbeatTime < thresholdMillis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.viewsh.module.ops.enums;
|
||||
|
||||
import com.viewsh.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 工牌设备<E8AEBE><E5A487>态枚举
|
||||
* <p>
|
||||
* 与 CleanerStatusEnum 保持一致,用于工牌设备状态管理
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum BadgeDeviceStatusEnum implements ArrayValuable<String> {
|
||||
|
||||
/**
|
||||
* 空闲 - 可接新单
|
||||
*/
|
||||
IDLE("idle", "空闲", true),
|
||||
|
||||
/**
|
||||
* 忙碌 - 正在执行工单
|
||||
*/
|
||||
BUSY("busy", "忙碌", false),
|
||||
|
||||
/**
|
||||
* 暂停 - 临时离开或被P0打断
|
||||
*/
|
||||
PAUSED("paused", "暂停", false),
|
||||
|
||||
/**
|
||||
* 离线 - 设备无心跳
|
||||
*/
|
||||
OFFLINE("offline", "离线", false);
|
||||
|
||||
public static final String[] ARRAYS = Arrays.stream(values()).map(BadgeDeviceStatusEnum::getCode).toArray(String[]::new);
|
||||
|
||||
/**
|
||||
* 状态代码
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 状态描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* 是否可接新单
|
||||
*/
|
||||
private final boolean canAcceptNewOrder;
|
||||
|
||||
@Override
|
||||
public String[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态代码
|
||||
*/
|
||||
public String getStatus() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为活跃状态(非OFFLINE)
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return this != OFFLINE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以转换到目标状态
|
||||
*
|
||||
* @param target 目标状态
|
||||
* @return 是否可以转换
|
||||
*/
|
||||
public boolean canTransitionTo(BadgeDeviceStatusEnum target) {
|
||||
return switch (this) {
|
||||
case OFFLINE -> target == IDLE;
|
||||
case IDLE -> target == BUSY || target == OFFLINE;
|
||||
case BUSY -> target == PAUSED || target == IDLE || target == OFFLINE;
|
||||
case PAUSED -> target == BUSY || target == IDLE || target == OFFLINE;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @param code 状态代码
|
||||
* @return 枚举实例,未找到返回OFFLINE
|
||||
*/
|
||||
public static BadgeDeviceStatusEnum fromCode(String code) {
|
||||
if (code == null) {
|
||||
return OFFLINE;
|
||||
}
|
||||
return Arrays.stream(values())
|
||||
.filter(e -> e.getCode().equals(code))
|
||||
.findFirst()
|
||||
.orElse(OFFLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从保洁员状态枚举转换
|
||||
*
|
||||
* @param cleanerStatus 保洁员状态枚举
|
||||
* @return 工牌设备状态枚举
|
||||
*/
|
||||
public static BadgeDeviceStatusEnum fromCleanerStatus(CleanerStatusEnum cleanerStatus) {
|
||||
if (cleanerStatus == null) {
|
||||
return OFFLINE;
|
||||
}
|
||||
return fromCode(cleanerStatus.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为保洁员状态枚举
|
||||
*/
|
||||
public CleanerStatusEnum toCleanerStatus() {
|
||||
return CleanerStatusEnum.fromCode(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.viewsh.module.ops.core.badge;
|
||||
|
||||
import com.viewsh.module.ops.api.badge.BadgeDeviceStatusDTO;
|
||||
import com.viewsh.module.ops.enums.BadgeDeviceStatusEnum;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工牌设备状态服务接口
|
||||
* <p>
|
||||
* 职责:
|
||||
* 1. 管理工牌设备状态(Redis 存储和查询)
|
||||
* 2. 处理设备心跳更新
|
||||
* 3. 状态转换(IDLE ↔ BUSY ↔ PAUSED ↔ OFFLINE)
|
||||
* 4. 区域设备查询
|
||||
* 5. 当前任务关联
|
||||
* <p>
|
||||
* 设计原则:
|
||||
* - 状态存储在 Redis 中,不依赖数据库表
|
||||
* - 与保洁员状态解耦,只关注设备本身
|
||||
* - 为调度引擎提供设备状态查询能力
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public interface BadgeDeviceStatusService {
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
|
||||
/**
|
||||
* 更新工牌设备状态
|
||||
* <p>
|
||||
* 状态转换会记录状态变更时间和操作原因
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param status 目标状态
|
||||
* @param operatorId 操作人ID(可为null,表示系统操作)
|
||||
* @param reason 状态变更原因
|
||||
*/
|
||||
void updateBadgeStatus(Long deviceId, BadgeDeviceStatusEnum status, Long operatorId, String reason);
|
||||
|
||||
/**
|
||||
* 批量更新工牌设备状态
|
||||
*
|
||||
* @param deviceIds 设备ID列表
|
||||
* @param status 目标状态
|
||||
* @param operatorId 操作人ID
|
||||
* @param reason 状态变更原因
|
||||
*/
|
||||
void batchUpdateBadgeStatus(List<Long> deviceIds, BadgeDeviceStatusEnum status, Long operatorId, String reason);
|
||||
|
||||
// ==================== 状态查询 ====================
|
||||
|
||||
/**
|
||||
* 获取工牌设备状态
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 设备状态DTO,不存在返回null
|
||||
*/
|
||||
BadgeDeviceStatusDTO getBadgeStatus(Long deviceId);
|
||||
|
||||
/**
|
||||
* 批量获取工牌设备状态
|
||||
*
|
||||
* @param deviceIds 设备ID列表
|
||||
* @return 设备状态DTO列表
|
||||
*/
|
||||
List<BadgeDeviceStatusDTO> batchGetBadgeStatus(List<Long> deviceIds);
|
||||
|
||||
/**
|
||||
* 获取指定区域的工牌设备列表
|
||||
* <p>
|
||||
* 只返回非 OFFLINE 状态的设备
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @return 设备状态DTO列表
|
||||
*/
|
||||
List<BadgeDeviceStatusDTO> listBadgesByArea(Long areaId);
|
||||
|
||||
/**
|
||||
* 获取可接单的工牌设备(IDLE 状态)
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @return 可接单设备列表
|
||||
*/
|
||||
List<BadgeDeviceStatusDTO> listAvailableBadges(Long areaId);
|
||||
|
||||
/**
|
||||
* 获取所有活跃的工牌设备(非OFFLINE状态)
|
||||
*
|
||||
* @return 活跃设备列表
|
||||
*/
|
||||
List<BadgeDeviceStatusDTO> listActiveBadges();
|
||||
|
||||
// ==================== 心跳处理 ====================
|
||||
|
||||
/**
|
||||
* 处理工牌设备心跳
|
||||
* <p>
|
||||
* 更新最后心跳时间和电量:
|
||||
* <ul>
|
||||
* <li>如果设备之前为 OFFLINE,转为 IDLE</li>
|
||||
* <li>如果设备已存在,更新心跳时间和电量</li>
|
||||
* <li>如果设备不存在,创建新记录(状态为 IDLE)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param deviceCode 设备编码
|
||||
* @param batteryLevel 电量(0-100)
|
||||
*/
|
||||
void handleHeartbeat(Long deviceId, String deviceCode, Integer batteryLevel);
|
||||
|
||||
/**
|
||||
* 处理工牌设备心跳(带区域信息)
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param deviceCode 设备编码
|
||||
* @param batteryLevel 电量(0-100)
|
||||
* @param areaId 当前所在区域ID
|
||||
* @param areaName 当前所在区域名称
|
||||
*/
|
||||
void handleHeartbeatWithArea(Long deviceId, String deviceCode, Integer batteryLevel,
|
||||
Long areaId, String areaName);
|
||||
|
||||
// ==================== 在线状态检查 ====================
|
||||
|
||||
/**
|
||||
* 检查工牌设备是否在线(非OFFLINE状态)
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 是否在线
|
||||
*/
|
||||
boolean isBadgeOnline(Long deviceId);
|
||||
|
||||
/**
|
||||
* 检查工牌设备心跳是否超时
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param thresholdMinutes 超时阈值(分钟)
|
||||
* @return 是否超时
|
||||
*/
|
||||
boolean isHeartbeatTimeout(Long deviceId, int thresholdMinutes);
|
||||
|
||||
/**
|
||||
* 检查心跳超时并将超时设备设为OFFLINE
|
||||
* <p>
|
||||
* 定时任务调用,默认超时时间为30分钟
|
||||
*/
|
||||
void checkAndMarkOfflineDevices();
|
||||
|
||||
// ==================== 工单关联 ====================
|
||||
|
||||
/**
|
||||
* 设置当前工单
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param orderId 工单ID
|
||||
*/
|
||||
void setCurrentOrder(Long deviceId, Long orderId);
|
||||
|
||||
/**
|
||||
* 清除当前工单
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
*/
|
||||
void clearCurrentOrder(Long deviceId);
|
||||
|
||||
/**
|
||||
* 获取当前有工单的设备列表
|
||||
*
|
||||
* @return 有工单的设备列表
|
||||
*/
|
||||
List<BadgeDeviceStatusDTO> listBadgesWithCurrentOrder();
|
||||
|
||||
// ==================== 区域管理 ====================
|
||||
|
||||
/**
|
||||
* 更新工牌设备所在区域
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param areaId 区域ID
|
||||
* @param areaName 区域名称
|
||||
*/
|
||||
void updateBadgeArea(Long deviceId, Long areaId, String areaName);
|
||||
|
||||
/**
|
||||
* 初始化区域设备索引
|
||||
* <p>
|
||||
* 从 ops_area_device_relation 表加载 BADGE 类型的设备,
|
||||
* 建立区域到设备的索引关系
|
||||
*/
|
||||
void initAreaDeviceIndex();
|
||||
|
||||
/**
|
||||
* 刷新区域设备索引
|
||||
* <p>
|
||||
* 重新从数据库加载区域设备关系
|
||||
*/
|
||||
void refreshAreaDeviceIndex();
|
||||
|
||||
/**
|
||||
* 将设备添加到区域索引
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param areaId 区域ID
|
||||
*/
|
||||
void addToAreaIndex(Long deviceId, Long areaId);
|
||||
|
||||
/**
|
||||
* 从区域索引移除设备
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param areaId 区域ID
|
||||
*/
|
||||
void removeFromAreaIndex(Long deviceId, Long areaId);
|
||||
|
||||
// ==================== 设备管理 ====================
|
||||
|
||||
/**
|
||||
* 删除工牌设备状态
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
*/
|
||||
void deleteBadgeStatus(Long deviceId);
|
||||
|
||||
/**
|
||||
* 清理所有离线设备的状态
|
||||
*/
|
||||
void clearOfflineBadges();
|
||||
}
|
||||
Reference in New Issue
Block a user