chore: 【ops】保洁员状态管理

This commit is contained in:
lzh
2026-01-09 17:39:54 +08:00
parent 628d8fd8c4
commit f26bd15f5b
2 changed files with 282 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
package com.viewsh.module.ops.environment.integration.adapter;
import com.viewsh.module.ops.core.dispatch.model.AssigneeStatus;
import com.viewsh.module.ops.environment.dal.dataobject.cleaner.OpsCleanerStatusDO;
import com.viewsh.module.ops.enums.CleanerStatusEnum;
import java.time.LocalDateTime;
/**
* 保洁员状态适配器
* <p>
* 将 {@link OpsCleanerStatusDO} 适配为通用的 {@link AssigneeStatus} 接口
* <p>
* 设计说明:
* - 适配器模式:将保洁业务特定的状态对象转换为通用接口
* - 解耦设计:调度引擎通过通用接口访问保洁员状态,不依赖具体业务实现
*
* @author lzh
*/
public class CleanerAssigneeStatusAdapter implements AssigneeStatus {
private final OpsCleanerStatusDO cleanerStatus;
private final Long waitingTaskCount;
public CleanerAssigneeStatusAdapter(OpsCleanerStatusDO cleanerStatus) {
this(cleanerStatus, 0L);
}
public CleanerAssigneeStatusAdapter(OpsCleanerStatusDO cleanerStatus, Long waitingTaskCount) {
this.cleanerStatus = cleanerStatus;
this.waitingTaskCount = waitingTaskCount != null ? waitingTaskCount : 0L;
}
@Override
public String getStatus() {
return cleanerStatus.getStatus();
}
@Override
public boolean isIdle() {
CleanerStatusEnum statusEnum = cleanerStatus.getStatusEnum();
return statusEnum == CleanerStatusEnum.IDLE;
}
@Override
public boolean isBusy() {
CleanerStatusEnum statusEnum = cleanerStatus.getStatusEnum();
return statusEnum == CleanerStatusEnum.BUSY;
}
@Override
public boolean isOnline() {
CleanerStatusEnum statusEnum = cleanerStatus.getStatusEnum();
return statusEnum != CleanerStatusEnum.OFFLINE;
}
@Override
public Long getCurrentTaskCount() {
// 有正在执行的工单则返回1否则返回0
return cleanerStatus.getCurrentOpsOrderId() != null ? 1L : 0L;
}
@Override
public Long getWaitingTaskCount() {
return waitingTaskCount;
}
@Override
public Long getAssigneeId() {
return cleanerStatus.getUserId();
}
@Override
public String getAssigneeName() {
return cleanerStatus.getUserName();
}
@Override
public Long getAreaId() {
return cleanerStatus.getCurrentAreaId();
}
@Override
public LocalDateTime getLastHeartbeatTime() {
return cleanerStatus.getLastHeartbeatTime();
}
@Override
public Integer getBatteryLevel() {
return cleanerStatus.getBatteryLevel();
}
@Override
public Object getExtension(String key) {
// 支持业务特定的扩展属性
switch (key) {
case "deviceId":
return cleanerStatus.getDeviceId();
case "deviceCode":
return cleanerStatus.getDeviceCode();
case "currentAreaName":
return cleanerStatus.getCurrentAreaName();
case "currentOrderId":
return cleanerStatus.getCurrentOpsOrderId();
case "currentOrderCode":
return cleanerStatus.getCurrentOpsOrderCode();
case "statusChangeTime":
return cleanerStatus.getStatusChangeTime();
case "offlineReason":
return cleanerStatus.getOfflineReason();
default:
return null;
}
}
/**
* 获取原始的保洁员状态对象
*/
public OpsCleanerStatusDO getCleanerStatus() {
return cleanerStatus;
}
}

View File

