diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/consumer/CleanOrderCreateEventHandler.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/consumer/CleanOrderCreateEventHandler.java index ca1b621..33ecab7 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/consumer/CleanOrderCreateEventHandler.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/integration/consumer/CleanOrderCreateEventHandler.java @@ -36,8 +36,8 @@ import java.util.concurrent.TimeUnit; *
* 客流触发逻辑(周期化): * 1. 无活跃工单 → 创建新工单 → 标记活跃 → 重置阈值 - * 2. 有未派发工单(PENDING/QUEUED) → 升级优先级一级 → 重置阈值 - * 3. 有已派发工单(DISPATCHED/CONFIRMED/ARRIVED) → 忽略 → 重置阈值 + * 2. 有排队中工单(PENDING/QUEUED) → 升级优先级一级 → 重置阈值 + * 3. 有已派发/已到达工单(DISPATCHED/CONFIRMED/ARRIVED) → 静默处理,不升级不创建 → 重置阈值 * 4. 已是 P0 → 不升级,记录审计日志 → 重置阈值 *
* RocketMQ 配置:
@@ -69,9 +69,9 @@ public class CleanOrderCreateEventHandler implements RocketMQListener
- * 仅处理客流触发的工单。清除后下次客流达标将创建新工单(新周期)。
+ * 顺序至关重要:先重置计数器确保设备归零,再清除活跃标记开放新周期。
+ * 如果先清标记后重置,存在竞态窗口——已发出的 MQ 消息在标记清除后到达,
+ * 此时计数器尚未归零,会误创建基于残留计数的工单。
+ *
+ * 如果计数器重置失败,仍然清除活跃标记(降级处理),避免区域永远被锁死。
+ */
+ private void clearTrafficActiveOrderOnComplete(OrderStateChangedEvent event) {
+ try {
+ OpsOrderDO order = opsOrderMapper.selectById(event.getOrderId());
+ if (order != null && "IOT_TRAFFIC".equals(order.getTriggerSource()) && order.getAreaId() != null) {
+ // 1. 先重置 IoT 客流计数器(阻塞等待结果)
+ boolean resetOk = resetTrafficCounterOnComplete(order.getTriggerDeviceId(), order.getAreaId());
+
+ // 2. 再清除活跃标记
+ trafficActiveOrderRedisDAO.removeActive(order.getAreaId());
+
+ if (resetOk) {
+ log.info("[CleanOrderEventListener] 客流工单完成,计数器已重置,活跃标记已清除: areaId={}", order.getAreaId());
+ } else {
+ log.warn("[CleanOrderEventListener] 客流工单完成,计数器重置失败但活跃标记已清除(降级): areaId={}", order.getAreaId());
+ }
+ }
+ } catch (Exception e) {
+ log.warn("[CleanOrderEventListener] 清除客流活跃工单标记失败: orderId={}", event.getOrderId(), e);
+ }
+ }
+
+ /**
+ * 工单取消时,仅清除活跃标记,不重置计数器
+ *
+ * 取消意味着区域未被清洁,客流计数应保留,以便尽快重新触发工单。
*/
private void clearTrafficActiveOrder(OrderStateChangedEvent event) {
try {
OpsOrderDO order = opsOrderMapper.selectById(event.getOrderId());
if (order != null && "IOT_TRAFFIC".equals(order.getTriggerSource()) && order.getAreaId() != null) {
trafficActiveOrderRedisDAO.removeActive(order.getAreaId());
- log.info("[CleanOrderEventListener] 客流工单周期结束,已清除区域{}活跃标记", order.getAreaId());
+ log.info("[CleanOrderEventListener] 客流工单取消,已清除区域{}活跃标记(计数器保留)", order.getAreaId());
}
} catch (Exception e) {
log.warn("[CleanOrderEventListener] 清除客流活跃工单标记失败: orderId={}", event.getOrderId(), e);
}
}
+ /**
+ * 工单完成时重置客流计数器
+ *
+ * @return true 重置成功,false 重置失败或无设备ID
+ */
+ private boolean resetTrafficCounterOnComplete(Long deviceId, Long areaId) {
+ if (deviceId == null) {
+ return false;
+ }
+ try {
+ ResetTrafficCounterReqDTO reqDTO = ResetTrafficCounterReqDTO.builder()
+ .deviceId(deviceId)
+ .remark("工单完成后重置计数器,清除作业期间残留计数")
+ .build();
+ var result = iotDeviceControlApi.resetTrafficCounter(reqDTO);
+ if (result.getData() != null && result.getData()) {
+ log.info("[CleanOrderEventListener] 工单完成,计数器重置成功: deviceId={}, areaId={}", deviceId, areaId);
+ return true;
+ } else {
+ log.warn("[CleanOrderEventListener] 工单完成,计数器重置失败: deviceId={}, areaId={}", deviceId, areaId);
+ return false;
+ }
+ } catch (Exception e) {
+ log.warn("[CleanOrderEventListener] 工单完成,计数器重置异常: deviceId={}", deviceId, e);
+ return false;
+ }
+ }
+
/**
* 记录暂停开始时间
*/