diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/adapter/CleanerAssigneeStatusAdapter.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/adapter/CleanerAssigneeStatusAdapter.java new file mode 100644 index 0000000..e034f4d --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/adapter/CleanerAssigneeStatusAdapter.java @@ -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; + +/** + * 保洁员状态适配器 + *
+ * 将 {@link OpsCleanerStatusDO} 适配为通用的 {@link AssigneeStatus} 接口 + *
+ * 设计说明: + * - 适配器模式:将保洁业务特定的状态对象转换为通用接口 + * - 解耦设计:调度引擎通过通用接口访问保洁员状态,不依赖具体业务实现 + * + * @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; + } +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanerStateChangeListener.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanerStateChangeListener.java new file mode 100644 index 0000000..d275518 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanerStateChangeListener.java @@ -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; + +/** + * 保洁员状态变更监听器 + *
+ * 职责:订阅工单状态变更事件,同步更新保洁员状态 + *
+ * 设计说明: + * - 使用 @EventListener 订阅领域事件 + * - 业务逻辑同步执行(保证状态一致性) + * - 通过事件驱动解耦通用层与业务层 + * - 只处理保洁类型的工单(orderType = "CLEAN") + * + * @author lzh + */ +@Slf4j +@Component +public class CleanerStateChangeListener { + + @Resource + private CleanerStatusService cleanerStatusService; + + /** + * 订阅状态变更事件 + *
+ * 只处理保洁类型的工单(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; + } + } + + /** + * 订阅工单完成事件 + *
+ * 在工单完成后自动推送下一个任务或设置保洁员为空闲 + */ + @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); + // 不抛出异常,避免影响主流程 + } + } +}