@@ -0,0 +1,160 @@
package com.viewsh.module.ops.environment.integration.listener;
import com.viewsh.module.ops.core.event.OrderCompletedEvent;
import com.viewsh.module.ops.core.event.OrderStateChangedEvent;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import com.viewsh.module.ops.environment.service.cleaner.CleanerStatusService;
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.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 保洁员状态变更监听器
* <p>
* 职责:订阅工单状态变更事件,同步更新保洁员状态
* <p>
* 设计说明:
* - 使用 @EventListener 订阅领域事件
* - 业务逻辑同步执行(保证状态一致性)
* - 通过事件驱动解耦通用层与业务层
* - 只处理保洁类型的工单orderType = "CLEAN"
*
* @author lzh
*/
@Slf4j
@Component
public class CleanerStateChangeListener {
@Resource
private CleanerStatusService cleanerStatusService;
/**
* 订阅状态变更事件
* <p>
* 只处理保洁类型的工单orderType = "CLEAN"
*/
@EventListener
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void onOrderStateChanged(OrderStateChangedEvent event) {
// 只处理保洁类型的工单
if (!"CLEAN".equals(event.getOrderType())) {
return;
}
log.info("保洁工单状态变更: orderId={}, {} -> {}, assigneeId={}",
event.getOrderId(), event.getOldStatus(), event.getNewStatus(), event.getOperatorId());
Long assigneeId = event.getOperatorId();
if (assigneeId == null) {
return;
}
switch (event.getNewStatus()) {
case DISPATCHED:
// 派单:不改变保洁员状态(等待保洁员确认)
log.debug("工单已派发,等待保洁员确认: orderId={}, assigneeId={}",
event.getOrderId(), assigneeId);
break;
case CONFIRMED:
// 确认:保洁员变更为 BUSY
updateCleanerStatus(assigneeId, com.viewsh.module.ops.enums.CleanerStatusEnum.BUSY,
"确认工单: " + event.getOrderId());
// 设置当前工单
cleanerStatusService.setCurrentWorkOrder(assigneeId, event.getOrderId(),
event.getOrderCode());
break;
case ARRIVED:
// 到岗:保洁员保持 BUSY
updateCleanerStatus(assigneeId, com.viewsh.module.ops.enums.CleanerStatusEnum.BUSY,
"开始作业: " + event.getOrderId());
break;
case PAUSED:
// 暂停:根据暂停原因决定状态
handlePausedStatus(event, assigneeId);
break;
case COMPLETED:
case CANCELLED:
// 完成/取消:清理当前工单,状态由 autoDispatchNext 决定
cleanerStatusService.clearCurrentWorkOrder(assigneeId);
log.info("工单完成或取消,已清理当前工单: assigneeId={}, orderId={}",
assigneeId, event.getOrderId());
break;
default:
break;
}
}
/**
* 订阅工单完成事件
* <p>
* 在工单完成后自动推送下一个任务或设置保洁员为空闲
*/
@EventListener
@Async("ops-task-executor")
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void onOrderCompleted(OrderCompletedEvent event) {
// 只处理保洁类型的工单
if (!"CLEAN".equals(event.getOrderType())) {
return;
}
log.info("保洁工单完成事件: orderId={}, assigneeId={}, workDuration={}秒",
event.getOrderId(), event.getAssigneeId(), event.getWorkDuration());
Long assigneeId = event.getAssigneeId();
if (assigneeId == null) {
return;
}
// 清理当前工单
cleanerStatusService.clearCurrentWorkOrder(assigneeId);
// 注意状态更新IDLE/BUSY由 autoDispatchNextOrder 处理
// 这里只负责清理当前工单记录
}
// ==================== 私有方法 ====================
/**
* 处理暂停状态
*/
private void handlePausedStatus(OrderStateChangedEvent event, Long assigneeId) {
String interruptReason = event.getPayloadString("interruptReason");
if ("P0_TASK_INTERRUPT".equals(interruptReason)) {
// P0任务打断释放保洁员资源
log.warn("保洁任务被P0任务打断: orderId={}, assigneeId={}",
event.getOrderId(), assigneeId);
cleanerStatusService.clearCurrentWorkOrder(assigneeId);
// 状态保持 BUSY因为有P0任务要处理
} else {
// 普通暂停:保洁员变更为 PAUSED
updateCleanerStatus(assigneeId, com.viewsh.module.ops.enums.CleanerStatusEnum.PAUSED,
event.getRemark() != null ? event.getRemark() : "任务暂停");
}
}
/**
* 更新保洁员状态
*/
private void updateCleanerStatus(Long userId, com.viewsh.module.ops.enums.CleanerStatusEnum newStatus,
String remark) {
try {
cleanerStatusService.updateStatus(userId, newStatus, remark);
log.info("保洁员状态已更新: userId={}, newStatus={}, remark={}",
userId, newStatus, remark);
} catch (Exception e) {
log.error("更新保洁员状态失败: userId={}, newStatus={}", userId, newStatus, e);
// 不抛出异常,避免影响主流程
}
}
}