* | PAUSED |
@@ -80,9 +78,6 @@ public class BadgeDeviceStatusEventListener {
@Resource
private OrderQueueService orderQueueService;
- @Resource
- private EventLogRecorder eventLogRecorder;
-
/**
* 监听工单状态变更事件,同步更新设备工单关联
*
@@ -117,7 +112,7 @@ public class BadgeDeviceStatusEventListener {
}
// 根据状态更新设备工单关联
- handleOrderStatusTransition(deviceId, orderId, newStatus, event);
+ handleOrderStatusTransition(deviceId, orderId, newStatus, event, order);
} catch (Exception e) {
log.error("[BadgeDeviceStatusEventListener] 处理工单状态变更事件失败: orderId={}", event.getOrderId(), e);
@@ -126,177 +121,154 @@ public class BadgeDeviceStatusEventListener {
/**
* 根据工单状态更新设备工牌关联
+ *
+ * 设计原则:每个状态只更新变化的部分,避免重复设置相同数据
+ *
+ * 状态处理逻辑:
+ *
+ * - DISPATCHED: 首次设置工单关联(orderId + areaId)
+ * - CONFIRMED: 只更新状态,orderId/areaId 已在 DISPATCHED 设置
+ * - ARRIVED: 更新状态 + 信标MAC
+ * - PAUSED: 只更新设备状态,工单关联保持
+ * - COMPLETED: 清除工单关联,根据等待任务决定设备状态
+ * - CANCELLED: 清除工单关联(如果是当前工单),根据等待任务决定设备状态
+ *
*/
private void handleOrderStatusTransition(Long deviceId, Long orderId, WorkOrderStatusEnum newStatus,
- OrderStateChangedEvent event) {
- var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId);
+ OrderStateChangedEvent event, OpsOrderDO order) {
switch (newStatus) {
case DISPATCHED:
- // 工单已推送到工牌,设置工单关联和区域信息
- // currentAreaId 应该设置为工单所属区域,而非设备物理位置
- badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY, null, "新工单已推送");
-
- OpsOrderDO order = opsOrderMapper.selectById(orderId);
- if (order != null && order.getAreaId() != null) {
- badgeDeviceStatusService.setCurrentOrderInfo(
- deviceId, orderId,
- event.getNewStatus().getStatus(),
- order.getAreaId(),
- null
- );
- log.info("[BadgeDeviceStatusEventListener] 工单已推送,设备状态转为 BUSY: deviceId={}, orderId={}, areaId={}",
- deviceId, orderId, order.getAreaId());
- } else {
- badgeDeviceStatusService.setCurrentOrder(deviceId, orderId);
- log.info("[BadgeDeviceStatusEventListener] 工单已推送,设备状态转为 BUSY: deviceId={}, orderId={}", deviceId,
- orderId);
- }
+ handleDispatched(deviceId, orderId, order);
break;
case CONFIRMED:
- // 设备按键确认,更新工单关联和区域信息
- // currentAreaId 应该设置为工单所属区域,而非设备物理位置
- OpsOrderDO order = opsOrderMapper.selectById(orderId);
- if (order != null && order.getAreaId() != null) {
- badgeDeviceStatusService.setCurrentOrderInfo(
- deviceId, orderId,
- event.getNewStatus().getStatus(),
- order.getAreaId(),
- null
- );
- log.debug("[BadgeDeviceStatusEventListener] 工单已确认,更新区域信息: deviceId={}, orderId={}, areaId={}",
- deviceId, orderId, order.getAreaId());
- } else {
- badgeDeviceStatusService.setCurrentOrder(deviceId, orderId);
- log.debug("[BadgeDeviceStatusEventListener] 工单已确认: deviceId={}, orderId={}", deviceId, orderId);
- }
+ handleConfirmed(deviceId, orderId);
break;
case ARRIVED:
- // 设备已到岗,设置完整的工单信息(areaId, beaconMac)
- updateDeviceOrderInfo(deviceId, orderId, event);
- recordOrderArrivedLog(orderId, deviceId, event);
- log.info("[BadgeDeviceStatusEventListener] 工单已到岗,更新设备工单信息: deviceId={}, orderId={}", deviceId, orderId);
+ handleArrived(deviceId, orderId, event);
break;
case PAUSED:
- // 检查是否是 P0 打断场景
- Long urgentOrderId = event.getPayloadLong("urgentOrderId");
- if (urgentOrderId != null) {
- // P0 打断场景:不修改设备状态,保持当前状态
- // 紧接着会有 P0 工单的 DISPATCHED 事件,将当前工单更新为 P0 工单
- log.info(
- "[BadgeDeviceStatusEventListener] P0打断场景,工单已暂停,等待P0工单派发: pausedOrderId={}, urgentOrderId={}, deviceId={}",
- orderId, urgentOrderId, deviceId);
- } else {
- // 普通暂停场景:设备状态转为 PAUSED
- badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.PAUSED, null, "任务暂停");
- log.info("[BadgeDeviceStatusEventListener] 任务暂停,设备状态转为 PAUSED: deviceId={}", deviceId);
- }
+ handlePaused(deviceId, orderId, event);
break;
case COMPLETED:
- // 任务完成,清除工单关联
- badgeDeviceStatusService.clearCurrentOrder(deviceId);
-
- // 检查是否有等待任务,决定设备状态
- if (waitingTasks != null && !waitingTasks.isEmpty()) {
- // 有等待任务,设备状态保持 BUSY(由后续 DISPATCHED 事件更新)
- log.info("[BadgeDeviceStatusEventListener] 任务完成,有{}个等待任务,设备保持 BUSY: deviceId={}",
- waitingTasks.size(), deviceId);
- } else {
- // 无等待任务,设备状态转为 IDLE
- badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE, null, "任务完成,无新任务");
- log.info("[BadgeDeviceStatusEventListener] 任务完成,无等待任务,设备转为 IDLE: deviceId={}", deviceId);
- }
+ handleCompleted(deviceId);
break;
case CANCELLED:
- // 检查被取消的工单是否是设备当前正在执行的工单
- BadgeDeviceStatusDTO deviceStatus = badgeDeviceStatusService.getBadgeStatus(deviceId);
- Long currentOrderId = deviceStatus != null ? deviceStatus.getCurrentOpsOrderId() : null;
-
- if (orderId.equals(currentOrderId)) {
- // 取消的是当前正在执行的工单,清除工单关联
- badgeDeviceStatusService.clearCurrentOrder(deviceId);
-
- // 检查是否有等待任务,决定设备状态
- if (waitingTasks != null && !waitingTasks.isEmpty()) {
- // 有等待任务,设备状态保持 BUSY(由后续 DISPATCHED 事件更新)
- log.info("[BadgeDeviceStatusEventListener] 当前工单已取消,有{}个等待任务,设备保持 BUSY: deviceId={}",
- waitingTasks.size(), deviceId);
- } else {
- // 无等待任务,设备状态转为 IDLE
- badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE, null, "工单已取消");
- log.info("[BadgeDeviceStatusEventListener] 当前工单已取消,无等待任务,设备转为 IDLE: deviceId={}", deviceId);
- }
- } else {
- // 取消的不是当前工单(可能是队列中的等待任务),不需要修改设备状态
- log.debug(
- "[BadgeDeviceStatusEventListener] 取消的工单非当前执行工单,跳过设备状态更新: cancelledOrderId={}, currentOrderId={}, deviceId={}",
- orderId, currentOrderId, deviceId);
- }
+ handleCancelled(deviceId, orderId);
break;
default:
- // 其他状态不处理
break;
}
}
/**
- * 更新设备工单信息(ARRIVED 状态专用)
- *
- * 设置完整的工单信息:工单ID、工单状态、区域ID、信标MAC
+ * 处理工单推送状态(首次设置工单关联)
*/
- private void updateDeviceOrderInfo(Long deviceId, Long orderId, OrderStateChangedEvent event) {
- try {
- // 从 payload 中提取信息
- Long areaId = event.getPayloadLong("areaId");
+ private void handleDispatched(Long deviceId, Long orderId, OpsOrderDO order) {
+ // 设备状态转为 BUSY
+ badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY, null, "新工单已推送");
- String beaconMac = null;
- Object beaconMacObj = event.getPayload().get("beaconMac");
- if (beaconMacObj != null) {
- beaconMac = String.valueOf(beaconMacObj);
- }
-
- // 使用 BadgeDeviceStatusService 设置完整工单信息
+ // 首次设置工单关联:orderId + status + areaId
+ if (order != null && order.getAreaId() != null) {
badgeDeviceStatusService.setCurrentOrderInfo(
- deviceId,
- orderId,
- event.getNewStatus().getStatus(),
- areaId,
- beaconMac);
-
- log.debug("[BadgeDeviceStatusEventListener] 设备工单信息已更新: deviceId={}, orderId={}, areaId={}, beaconMac={}",
- deviceId, orderId, areaId, beaconMac);
-
- } catch (Exception e) {
- log.error("[BadgeDeviceStatusEventListener] 更新设备工单信息失败: deviceId={}, orderId={}", deviceId, orderId, e);
+ deviceId, orderId,
+ WorkOrderStatusEnum.DISPATCHED.getStatus(),
+ order.getAreaId(),
+ null
+ );
+ log.info("[BadgeDeviceStatusEventListener] 工单已推送: deviceId={}, orderId={}, areaId={}",
+ deviceId, orderId, order.getAreaId());
+ } else {
+ badgeDeviceStatusService.setCurrentOrder(deviceId, orderId);
+ log.info("[BadgeDeviceStatusEventListener] 工单已推送(无区域信息): deviceId={}, orderId={}", deviceId, orderId);
}
}
/**
- * 记录工单到岗业务日志
+ * 处理工单确认状态(只更新状态,不重复设置 orderId/areaId)
*/
- private void recordOrderArrivedLog(Long orderId, Long deviceId, OrderStateChangedEvent event) {
- try {
- Long areaId = event.getPayloadLong("areaId");
- String deviceKey = (String) event.getPayload().get("deviceKey");
+ private void handleConfirmed(Long deviceId, Long orderId) {
+ // 只更新工单状态,orderId/areaId 已在 DISPATCHED 设置
+ badgeDeviceStatusService.updateOrderStatus(deviceId, WorkOrderStatusEnum.CONFIRMED.getStatus());
+ log.debug("[BadgeDeviceStatusEventListener] 工单已确认: deviceId={}, orderId={}", deviceId, orderId);
+ }
- eventLogRecorder.record(EventLogRecord.builder()
- .module("clean")
- .domain(EventDomain.BEACON)
- .eventType("ORDER_ARRIVED")
- .message(String.format("蓝牙信标自动到岗确认 [设备:%s, 区域:%d]", deviceKey, areaId))
- .targetId(orderId)
- .targetType("order")
- .deviceId(deviceId)
- .personId(deviceId)
- .build());
+ /**
+ * 处理工单到岗状态(更新状态 + 信标MAC)
+ */
+ private void handleArrived(Long deviceId, Long orderId, OrderStateChangedEvent event) {
+ // 从 payload 中提取信标信息
+ String beaconMac = (String) event.getPayload().get("beaconMac");
- } catch (Exception e) {
- log.warn("[BadgeDeviceStatusEventListener] 记录到岗业务日志失败: orderId={}", orderId, e);
+ // 更新状态和信标MAC(orderId/areaId 已设置)
+ badgeDeviceStatusService.updateOrderStatusAndBeacon(deviceId, orderId,
+ WorkOrderStatusEnum.ARRIVED.getStatus(), beaconMac);
+
+ log.debug("[BadgeDeviceStatusEventListener] 工单已到岗,更新信标: deviceId={}, orderId={}, beaconMac={}",
+ deviceId, orderId, beaconMac);
+ }
+
+ /**
+ * 处理工单暂停状态
+ */
+ private void handlePaused(Long deviceId, Long orderId, OrderStateChangedEvent event) {
+ Long urgentOrderId = event.getPayloadLong("urgentOrderId");
+ if (urgentOrderId != null) {
+ // P0 打断场景:不修改设备状态,等待 P0 工单的 DISPATCHED 事件
+ log.info("[BadgeDeviceStatusEventListener] P0打断场景,等待P0工单派发: pausedOrderId={}, urgentOrderId={}",
+ orderId, urgentOrderId);
+ } else {
+ // 普通暂停场景:设备状态转为 PAUSED
+ badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.PAUSED, null, "任务暂停");
+ log.info("[BadgeDeviceStatusEventListener] 任务暂停: deviceId={}", deviceId);
+ }
+ }
+
+ /**
+ * 处理工单完成状态(清除工单关联)
+ */
+ private void handleCompleted(Long deviceId) {
+ badgeDeviceStatusService.clearCurrentOrder(deviceId);
+
+ // 检查是否有等待任务,决定设备状态
+ var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId);
+ if (waitingTasks != null && !waitingTasks.isEmpty()) {
+ log.info("[BadgeDeviceStatusEventListener] 任务完成,有{}个等待任务,设备保持 BUSY: deviceId={}",
+ waitingTasks.size(), deviceId);
+ } else {
+ badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE, null, "任务完成,无新任务");
+ log.info("[BadgeDeviceStatusEventListener] 任务完成,无等待任务,设备转为 IDLE: deviceId={}", deviceId);
+ }
+ }
+
+ /**
+ * 处理工单取消状态
+ */
+ private void handleCancelled(Long deviceId, Long orderId) {
+ // 检查被取消的工单是否是设备当前正在执行的工单
+ BadgeDeviceStatusDTO deviceStatus = badgeDeviceStatusService.getBadgeStatus(deviceId);
+ Long currentOrderId = deviceStatus != null ? deviceStatus.getCurrentOpsOrderId() : null;
+
+ if (orderId.equals(currentOrderId)) {
+ badgeDeviceStatusService.clearCurrentOrder(deviceId);
+
+ // 检查是否有等待任务,决定设备状态
+ var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId);
+ if (waitingTasks != null && !waitingTasks.isEmpty()) {
+ log.info("[BadgeDeviceStatusEventListener] 当前工单已取消,有{}个等待任务,设备保持 BUSY: deviceId={}",
+ waitingTasks.size(), deviceId);
+ } else {
+ badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.IDLE, null, "工单已取消");
+ log.info("[BadgeDeviceStatusEventListener] 当前工单已取消,无等待任务,设备转为 IDLE: deviceId={}", deviceId);
+ }
+ } else {
+ log.debug("[BadgeDeviceStatusEventListener] 取消的工单非当前执行工单,跳过: cancelledOrderId={}, currentOrderId={}",
+ orderId, currentOrderId);
}
}
}
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 62ea445..a6022ca 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
@@ -359,6 +359,9 @@ public class CleanOrderEventListener {
playVoice(deviceId, arrivedMessage);
}
+ // 4. 记录到岗业务日志
+ recordOrderArrivedLog(orderId, deviceId, event);
+
log.info("[CleanOrderEventListener] 到岗时间已记录: orderId={}, deviceId={}", orderId, deviceId);
}
@@ -787,6 +790,32 @@ public class CleanOrderEventListener {
}
}
+ /**
+ * 记录工单到岗业务日志
+ */
+ private void recordOrderArrivedLog(Long orderId, Long deviceId, OrderStateChangedEvent event) {
+ try {
+ Long areaId = event.getPayloadLong("areaId");
+ String deviceKey = (String) event.getPayload().get("deviceKey");
+ String beaconMac = (String) event.getPayload().get("beaconMac");
+
+ eventLogRecorder.record(EventLogRecord.builder()
+ .module("clean")
+ .domain(EventDomain.BEACON)
+ .eventType("ORDER_ARRIVED")
+ .message(String.format("蓝牙信标自动到岗确认 [设备:%s, 区域:%d, 信标:%s]",
+ deviceKey, areaId, beaconMac))
+ .targetId(orderId)
+ .targetType("order")
+ .deviceId(deviceId)
+ .personId(deviceId)
+ .build());
+
+ } catch (Exception e) {
+ log.warn("[CleanOrderEventListener] 记录到岗业务日志失败: orderId={}", orderId, e);
+ }
+ }
+
/**
* 记录工单完成业务日志
*/
diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusService.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusService.java
index b3b3896..62c8544 100644
--- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusService.java
+++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusService.java
@@ -153,6 +153,18 @@ public interface BadgeDeviceStatusService {
*/
void updateOrderStatus(Long deviceId, String orderStatus);
+ /**
+ * 更新工单状态和信标MAC
+ *
+ * 用于到岗场景,同时更新工单状态和信标MAC地址
+ *
+ * @param deviceId 设备ID
+ * @param orderId 工单ID
+ * @param orderStatus 工单状态
+ * @param beaconMac 信标MAC地址(可为null)
+ */
+ void updateOrderStatusAndBeacon(Long deviceId, Long orderId, String orderStatus, String beaconMac);
+
/**
* 清除当前工单(包括工单ID、工单状态、信标MAC)
*
@@ -178,37 +190,6 @@ public interface BadgeDeviceStatusService {
*/
void updateBadgeArea(Long deviceId, Long areaId, String areaName);
- /**
- * 初始化区域设备索引
- *
- * 从 ops_area_device_relation 表加载 BADGE 类型的设备,
- * 建立区域到设备的索引关系
- */
- void initAreaDeviceIndex();
-
- /**
- * 刷新区域设备索引
- *
- * 重新从数据库加载区域设备关系
- */
- void refreshAreaDeviceIndex();
-
- /**
- * 将设备添加到区域索引
- *
- * @param deviceId 设备ID
- * @param areaId 区域ID
- */
- void addToAreaIndex(Long deviceId, Long areaId);
-
- /**
- * 从区域索引移除设备
- *
- * @param deviceId 设备ID
- * @param areaId 区域ID
- */
- void removeFromAreaIndex(Long deviceId, Long areaId);
-
// ==================== 设备管理 ====================
/**
diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java
index 3c2ae2d..73e736b 100644
--- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java
+++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/badge/BadgeDeviceStatusServiceImpl.java
@@ -53,8 +53,8 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
@Override
public void afterPropertiesSet() {
- // 启动时初始化区域设备索引
- initAreaDeviceIndex();
+ // 启动时初始化区域设备配置缓存
+ areaDeviceService.initConfigCache();
}
// ==================== 状态管理 ====================
@@ -163,13 +163,14 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
}
try {
- Set deviceIds = areaDeviceService.getDeviceIdsByArea(areaId);
+ // 直接从DB查询该区域的工牌设备ID列表
+ List deviceIds = areaDeviceService.getDeviceIdsByAreaAndType(areaId, "BADGE");
if (deviceIds.isEmpty()) {
return Collections.emptyList();
}
- // 使用批量查询,避免 N+1
- return batchGetBadgeStatus(new ArrayList<>(deviceIds)).stream()
+ // 批量查询设备状态,避免 N+1
+ return batchGetBadgeStatus(deviceIds).stream()
.filter(Objects::nonNull)
.filter(dto -> dto.getStatus() != null && dto.getStatus().isActive())
.sorted(Comparator.comparing(BadgeDeviceStatusDTO::getStatusChangeTime))
@@ -188,14 +189,14 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
}
try {
- // 使用读穿透方式获取设备ID列表,缓存未命中时会从数据库重建
- Set deviceIds = areaDeviceService.getDeviceIdsByArea(areaId);
+ // 直接从DB查询该区域的工牌设备ID列表
+ List deviceIds = areaDeviceService.getDeviceIdsByAreaAndType(areaId, "BADGE");
if (deviceIds.isEmpty()) {
return Collections.emptyList();
}
- // 使用批量查询,避免 N+1
- return batchGetBadgeStatus(new ArrayList<>(deviceIds)).stream()
+ // 批量查询设备状态,避免 N+1
+ return batchGetBadgeStatus(deviceIds).stream()
.filter(Objects::nonNull)
.filter(dto -> dto.getStatus() == BadgeDeviceStatusEnum.IDLE)
.sorted(Comparator.comparing(BadgeDeviceStatusDTO::getLastHeartbeatTime).reversed())
@@ -387,6 +388,26 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
}
}
+ @Override
+ public void updateOrderStatusAndBeacon(Long deviceId, Long orderId, String orderStatus, String beaconMac) {
+ if (deviceId == null) {
+ return;
+ }
+
+ try {
+ String key = BADGE_STATUS_KEY_PREFIX + deviceId;
+ // 更新工单状态和信标MAC(orderId 和 areaId 已在 DISPATCHED 时设置,不需要重复)
+ redisTemplate.opsForHash().put(key, "currentOrderStatus", orderStatus);
+ if (beaconMac != null) {
+ redisTemplate.opsForHash().put(key, "beaconMac", beaconMac);
+ }
+ log.debug("更新工单状态和信标: deviceId={}, orderId={}, orderStatus={}, beaconMac={}",
+ deviceId, orderId, orderStatus, beaconMac);
+ } catch (Exception e) {
+ log.error("更新工单状态和信标失败: deviceId={}, orderId={}", deviceId, orderId, e);
+ }
+ }
+
@Override
public void clearCurrentOrder(Long deviceId) {
if (deviceId == null) {
@@ -448,44 +469,12 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
redisTemplate.opsForHash().putAll(key, statusMap);
}
- if (areaId != null) {
- addToAreaIndex(deviceId, areaId);
- }
-
log.debug("更新工牌设备区域: deviceId={}, areaId={}, areaName={}", deviceId, areaId, areaName);
} catch (Exception e) {
log.error("更新工牌设备区域失败: deviceId={}", deviceId, e);
}
}
- // ==================== 区域管理 ====================
-
- @Override
- public void initAreaDeviceIndex() {
- log.info("[BadgeDeviceStatusService] 开始初始化区域设备索引...");
- // 委托给 AreaDeviceService 处理
- areaDeviceService.initAreaDeviceIndex();
- log.info("[BadgeDeviceStatusService] 区域设备索引初始化完成");
- }
-
- @Override
- public void refreshAreaDeviceIndex() {
- log.info("[BadgeDeviceStatusService] 开始刷新区域设备索引...");
- // 委托给 AreaDeviceService 处理
- areaDeviceService.refreshAreaDeviceIndex();
- log.info("[BadgeDeviceStatusService] 区域设备索引刷新完成");
- }
-
- @Override
- public void addToAreaIndex(Long deviceId, Long areaId) {
- areaDeviceService.addToAreaIndex(deviceId, areaId);
- }
-
- @Override
- public void removeFromAreaIndex(Long deviceId, Long areaId) {
- areaDeviceService.removeFromAreaIndex(deviceId, areaId);
- }
-
// ==================== 设备管理 ====================
@Override
diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceService.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceService.java
index 3328580..04f646d 100644
--- a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceService.java
+++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceService.java
@@ -8,7 +8,6 @@ import java.util.List;
* 区域设备关联服务
*
* 职责:管理运营区域与 IoT 设备的关联关系
- * 提供缓存能力,支持快速查询
*
* @author lzh
*/
@@ -24,9 +23,17 @@ public interface AreaDeviceService {
List listByAreaIdAndType(Long areaId, String relationType);
/**
- * 根据区域ID和关联类型查询单个启用的关联关系
+ * 根据区域ID查询所有关联关系
+ *
+ * @param areaId 区域ID
+ * @return 关联关系列表
+ */
+ List listByAreaId(Long areaId);
+
+ /**
+ * 根据区域ID和关联类型查询单个启用的关联关系(带缓存)
*
- * 带缓存,返回第一个启用的配置
+ * 缓存Key: ops:area:{areaId}:type:{relationType}
*
* @param areaId 区域ID
* @param relationType 关联类型
@@ -34,16 +41,10 @@ public interface AreaDeviceService {
*/
OpsAreaDeviceRelationDO getByAreaIdAndType(Long areaId, String relationType);
- /**
- * 根据区域ID查询所有启用的关联关系
- *
- * @param areaId 区域ID
- * @return 关联关系列表
- */
- List listByAreaId(Long areaId);
-
/**
* 根据设备ID查询关联关系
+ *
+ * 注意:如果设备关联多个区域,只返回第一个
*
* @param deviceId 设备ID
* @return 关联关系,不存在返回 null
@@ -51,54 +52,53 @@ public interface AreaDeviceService {
OpsAreaDeviceRelationDO getByDeviceId(Long deviceId);
/**
- * 初始化区域设备索引
+ * 获取设备关联的所有区域ID
*
- * 从数据库加载所有区域设备关系,建立 Redis 索引
+ * 从数据库直接查询(无缓存)
+ *
+ * @param deviceId 设备ID
+ * @return 区域ID集合
*/
- void initAreaDeviceIndex();
+ List getAreaIdsByDevice(Long deviceId);
/**
- * 刷新区域设备索引
+ * 获取区域关联的所有设备ID
*
- * 重新从数据库加载并建立索引
- */
- void refreshAreaDeviceIndex();
-
- /**
- * 获取区域下的设备ID列表(带读穿透缓存)
+ * 从数据库直接查询(无缓存)
*
* @param areaId 区域ID
- * @return 设备ID集合
+ * @return 设备ID列表
*/
- java.util.Set getDeviceIdsByArea(Long areaId);
+ List getDeviceIdsByArea(Long areaId);
/**
- * 添加设备到区域索引
+ * 获取区域关联的指定类型设备ID
+ *
+ * 从数据库直接查询(无缓存)
*
- * @param deviceId 设备ID
- * @param areaId 区域ID
+ * @param areaId 区域ID
+ * @param relationType 关联类型
+ * @return 设备ID列表
*/
- void addToAreaIndex(Long deviceId, Long areaId);
+ List getDeviceIdsByAreaAndType(Long areaId, String relationType);
/**
- * 从区域索引移除设备
- *
- * @param deviceId 设备ID
- * @param areaId 区域ID
+ * 初始化区域设备配置缓存
+ *
+ * 预热 ops:area:{areaId}:type:{relationType} 缓存
*/
- void removeFromAreaIndex(Long deviceId, Long areaId);
+ void initConfigCache();
/**
- * 清除区域缓存
- *
- * @param areaId 区域ID
+ * 刷新区域设备配置缓存
*/
- void evictAreaCache(Long areaId);
+ void refreshConfigCache();
/**
- * 清除设备缓存
+ * 清除区域配置缓存
*
- * @param deviceId 设备ID
+ * @param areaId 区域ID
+ * @param relationType 关联类型
*/
- void evictDeviceCache(Long deviceId);
+ void evictConfigCache(Long areaId, String relationType);
}
diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceServiceImpl.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceServiceImpl.java
index 1ddbd80..8c3f2e4 100644
--- a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceServiceImpl.java
+++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/service/area/AreaDeviceServiceImpl.java
@@ -7,22 +7,20 @@ import com.viewsh.module.ops.dal.mysql.area.OpsAreaDeviceRelationMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 区域设备关联服务实现
*
- * 职责:
- * 1. 管理运营区域与 IoT 设备的关联关系
- * 2. 维护 Redis 缓存索引
- * 3. 提供快速查询能力
+ * 缓存设计:
+ * - 唯一缓存Key: ops:area:{areaId}:type:{relationType}
+ * - 用途: 存储区域+类型的设备配置(1对1绑定设备)
+ * - N对N设备(如工牌): 先查DB获取关联区域,再用此Key获取配置
*
* @author lzh
*/
@@ -33,30 +31,15 @@ public class AreaDeviceServiceImpl implements AreaDeviceService, InitializingBea
@Resource
private OpsAreaDeviceRelationMapper relationMapper;
- @Resource
- private RedisTemplate redisTemplate;
-
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
- * Redis Key 前缀
- */
- private static final String AREA_BADGES_KEY_PREFIX = "ops:area:badges:";
-
- /**
- * 设备配置缓存 Key 前缀(JSON 格式,IoT 可读)
+ * 缓存Key前缀: ops:area:{areaId}:type:{relationType}
*
- * 格式: ops:area:device:{deviceId}
+ * 唯一的区域设备配置缓存,适用于所有设备类型
*/
- private static final String DEVICE_CACHE_KEY_PREFIX = "ops:area:device:";
-
- /**
- * 区域+类型配置缓存 Key 前缀(JSON 格式,IoT 可读)
- *
- * 格式: ops:area:{areaId}:type:{relationType}
- */
- private static final String AREA_TYPE_CACHE_KEY_PREFIX = "ops:area:%s:type:%s";
+ private static final String CONFIG_CACHE_KEY_PREFIX = "ops:area:%s:type:%s";
/**
* 空值缓存标记
@@ -64,26 +47,22 @@ public class AreaDeviceServiceImpl implements AreaDeviceService, InitializingBea
private static final String NULL_CACHE = "NULL";
/**
- * 缓存 TTL(24 小时)
- * 区域设备关系相对静态,可以设置较长过期时间
+ * 缓存TTL(24小时)
*/
private static final int CACHE_TTL_HOURS = 24;
@Override
public void afterPropertiesSet() {
- // 启动时初始化区域设备索引
- initAreaDeviceIndex();
+ initConfigCache();
}
@Override
public List listByAreaIdAndType(Long areaId, String relationType) {
- log.debug("[AreaDevice] 查询区域设备关联:areaId={}, relationType={}", areaId, relationType);
return relationMapper.selectListByAreaIdAndRelationType(areaId, relationType);
}
@Override
public List listByAreaId(Long areaId) {
- log.debug("[AreaDevice] 查询区域所有设备:areaId={}", areaId);
return relationMapper.selectListByAreaId(areaId);
}
@@ -93,33 +72,31 @@ public class AreaDeviceServiceImpl implements AreaDeviceService, InitializingBea
return null;
}
- // 先从缓存读取(缓存存储的是 AreaDeviceDTO JSON)
- String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType);
+ String cacheKey = String.format(CONFIG_CACHE_KEY_PREFIX, areaId, relationType);
+
+ // 1. 先读缓存
try {
String cached = stringRedisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
if (NULL_CACHE.equals(cached)) {
return null;
}
- log.debug("[AreaDevice] 命中区域类型配置缓存:areaId={}, type={}", areaId, relationType);
+ log.debug("[AreaDevice] 命中配置缓存:areaId={}, type={}", areaId, relationType);
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return toAreaDeviceRelationDO(dto);
}
} catch (Exception e) {
- log.warn("[AreaDevice] 读取区域类型配置缓存失败:areaId={}, type={}", areaId, relationType, e);
+ log.warn("[AreaDevice] 读取配置缓存失败:areaId={}, type={}", areaId, relationType, e);
}
- // 从数据库查询
- List relations = relationMapper.selectListByAreaIdAndRelationType(areaId,
- relationType);
-
- // 返回第一个启用的
+ // 2. 缓存未命中,查数据库
+ List relations = relationMapper.selectListByAreaIdAndRelationType(areaId, relationType);
OpsAreaDeviceRelationDO relation = relations.stream()
.filter(r -> r.getEnabled())
.findFirst()
.orElse(null);
- // 写入 JSON 缓存(IoT 可读,存储 AreaDeviceDTO)
+ // 3. 写入缓存
try {
if (relation != null) {
AreaDeviceDTO dto = toAreaDeviceDTO(relation);
@@ -129,11 +106,10 @@ public class AreaDeviceServiceImpl implements AreaDeviceService, InitializingBea
CACHE_TTL_HOURS,
TimeUnit.HOURS);
} else {
- // 空值缓存,防止穿透
stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE, 1, TimeUnit.MINUTES);
}
} catch (Exception e) {
- log.warn("[AreaDevice] 写入区域类型配置缓存失败:areaId={}, type={}", areaId, relationType, e);
+ log.warn("[AreaDevice] 写入配置缓存失败:areaId={}, type={}", areaId, relationType, e);
}
return relation;
@@ -144,219 +120,108 @@ public class AreaDeviceServiceImpl implements AreaDeviceService, InitializingBea
if (deviceId == null) {
return null;
}
-
- // 先从缓存读取(缓存存储的是 AreaDeviceDTO JSON)
- String cacheKey = DEVICE_CACHE_KEY_PREFIX + deviceId;
- try {
- String cached = stringRedisTemplate.opsForValue().get(cacheKey);
- if (cached != null) {
- if (NULL_CACHE.equals(cached)) {
- return null;
- }
- log.debug("[AreaDevice] 命中设备配置缓存:deviceId={}", deviceId);
- AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
- return toAreaDeviceRelationDO(dto);
- }
- } catch (Exception e) {
- log.warn("[AreaDevice] 读取设备配置缓存失败:deviceId={}", deviceId, e);
- }
-
- // 从数据库查询
- OpsAreaDeviceRelationDO relation = relationMapper.selectByDeviceId(deviceId);
-
- // 写入 JSON 缓存(IoT 可读,存储 AreaDeviceDTO)
- try {
- if (relation != null) {
- AreaDeviceDTO dto = toAreaDeviceDTO(relation);
- stringRedisTemplate.opsForValue().set(
- cacheKey,
- JsonUtils.toJsonString(dto),
- CACHE_TTL_HOURS,
- TimeUnit.HOURS);
- } else {
- // 空值缓存,防止穿透
- stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE, 1, TimeUnit.MINUTES);
- }
- } catch (Exception e) {
- log.warn("[AreaDevice] 写入设备配置缓存失败:deviceId={}", deviceId, e);
- }
-
- return relation;
+ // 直接查数据库,返回第一个关联
+ return relationMapper.selectByDeviceId(deviceId);
}
@Override
- public void initAreaDeviceIndex() {
- log.info("[AreaDevice] 开始初始化区域设备索引...");
-
- try {
- // 1. 查询所有工牌设备关联,建立区域索引
- List badgeRelations = relationMapper
- .selectListByAreaIdAndRelationType(null, "BADGE");
-
- int badgeCount = 0;
- for (OpsAreaDeviceRelationDO relation : badgeRelations) {
- if (relation.getAreaId() != null && relation.getDeviceId() != null) {
- addToAreaIndex(relation.getDeviceId(), relation.getAreaId());
- badgeCount++;
- }
- }
-
- log.info("[AreaDevice] 工牌设备索引初始化完成:共加载 {} 个工牌设备关联", badgeCount);
-
- // 2. 查询所有信标设备关联,预热区域+类型配置缓存
- List beaconRelations = relationMapper
- .selectListByAreaIdAndRelationType(null, "BEACON");
-
- int beaconCount = 0;
- for (OpsAreaDeviceRelationDO relation : beaconRelations) {
- if (relation.getAreaId() != null && relation.getEnabled()) {
- // 写入区域+类型配置缓存
- String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, relation.getAreaId(), "BEACON");
- try {
- AreaDeviceDTO dto = toAreaDeviceDTO(relation);
- stringRedisTemplate.opsForValue().set(
- cacheKey,
- JsonUtils.toJsonString(dto),
- CACHE_TTL_HOURS,
- TimeUnit.HOURS);
- beaconCount++;
- } catch (Exception e) {
- log.warn("[AreaDevice] 写入信标配置缓存失败:areaId={}", relation.getAreaId(), e);
- }
- }
- }
-
- log.info("[AreaDevice] 信标配置缓存初始化完成:共加载 {} 个信标配置", beaconCount);
-
- } catch (Exception e) {
- log.error("[AreaDevice] 区域设备索引初始化失败", e);
- }
- }
-
- @Override
- public void refreshAreaDeviceIndex() {
- log.info("[AreaDevice] 开始刷新区域设备索引...");
- initAreaDeviceIndex();
- }
-
- @Override
- public Set getDeviceIdsByArea(Long areaId) {
- if (areaId == null) {
- return Set.of();
- }
-
- String areaKey = AREA_BADGES_KEY_PREFIX + areaId;
- try {
- Set