diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanOrderEventListener.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanOrderEventListener.java index 2efdbb4..4d03cbb 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanOrderEventListener.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/listener/CleanOrderEventListener.java @@ -1,8 +1,7 @@ package com.viewsh.module.ops.environment.integration.listener; -import cn.hutool.core.map.MapUtil; import com.viewsh.module.iot.api.device.IotDeviceControlApi; -import com.viewsh.module.iot.api.device.dto.IotDeviceServiceInvokeReqDTO; +import com.viewsh.module.iot.api.device.dto.ResetTrafficCounterReqDTO; import com.viewsh.module.ops.core.dispatch.DispatchEngine; import com.viewsh.module.ops.core.dispatch.model.DispatchResult; import com.viewsh.module.ops.core.dispatch.model.OrderDispatchContext; @@ -15,9 +14,11 @@ import com.viewsh.module.ops.enums.PriorityEnum; import com.viewsh.module.ops.environment.constants.CleanNotificationConstants; import com.viewsh.module.ops.environment.dal.dataobject.workorder.OpsOrderCleanExtDO; import com.viewsh.module.ops.environment.dal.mysql.workorder.OpsOrderCleanExtMapper; -import com.viewsh.module.ops.environment.service.cleaner.CleanerStatusService; import com.viewsh.module.ops.environment.service.cleanorder.CleanOrderService; import com.viewsh.module.ops.environment.service.voice.VoiceBroadcastService; +import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain; +import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecord; +import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder; import com.viewsh.module.system.api.notify.NotifyMessageSendApi; import com.viewsh.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; import jakarta.annotation.Resource; @@ -28,6 +29,7 @@ import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; import java.time.LocalDateTime; +import java.util.Map; /** * 保洁工单事件监听器(统一入口) @@ -36,12 +38,19 @@ import java.time.LocalDateTime; * 1. 监听工单创建事件,触发自动调度 * 2. 监听工单状态变更事件,处理: * - 扩展表时间记录(到岗、完成、暂停) - * - 保洁员状态同步 - * - 通知发送(语音、震动、站内信) + * - 通知发送(语音、站内信) + * - 业务日志记录 * 3. 监听工单完成事件,触发自动派单下一个任务 *
+ * 职责划分: + * - 设备状态管理:由 BadgeDeviceStatusServiceImpl 处理 + * - 扩展表时间记录:由 CleanOrderEventListener 处理 + * - 通知发送:由 CleanOrderEventListener 处理 + * - 业务日志记录:由 CleanOrderEventListener 处理 + * - 自动调度:由 CleanOrderEventListener 触发 + *
* 设计说明: - * - 整合了 OrderCreatedEventListener、CleanerStateChangeListener、CleanOrderEventHandler 的功能 + * - assigneeId 存储的是工牌设备ID(非保洁员ID) * - 使用异步处理,避免阻塞主流程 * - 使用 @TransactionalEventListener(AFTER_COMMIT) 确保事务提交后再处理 * @@ -60,9 +69,6 @@ public class CleanOrderEventListener { @Resource private OpsOrderCleanExtMapper cleanExtMapper; - @Resource - private CleanerStatusService cleanerStatusService; - @Resource private CleanOrderService cleanOrderService; @@ -75,13 +81,13 @@ public class CleanOrderEventListener { @Resource private IotDeviceControlApi iotDeviceControlApi; + @Resource + private EventLogRecorder eventLogRecorder; + // ==================== 工单创建事件 ==================== /** * 监听工单创建事件,触发自动调度 - *
- * 使用 @TransactionalEventListener 确保在事务提交后才执行调度
- * 避免调度失败导致工单创建回滚
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
@@ -94,6 +100,11 @@ public class CleanOrderEventListener {
// 异步触发调度
asyncDispatchAfterCreated(event);
+
+ // 如果是客流触发的工单,重置客流计数器
+ if ("IOT_TRAFFIC".equals(event.getPayload().get("triggerSource"))) {
+ asyncResetTrafficCounter(event);
+ }
}
/**
@@ -122,7 +133,7 @@ public class CleanOrderEventListener {
DispatchResult result = dispatchEngine.dispatch(context);
if (result.isSuccess()) {
- log.info("[CleanOrderEventListener] 自动调度成功: orderId={}, assigneeId={}",
+ log.info("[CleanOrderEventListener] 自动调度成功: orderId={}, deviceId={}",
event.getOrderId(), result.getAssigneeId());
} else {
log.warn("[CleanOrderEventListener] 自动调度失败: orderId={}, reason={}",
@@ -134,10 +145,63 @@ public class CleanOrderEventListener {
}
}
+ /**
+ * 异步重置客流计数器
+ */
+ @Async("ops-task-executor")
+ public void asyncResetTrafficCounter(OrderCreatedEvent event) {
+ try {
+ Long deviceId = (Long) event.getPayload().get("triggerDeviceId");
+ @SuppressWarnings("unchecked")
+ Map
+ * 注意:设备状态由 BadgeDeviceStatusServiceImpl 统一管理
+ * 这里只处理扩展表时间记录和通知发送
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderStateChanged(OrderStateChangedEvent event) {
@@ -145,8 +209,8 @@ public class CleanOrderEventListener {
return;
}
- log.info("[CleanOrderEventListener] 状态变更: orderId={}, {} -> {}, operatorId={}",
- event.getOrderId(), event.getOldStatus(), event.getNewStatus(), event.getOperatorId());
+ log.info("[CleanOrderEventListener] 状态变更: orderId={}, {} -> {}",
+ event.getOrderId(), event.getOldStatus(), event.getNewStatus());
switch (event.getNewStatus()) {
case DISPATCHED:
@@ -165,7 +229,8 @@ public class CleanOrderEventListener {
handleCompleted(event);
break;
case CANCELLED:
- handleCancelled(event);
+ // 设备状态由 BadgeDeviceStatusEventListener 统一处理
+ log.debug("[CleanOrderEventListener] CANCELLED 状态已处理: orderId={}", event.getOrderId());
break;
default:
break;
@@ -177,8 +242,28 @@ public class CleanOrderEventListener {
*/
@Async("ops-task-executor")
public void handleDispatched(OrderStateChangedEvent event) {
- // 发送新工单通知(语音+震动+站内信)
- sendNewOrderNotification(event.getOperatorId(), event.getOrderId());
+ Long orderId = event.getOrderId();
+
+ // 1. 记录下发时间到扩展表(用于计算响应时长)
+ recordDispatchedTime(orderId);
+
+ // 2. 如果是从 PAUSED 恢复,处理暂停结束逻辑
+ if (event.getOldStatus() == com.viewsh.module.ops.enums.WorkOrderStatusEnum.PAUSED) {
+ handlePauseEnd(orderId);
+ }
+
+ // 3. 优先使用 assigneeId(自动调度场景),其次使用 operatorId(手动派单场景)
+ Long deviceId = event.getPayloadLong("assigneeId");
+ if (deviceId == null) {
+ deviceId = event.getOperatorId();
+ }
+ if (deviceId != null) {
+ // 发送新工单通知(语音+站内信)
+ sendNewOrderNotification(deviceId, orderId);
+ } else {
+ log.warn("[CleanOrderEventListener] DISPATCHED 事件缺少 assigneeId 和 operatorId: orderId={}",
+ orderId);
+ }
}
/**
@@ -186,21 +271,25 @@ public class CleanOrderEventListener {
*/
@Async("ops-task-executor")
public void handleConfirmed(OrderStateChangedEvent event) {
- Long cleanerId = event.getOperatorId();
- if (cleanerId == null) {
- return;
+ Long orderId = event.getOrderId();
+
+ // 获取 deviceId(优先从 payload 获取,其次使用 operatorId)
+ Long deviceId = event.getPayloadLong("deviceId");
+ if (deviceId == null) {
+ deviceId = event.getOperatorId();
}
- // 更新保洁员状态为 BUSY
- cleanerStatusService.updateStatus(cleanerId,
- com.viewsh.module.ops.enums.CleanerStatusEnum.BUSY,
- "确认工单");
+ // 记录业务日志
+ recordOrderConfirmedLog(orderId, deviceId, event);
- // 设置当前工单
- cleanerStatusService.setCurrentWorkOrder(cleanerId, event.getOrderId(),
- opsOrderMapper.selectById(event.getOrderId()).getOrderCode());
+ if (deviceId != null) {
+ // 发送确认成功语音播报(使用统一模板)
+ OpsOrderDO order = opsOrderMapper.selectById(orderId);
+ String confirmMessage = CleanNotificationConstants.VoiceBuilder.buildOrderConfirmed(order);
+ playVoice(deviceId, confirmMessage);
+ }
- log.info("[CleanOrderEventListener] 保洁员确认工单,状态更新为BUSY: cleanerId={}", cleanerId);
+ log.info("[CleanOrderEventListener] 工单已确认: orderId={}, deviceId={}", orderId, deviceId);
}
/**
@@ -216,35 +305,28 @@ public class CleanOrderEventListener {
updateObj.setArrivedTime(LocalDateTime.now());
cleanExtMapper.insertOnDuplicateKeyUpdate(updateObj);
- log.info("[CleanOrderEventListener] 保洁员已到岗: orderId={}", orderId);
+ // 2. 计算并更新响应时长(下发→到岗,排除暂停时间)
+ updateResponseSeconds(orderId);
+
+ log.info("[CleanOrderEventListener] 到岗时间已记录: orderId={}", orderId);
}
/**
* 处理暂停状态
+ *
+ * 注意:PAUSED 状态不是由 IoT 消息触发的,而是通过服务层 (REST API) 调用:
+ * - OrderLifecycleManager.pauseOrder() - 用户主动暂停
+ * - OrderLifecycleManager.interruptOrder() - P0任务打断
*/
@Async("ops-task-executor")
public void handlePaused(OrderStateChangedEvent event) {
Long orderId = event.getOrderId();
- Long operatorId = event.getOperatorId();
- if (operatorId == null) {
- return;
- }
- // 检查是否是被 P0 任务打断
- String interruptReason = event.getPayloadString("interruptReason");
- if ("P0_TASK_INTERRUPT".equals(interruptReason)) {
- log.warn("[CleanOrderEventListener] 保洁任务被P0任务打断: orderId={}", orderId);
- // 释放保洁员资源
- cleanerStatusService.clearCurrentWorkOrder(operatorId);
- } else {
- // 普通暂停:更新保洁员状态为 PAUSED
- cleanerStatusService.updateStatus(operatorId,
- com.viewsh.module.ops.enums.CleanerStatusEnum.PAUSED,
- event.getRemark() != null ? event.getRemark() : "任务暂停");
- }
-
- // 记录暂停开始时间
+ // 记录暂停开始时间到扩展表
recordPauseStartTime(orderId);
+
+ // 设备状态由 BadgeDeviceStatusEventListener 统一处理
+ log.info("[CleanOrderEventListener] 暂停时间已记录: orderId={}", orderId);
}
/**
@@ -254,35 +336,27 @@ public class CleanOrderEventListener {
public void handleCompleted(OrderStateChangedEvent event) {
Long orderId = event.getOrderId();
- // 1. 计算作业时长
- Integer actualDuration = cleanOrderService.calculateActualDuration(orderId);
+ // 获取 deviceId(优先从 payload 获取,其次从工单获取)
+ Long deviceId = event.getPayloadLong("deviceId");
- // 2. 记录完成时间到扩展表
+ // 记录完成时间到扩展表
OpsOrderCleanExtDO updateObj = new OpsOrderCleanExtDO();
updateObj.setOpsOrderId(orderId);
updateObj.setCompletedTime(LocalDateTime.now());
cleanExtMapper.insertOnDuplicateKeyUpdate(updateObj);
- log.info("[CleanOrderEventListener] 保洁作业完成: orderId={}, actualDuration={}秒", orderId, actualDuration);
- }
+ // 记录业务日志
+ recordOrderCompletedLog(orderId, deviceId, event);
- /**
- * 处理取消状态
- */
- @Async("ops-task-executor")
- public void handleCancelled(OrderStateChangedEvent event) {
- Long operatorId = event.getOperatorId();
- if (operatorId != null) {
- // 清理保洁员当前工单
- cleanerStatusService.clearCurrentWorkOrder(operatorId);
- }
- log.info("[CleanOrderEventListener] 保洁工单已取消: orderId={}", event.getOrderId());
+ log.info("[CleanOrderEventListener] 完成时间已记录: orderId={}, deviceId={}", orderId, deviceId);
}
// ==================== 工单完成事件 ====================
/**
* 监听工单完成事件
+ *
+ * 触发自动调度下一个任务
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCompleted(OrderCompletedEvent event) {
@@ -290,19 +364,16 @@ public class CleanOrderEventListener {
return;
}
- log.info("[CleanOrderEventListener] 工单完成: orderId={}, assigneeId={}",
+ log.info("[CleanOrderEventListener] 工单完成: orderId={}, deviceId={}",
event.getOrderId(), event.getAssigneeId());
- Long assigneeId = event.getAssigneeId();
- if (assigneeId != null) {
- // 1. 清理当前工单
- cleanerStatusService.clearCurrentWorkOrder(assigneeId);
-
- // 2. 自动推送下一个任务(异步)
- asyncDispatchNext(assigneeId, event.getOrderId());
+ Long deviceId = event.getAssigneeId();
+ if (deviceId != null) {
+ // 自动推送下一个任务(异步)
+ asyncDispatchNext(deviceId, event.getOrderId());
}
- // 3. 发送完成通知(异步)
+ // 发送完成通知(异步)
asyncSendOrderCompletedNotification(event.getOrderId());
}
@@ -310,8 +381,8 @@ public class CleanOrderEventListener {
* 异步推送下一个任务
*/
@Async("ops-task-executor")
- public void asyncDispatchNext(Long cleanerId, Long completedOrderId) {
- cleanOrderService.autoDispatchNextOrder(completedOrderId, cleanerId);
+ public void asyncDispatchNext(Long deviceId, Long completedOrderId) {
+ cleanOrderService.autoDispatchNextOrder(completedOrderId, deviceId);
}
/**
@@ -325,10 +396,10 @@ public class CleanOrderEventListener {
// ==================== 通知方法 ====================
/**
- * 发送新工单通知(语音播报 + 震动提醒 + 站内信)
+ * 发送新工单通知(语音播报 + 站内信)
*/
@Async("ops-task-executor")
- public void sendNewOrderNotification(Long cleanerId, Long orderId) {
+ public void sendNewOrderNotification(Long deviceId, Long orderId) {
try {
OpsOrderDO order = opsOrderMapper.selectById(orderId);
if (order == null) {
@@ -336,25 +407,23 @@ public class CleanOrderEventListener {
return;
}
- log.info("[新工单通知] cleanerId={}, orderId={}", cleanerId, orderId);
+ log.info("[新工单通知] deviceId={}, orderId={}", deviceId, orderId);
- // 1. 语音播报
- playVoice(cleanerId, CleanNotificationConstants.VoiceMessage.NEW_ORDER);
+ // 1. 语音播报(使用统一模板构建器)
+ String voiceMessage = CleanNotificationConstants.VoiceBuilder.buildNewOrder(order, true);
+ playVoice(deviceId, voiceMessage);
- // 2. 震动提醒
- vibrate(cleanerId, CleanNotificationConstants.VibrationDuration.NORMAL);
-
- // 3. 发送站内信
- sendNotifyMessageToMember(cleanerId,
+ // 2. 发送站内信(暂时发送到管理员)
+ sendNotifyMessage(1L,
CleanNotificationConstants.TemplateCode.NEW_ORDER,
CleanNotificationConstants.NotifyParamsBuilder.newOrderParams(
order.getOrderCode(),
order.getTitle(),
- getAreaName(order.getAreaId())
+ CleanNotificationConstants.VoiceBuilder.getAreaName(order.getLocation())
));
} catch (Exception e) {
- log.error("[新工单通知] 发送失败: cleanerId={}, orderId={}", cleanerId, orderId, e);
+ log.error("[新工单通知] 发送失败: deviceId={}, orderId={}", deviceId, orderId, e);
}
}
@@ -362,26 +431,23 @@ public class CleanOrderEventListener {
* 发送待办增加通知
*/
@Async("ops-task-executor")
- public void sendQueuedOrderNotification(Long cleanerId, int queueCount) {
+ public void sendQueuedOrderNotification(Long deviceId, int queueCount) {
try {
- log.info("[待办增加通知] cleanerId={}, queueCount={}", cleanerId, queueCount);
+ log.info("[待办增加通知] deviceId={}, queueCount={}", deviceId, queueCount);
- // 1. 语音播报
- String voiceMessage = CleanNotificationConstants.VoiceHelper.formatQueuedOrder(queueCount, 1);
- playVoice(cleanerId, voiceMessage);
+ // 1. 语音播报(使用统一模板构建器)
+ String voiceMessage = CleanNotificationConstants.VoiceBuilder.buildQueuedOrder(queueCount);
+ playVoice(deviceId, voiceMessage);
- // 2. 震动提醒
- vibrate(cleanerId, CleanNotificationConstants.VibrationDuration.LIGHT);
-
- // 3. 发送站内信(待办数量较多时)
+ // 2. 发送站内信(待办数量较多时)
if (queueCount >= 3) {
- sendNotifyMessageToMember(cleanerId,
+ sendNotifyMessage(1L,
CleanNotificationConstants.TemplateCode.QUEUED_ORDER,
CleanNotificationConstants.NotifyParamsBuilder.queuedOrderParams(queueCount, 1));
}
} catch (Exception e) {
- log.error("[待办增加通知] 发送失败: cleanerId={}", cleanerId, e);
+ log.error("[待办增加通知] 发送失败: deviceId={}", deviceId, e);
}
}
@@ -389,24 +455,21 @@ public class CleanOrderEventListener {
* 发送下一个任务通知
*/
@Async("ops-task-executor")
- public void sendNextTaskNotification(Long cleanerId, int queueCount, String orderTitle) {
+ public void sendNextTaskNotification(Long deviceId, int queueCount, String orderTitle) {
try {
- log.info("[下一任务通知] cleanerId={}, queueCount={}, title={}", cleanerId, queueCount, orderTitle);
+ log.info("[下一任务通知] deviceId={}, queueCount={}, title={}", deviceId, queueCount, orderTitle);
- // 1. 语音播报
- String voiceMessage = CleanNotificationConstants.VoiceHelper.formatNextTask(queueCount, orderTitle);
- playVoice(cleanerId, voiceMessage);
+ // 1. 语音播报(使用统一模板构建器)
+ String voiceMessage = CleanNotificationConstants.VoiceBuilder.buildNextTask(orderTitle);
+ playVoice(deviceId, voiceMessage);
- // 2. 震动提醒
- vibrate(cleanerId, CleanNotificationConstants.VibrationDuration.NORMAL);
-
- // 3. 发送站内信
- sendNotifyMessageToMember(cleanerId,
+ // 2. 发送站内信
+ sendNotifyMessage(1L,
CleanNotificationConstants.TemplateCode.NEXT_TASK,
CleanNotificationConstants.NotifyParamsBuilder.nextTaskParams(queueCount, orderTitle));
} catch (Exception e) {
- log.error("[下一任务通知] 发送失败: cleanerId={}", cleanerId, e);
+ log.error("[下一任务通知] 发送失败: deviceId={}", deviceId, e);
}
}
@@ -414,24 +477,21 @@ public class CleanOrderEventListener {
* 发送P0紧急任务插队通知
*/
@Async("ops-task-executor")
- public void sendPriorityUpgradeNotification(Long cleanerId, String orderCode) {
+ public void sendPriorityUpgradeNotification(Long deviceId, String orderCode) {
try {
- log.warn("[P0紧急通知] cleanerId={}, orderCode={}", cleanerId, orderCode);
+ log.warn("[P0紧急通知] deviceId={}, orderCode={}", deviceId, orderCode);
- // 1. 语音播报
- String voiceMessage = CleanNotificationConstants.VoiceHelper.formatPriorityUpgrade(orderCode);
- playVoice(cleanerId, voiceMessage);
+ // 1. 语音播报(使用统一模板构建器)
+ String voiceMessage = CleanNotificationConstants.VoiceBuilder.buildPriorityUpgrade(orderCode);
+ playVoice(deviceId, voiceMessage);
- // 2. 强烈震动提醒
- vibrate(cleanerId, CleanNotificationConstants.VibrationDuration.STRONG);
-
- // 3. 发送站内信
- sendNotifyMessageToMember(cleanerId,
+ // 2. 发送站内信
+ sendNotifyMessage(1L,
CleanNotificationConstants.TemplateCode.PRIORITY_UPGRADE,
CleanNotificationConstants.NotifyParamsBuilder.priorityUpgradeParams(orderCode, "P0紧急任务"));
} catch (Exception e) {
- log.error("[P0紧急通知] 发送失败: cleanerId={}", cleanerId, e);
+ log.error("[P0紧急通知] 发送失败: deviceId={}", deviceId, e);
}
}
@@ -439,24 +499,21 @@ public class CleanOrderEventListener {
* 发送任务恢复通知
*/
@Async("ops-task-executor")
- public void sendTaskResumedNotification(Long cleanerId, String areaName) {
+ public void sendTaskResumedNotification(Long deviceId, String areaName) {
try {
- log.info("[任务恢复通知] cleanerId={}, areaName={}", cleanerId, areaName);
+ log.info("[任务恢复通知] deviceId={}, areaName={}", deviceId, areaName);
- // 1. 语音播报
- String voiceMessage = CleanNotificationConstants.VoiceHelper.formatTaskResumed(areaName);
- playVoice(cleanerId, voiceMessage);
+ // 1. 语音播报(使用统一模板构建器)
+ String voiceMessage = CleanNotificationConstants.VoiceBuilder.buildTaskResumed(areaName);
+ playVoice(deviceId, voiceMessage);
- // 2. 震动提醒
- vibrate(cleanerId, CleanNotificationConstants.VibrationDuration.NORMAL);
-
- // 3. 发送站内信
- sendNotifyMessageToMember(cleanerId,
+ // 2. 发送站内信
+ sendNotifyMessage(1L,
CleanNotificationConstants.TemplateCode.TASK_RESUMED,
CleanNotificationConstants.NotifyParamsBuilder.taskResumedParams(areaName));
} catch (Exception e) {
- log.error("[任务恢复通知] 发送失败: cleanerId={}", cleanerId, e);
+ log.error("[任务恢复通知] 发送失败: deviceId={}", deviceId, e);
}
}
@@ -485,73 +542,31 @@ public class CleanOrderEventListener {
* 播放语音(供外部调用)
*/
@Async("ops-task-executor")
- public void playVoiceForNewOrder(Long cleanerId) {
- playVoice(cleanerId, CleanNotificationConstants.VoiceMessage.NEW_ORDER);
+ public void playVoiceForNewOrder(Long deviceId) {
+ playVoice(deviceId, CleanNotificationConstants.VoiceTemplate.NEW_ORDER_SHORT);
}
- // ==================== IoT 设备操作���法 ====================
+ // ==================== 设备操作方法 ====================
/**
* 语音播报
*/
- private void playVoice(Long cleanerId, String message) {
+ private void playVoice(Long deviceId, String message) {
try {
- Long deviceId = getBadgeDeviceId(cleanerId);
- if (deviceId == null) {
- log.warn("[语音播报] 保洁员无关联工牌设备: cleanerId={}", cleanerId);
- return;
- }
-
voiceBroadcastService.broadcast(deviceId, message);
- log.debug("[语音播报] 调用成功: cleanerId={}, deviceId={}, message={}", cleanerId, deviceId, message);
+ log.debug("[语音播报] 调用成功: deviceId={}, message={}", deviceId, message);
} catch (Exception e) {
- log.error("[语音播报] 调用失败: cleanerId={}, message={}", cleanerId, message, e);
+ log.error("[语音播报] 调用失败: deviceId={}, message={}", deviceId, message, e);
}
}
- /**
- * 震动提醒
- */
- private void vibrate(Long cleanerId, int durationMs) {
- try {
- Long deviceId = getBadgeDeviceId(cleanerId);
- if (deviceId == null) {
- return;
- }
-
- IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO();
- reqDTO.setDeviceId(deviceId);
- reqDTO.setIdentifier("vibrate");
- reqDTO.setParams(MapUtil.
+ * 计算暂停时长并累加到 totalPauseSeconds
+ */
+ private void handlePauseEnd(Long orderId) {
+ try {
+ OpsOrderCleanExtDO ext = cleanExtMapper.selectByOpsOrderId(orderId);
+ if (ext == null || ext.getPauseStartTime() == null) {
+ log.warn("[CleanOrderEventListener] 暂停开始时间不存在,无法计算暂停时长: orderId={}", orderId);
+ return;
+ }
+
+ // 计算暂停时长(秒)
+ LocalDateTime pauseEnd = LocalDateTime.now();
+ long pauseSeconds = java.time.Duration.between(ext.getPauseStartTime(), pauseEnd).getSeconds();
+
+ // 获取现有累计暂停时长
+ int totalPauseSeconds = ext.getTotalPauseSeconds() != null ? ext.getTotalPauseSeconds() : 0;
+ totalPauseSeconds += (int) pauseSeconds;
+
+ // 更新暂停结束时间和累计暂停时长
+ OpsOrderCleanExtDO updateObj = OpsOrderCleanExtDO.builder()
+ .opsOrderId(orderId)
+ .pauseEndTime(pauseEnd)
+ .totalPauseSeconds(totalPauseSeconds)
+ .firstDispatchedTime(ext.getFirstDispatchedTime()) // 保留首次下发时间
+ .build();
+ cleanExtMapper.insertOrUpdateSelective(updateObj);
+
+ log.info("[CleanOrderEventListener] 暂停时长已累加: orderId={}, 本次={}秒, 累计={}秒",
+ orderId, pauseSeconds, totalPauseSeconds);
+
+ } catch (Exception e) {
+ log.error("[CleanOrderEventListener] 处理暂停结束失败: orderId={}", orderId, e);
+ }
+ }
+
+ /**
+ * 计算并更新响应时长
+ *
+ * 响应时长 = 到岗时间 - 首次下发时间 - 累计暂停时长
+ */
+ private void updateResponseSeconds(Long orderId) {
+ try {
+ OpsOrderCleanExtDO ext = cleanExtMapper.selectByOpsOrderId(orderId);
+ if (ext == null || ext.getFirstDispatchedTime() == null || ext.getArrivedTime() == null) {
+ log.warn("[CleanOrderEventListener] 缺少必要时间数据,无法计算响应时长: orderId={}", orderId);
+ return;
+ }
+
+ // 计算响应时长(秒):到岗 - 首次下发 - 暂停时长
+ long responseSeconds = java.time.Duration.between(ext.getFirstDispatchedTime(), ext.getArrivedTime()).getSeconds();
+ int totalPauseSeconds = ext.getTotalPauseSeconds() != null ? ext.getTotalPauseSeconds() : 0;
+ responseSeconds = responseSeconds - totalPauseSeconds;
+
+ // 确保不为负数
+ responseSeconds = Math.max(responseSeconds, 0);
+
+ // 更新工单主表的响应时长
+ OpsOrderDO orderUpdate = new OpsOrderDO();
+ orderUpdate.setId(orderId);
+ orderUpdate.setResponseSeconds((int) responseSeconds);
+ opsOrderMapper.updateById(orderUpdate);
+
+ log.info("[CleanOrderEventListener] 响应时长已更新: orderId={}, 响应时长={}秒", orderId, responseSeconds);
+
+ } catch (Exception e) {
+ log.error("[CleanOrderEventListener] 更新响应时长失败: orderId={}", orderId, e);
+ }
+ }
+
+ /**
+ * 记录工单确认业务日志
+ */
+ private void recordOrderConfirmedLog(Long orderId, Long deviceId, OrderStateChangedEvent event) {
+ try {
+ eventLogRecorder.record(EventLogRecord.builder()
+ .module("clean")
+ .domain(EventDomain.DEVICE)
+ .eventType("ORDER_CONFIRM")
+ .message("工单已确认 (工牌按键)")
+ .targetId(orderId)
+ .targetType("order")
+ .deviceId(deviceId)
+ .personId(deviceId)
+ .build());
+
+ } catch (Exception e) {
+ log.warn("[CleanOrderEventListener] 记录确认业务日志失败: orderId={}", orderId, e);
+ }
+ }
+
+ /**
+ * 记录工单完成业务日志
+ */
+ private void recordOrderCompletedLog(Long orderId, Long deviceId, OrderStateChangedEvent event) {
+ try {
+ // 从 payload 中提取完成原因和作业时长
+ String triggerSource = (String) event.getPayload().get("triggerSource");
+ String deviceKey = (String) event.getPayload().get("deviceKey");
+
+ // 构建日志消息
+ String message = "工单已完成";
+ if ("SIGNAL_LOSS_TIMEOUT".equals(triggerSource)) {
+ Object durationMs = event.getPayload().get("durationMs");
+ String durationInfo = "";
+ if (durationMs != null) {
+ long durationMinutes = ((Number) durationMs).longValue() / 60000;
+ durationInfo = String.format(",作业时长: %d分钟", durationMinutes);
+ }
+ message = "信号丢失超时自动完成 [设备:" + deviceKey + durationInfo + "]";
+ }
+
+ EventLogRecord.EventLogRecordBuilder builder = EventLogRecord.builder()
+ .module("clean")
+ .domain(EventDomain.BEACON)
+ .eventType("ORDER_COMPLETED")
+ .message(message)
+ .targetId(orderId)
+ .targetType("order");
+
+ if (deviceId != null) {
+ builder.deviceId(deviceId).personId(deviceId);
+ }
+
+ eventLogRecorder.record(builder.build());
+
+ } catch (Exception e) {
+ log.warn("[CleanOrderEventListener] 记录完成业务日志失败: orderId={}", orderId, e);
+ }
+ }
}
+