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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user