refactor(iot): 重构配置模型与实现双层缓存优化

- 将 buttonEvent 配置从区域级移至设备级(iot_device.config)
- 添加 getConfigWrapperByDeviceId 方法支持一对一设备查询
- 实现设备索引缓存 + 区域配置缓存的双层缓存设计
- 添加设备类型常量 DEVICE_RELATION_TYPES
- 完善 JavaDoc 注释说明配置层级划分

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-02-01 00:57:03 +08:00
parent b73ef4f39f
commit 89e68eceb2
5 changed files with 396 additions and 238 deletions

View File

@@ -7,6 +7,9 @@ import lombok.Data;
* <p>
* 这是 IoT 设备与保洁工单集成的总配置类,包含所有子配置
* 存储在 Ops 模块的 {@code ops_area_device_relation.config_data} 字段中
* <p>
* 注意:此配置仅包含区域级配置(如客流阈值、信标检测),
* 设备级配置(如按键映射)应存储在 {@code iot_device.config} 字段中
*
* @author AI
*/
@@ -26,11 +29,4 @@ public class CleanOrderIntegrationConfig {
* 用于基于工牌蓝牙信标检测自动确认到岗/离岗
*/
private BeaconPresenceConfig beaconPresence;
/**
* 按键事件配置
* <p>
* 用于工牌按键事件的处理
*/
private ButtonEventConfig buttonEvent;
}

View File

@@ -13,15 +13,30 @@ import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrat
public interface CleanOrderIntegrationConfigService {
/**
* 根据设备ID查询配置包装器(包含完整信息)
* 根据设备ID查询配置包装器
* <p>
* 返回包含设备ID、区域ID、关联类型等完整信息的配置包装器
* 适用于一对一关系的设备(如 TRAFFIC_COUNTER设备只关联一个区域
* <p>
* 内部实现:先通过 deviceId 获取 areaId再查询配置
*
* @param deviceId 设备ID
* @return 配置包装器,如果不存在或未启用返回 null
*/
AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId);
/**
* 根据设备ID和区域ID查询配置包装器包含完整信息
* <p>
* 返回包含设备ID、区域ID、关联类型等完整信息的配置包装器
* <p>
* 缓存Key: ops:area:{areaId}:type:{relationType}
*
* @param deviceId 设备ID
* @param areaId 区域ID
* @return 配置包装器,如果不存在或未启用返回 null
*/
AreaDeviceConfigWrapper getConfigWrapperByDeviceIdAndAreaId(Long deviceId, Long areaId);
/**
* 根据区域ID查询所有启用的配置带缓存
* <p>
@@ -72,6 +87,16 @@ public interface CleanOrderIntegrationConfigService {
*/
void evictAreaCache(Long areaId);
/**
* 转换配置数据 Map -> CleanOrderIntegrationConfig
* <p>
* 公开方法,供其他模块(如 TrafficThresholdRuleProcessor调用
*
* @param configData 配置数据 Map
* @return 集成配置对象,如果输入为空返回 null
*/
CleanOrderIntegrationConfig convertConfig(java.util.Map<String, Object> configData);
/**
* 区域设备配置包装类
* <p>

View File

@@ -1,209 +1,320 @@
package com.viewsh.module.iot.service.integration.clean;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.framework.common.util.json.JsonUtils;
import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrationConfig;
import com.viewsh.module.ops.api.area.AreaDeviceApi;
import com.viewsh.module.ops.api.area.AreaDeviceDTO;
import com.viewsh.module.ops.api.area.DeviceRelationDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* 保洁工单集成配置 Service 实现类
*
* @author AI
*/
@Service
@Slf4j
public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService {
/**
* Redis Key 前缀(与 Ops 模块保持一致)
*/
private static final String DEVICE_CACHE_KEY_PREFIX = "ops:area:device:";
private static final String AREA_TYPE_CACHE_KEY_PREFIX = "ops:area:%s:type:%s";
private static final String NULL_CACHE = "NULL";
/**
* Ops 模块区域设备 API Client
*/
@Resource
private AreaDeviceApi areaDeviceApi;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaId(Long areaId) {
log.debug("[CleanOrderConfig] 查询区域配置areaId={}", areaId);
// 通过 Feign 调用 Ops 模块获取所有关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}", areaId);
return List.of();
}
return result.getData().stream()
.map(this::wrapDto)
.collect(Collectors.toList());
}
@Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaIdAndRelationType(Long areaId, String relationType) {
log.debug("[CleanOrderConfig] 查询区域配置areaId={}, relationType={}", areaId, relationType);
// 通过 Feign 调用 Ops 模块获取关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType);
return List.of();
}
return result.getData().stream()
.map(this::wrapDto)
.collect(Collectors.toList());
}
@Override
public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) {
log.debug("[CleanOrderConfig] 查询单个区域配置areaId={}, relationType={}", areaId, relationType);
// 1. 先读 Redis 缓存
String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType);
try {
String cached = stringRedisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
if (NULL_CACHE.equals(cached)) {
log.debug("[CleanOrderConfig] 命中空值缓存areaId={}, relationType={}", areaId, relationType);
return null;
}
log.debug("[CleanOrderConfig] 命中 Redis 缓存areaId={}, relationType={}", areaId, relationType);
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return wrapDto(dto);
}
} catch (Exception e) {
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败areaId={}, relationType={}", areaId, relationType, e);
}
// 2. 缓存未命中,调用 Ops 模块
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType);
return null;
}
// 返回第一个启用的配置
return result.getData().stream()
.filter(dto -> dto.getEnabled() != null && dto.getEnabled())
.findFirst()
.map(this::wrapDto)
.orElse(null);
}
@Override
public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) {
log.debug("[CleanOrderConfig] 查询设备完整配置deviceId={}", deviceId);
// 1. 先读 Redis 缓存
String cacheKey = DEVICE_CACHE_KEY_PREFIX + deviceId;
try {
String cached = stringRedisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
if (NULL_CACHE.equals(cached)) {
log.debug("[CleanOrderConfig] 命中空值缓存deviceId={}", deviceId);
return null;
}
log.debug("[CleanOrderConfig] 命中 Redis 缓存deviceId={}", deviceId);
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return wrapDto(dto);
}
} catch (Exception e) {
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败deviceId={}", deviceId, e);
}
// 2. 缓存未命中,调用 Ops 模块
CommonResult<DeviceRelationDTO> result = areaDeviceApi.getDeviceRelationWithConfig(deviceId);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取设备配置失败deviceId={}", deviceId);
return null;
}
DeviceRelationDTO dto = result.getData();
// 检查是否启用
if (dto.getEnabled() == null || !dto.getEnabled()) {
return null;
}
return wrapDto(dto);
}
@Override
public void evictCache(Long deviceId) {
// 缓存已由 Ops 模块管理,无需操作
log.debug("[CleanOrderConfig] evictCache 由 Ops 模块管理deviceId={}", deviceId);
}
@Override
public void evictAreaCache(Long areaId) {
// 缓存已由 Ops 模块管理,无需操作
log.debug("[CleanOrderConfig] evictAreaCache 由 Ops 模块管理areaId={}", areaId);
}
/**
* 包装配置数据
*/
private AreaDeviceConfigWrapper wrapDto(AreaDeviceDTO dto) {
if (dto == null) {
return null;
}
return new AreaDeviceConfigWrapper(
dto.getDeviceId(),
dto.getDeviceKey(),
dto.getProductId(),
dto.getProductKey(),
dto.getAreaId(),
dto.getRelationType(),
convertConfig(dto.getConfigData())
);
}
/**
* 包装配置数据
*/
private AreaDeviceConfigWrapper wrapDto(DeviceRelationDTO dto) {
if (dto == null) {
return null;
}
return new AreaDeviceConfigWrapper(
dto.getDeviceId(),
dto.getDeviceKey(),
dto.getProductId(),
dto.getProductKey(),
dto.getAreaId(),
dto.getRelationType(),
convertConfig(dto.getConfigData())
);
}
/**
* 转换配置数据 Map -> CleanOrderIntegrationConfig
*/
private CleanOrderIntegrationConfig convertConfig(java.util.Map<String, Object> configData) {
if (configData == null || configData.isEmpty()) {
return null;
}
return JsonUtils.parseObject(JsonUtils.toJsonString(configData), CleanOrderIntegrationConfig.class);
}
}
package com.viewsh.module.iot.service.integration.clean;
import com.viewsh.framework.common.pojo.CommonResult;
import com.viewsh.framework.common.util.json.JsonUtils;
import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrationConfig;
import com.viewsh.module.ops.api.area.AreaDeviceApi;
import com.viewsh.module.ops.api.area.AreaDeviceDTO;
import com.viewsh.module.ops.api.area.DeviceRelationDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 保洁工单集成配置 Service 实现类
*
* @author AI
*/
@Service
@Slf4j
public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService {
/**
* Redis Key 前缀
*/
private static final String AREA_TYPE_CACHE_KEY_PREFIX = "ops:area:%s:type:%s";
private static final String DEVICE_INDEX_KEY_PREFIX = "ops:device:index:%s";
private static final String NULL_CACHE = "NULL";
/**
* 设备关联类型枚举
* <p>
* 用于遍历查找设备配置
*/
private static final String[] DEVICE_RELATION_TYPES = {"BADGE", "BEACON", "TRAFFIC_COUNTER"};
/**
* 缓存过期时间
*/
private static final long DEVICE_INDEX_CACHE_TTL_SECONDS = 3600L; // 1小时
/**
* Ops 模块区域设备 API Client
*/
@Resource
private AreaDeviceApi areaDeviceApi;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaId(Long areaId) {
log.debug("[CleanOrderConfig] 查询区域配置areaId={}", areaId);
// 通过 Feign 调用 Ops 模块获取所有关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}", areaId);
return List.of();
}
return result.getData().stream()
.map(this::wrapDto)
.collect(Collectors.toList());
}
@Override
public List<AreaDeviceConfigWrapper> getConfigsByAreaIdAndRelationType(Long areaId, String relationType) {
log.debug("[CleanOrderConfig] 查询区域配置areaId={}, relationType={}", areaId, relationType);
// 通过 Feign 调用 Ops 模块获取关联设备
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType);
return List.of();
}
return result.getData().stream()
.map(this::wrapDto)
.collect(Collectors.toList());
}
@Override
public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) {
log.debug("[CleanOrderConfig] 查询单个区域配置areaId={}, relationType={}", areaId, relationType);
// 1. 先读 Redis 缓存
String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType);
try {
String cached = stringRedisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
if (NULL_CACHE.equals(cached)) {
log.debug("[CleanOrderConfig] 命中空值缓存areaId={}, relationType={}", areaId, relationType);
return null;
}
log.debug("[CleanOrderConfig] 命中 Redis 缓存areaId={}, relationType={}", areaId, relationType);
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
return wrapDto(dto);
}
} catch (Exception e) {
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败areaId={}, relationType={}", areaId, relationType, e);
}
// 2. 缓存未命中,调用 Ops 模块
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败areaId={}, relationType={}", areaId, relationType);
return null;
}
// 返回第一个启用的配置
return result.getData().stream()
.filter(dto -> dto.getEnabled() != null && dto.getEnabled())
.findFirst()
.map(this::wrapDto)
.orElse(null);
}
@Override
public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) {
if (deviceId == null) {
return null;
}
log.debug("[CleanOrderConfig] 查询设备配置deviceId={}", deviceId);
// 1. 查询设备反向索引缓存
String indexKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId);
try {
Map<Object, Object> indexMap = stringRedisTemplate.opsForHash().entries(indexKey);
Long areaId = null;
String relationType = null;
if (indexMap != null && !indexMap.isEmpty()) {
// 命中缓存
Object areaIdObj = indexMap.get("areaId");
Object relationTypeObj = indexMap.get("relationType");
Object enabledObj = indexMap.get("enabled");
if (enabledObj != null && Boolean.FALSE.equals(Boolean.parseBoolean(enabledObj.toString()))) {
log.debug("[CleanOrderConfig] 设备未启用缓存deviceId={}", deviceId);
return null;
}
if (areaIdObj != null) {
areaId = Long.parseLong(areaIdObj.toString());
}
if (relationTypeObj != null) {
relationType = relationTypeObj.toString();
}
log.debug("[CleanOrderConfig] 命中设备索引缓存deviceId={}, areaId={}, relationType={}",
deviceId, areaId, relationType);
}
// 2. 缓存未命中,调用 Ops API 获取设备关联
if (areaId == null || relationType == null) {
CommonResult<DeviceRelationDTO> result = areaDeviceApi.getDeviceRelation(deviceId);
if (result == null || !result.isSuccess() || result.getData() == null) {
log.debug("[CleanOrderConfig] 未找到设备关联deviceId={}", deviceId);
return null;
}
DeviceRelationDTO dto = result.getData();
if (dto.getEnabled() == null || !dto.getEnabled()) {
log.debug("[CleanOrderConfig] 设备未启用deviceId={}", deviceId);
return null;
}
areaId = dto.getAreaId();
relationType = dto.getRelationType();
if (areaId == null || relationType == null) {
log.debug("[CleanOrderConfig] 设备关联信息不完整deviceId={}, areaId={}, relationType={}",
deviceId, areaId, relationType);
return null;
}
// 3. 回写设备索引缓存
writeDeviceIndexCache(deviceId, areaId, relationType, dto.getEnabled());
log.debug("[CleanOrderConfig] 从Ops获取设备关联并写入缓存deviceId={}, areaId={}, relationType={}",
deviceId, areaId, relationType);
}
// 4. 使用 areaId + relationType 查询配置(带缓存)
return getConfigByAreaIdAndRelationType(areaId, relationType);
} catch (Exception e) {
log.error("[CleanOrderConfig] 查询设备配置失败deviceId={}", deviceId, e);
return null;
}
}
/**
* 写入设备索引缓存
*/
private void writeDeviceIndexCache(Long deviceId, Long areaId, String relationType, Boolean enabled) {
try {
String cacheKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId);
Map<String, String> indexData = new HashMap<>();
indexData.put("areaId", String.valueOf(areaId));
indexData.put("relationType", relationType);
indexData.put("enabled", String.valueOf(enabled));
stringRedisTemplate.opsForHash().putAll(cacheKey, indexData);
stringRedisTemplate.expire(cacheKey, DEVICE_INDEX_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
log.debug("[CleanOrderConfig] 写入设备索引缓存deviceId={}, areaId={}, relationType={}",
deviceId, areaId, relationType);
} catch (Exception e) {
log.warn("[CleanOrderConfig] 写入设备索引缓存失败deviceId={}", deviceId, e);
}
}
/**
* 清除设备索引缓存
*/
private void evictDeviceIndexCache(Long deviceId) {
try {
String cacheKey = String.format(DEVICE_INDEX_KEY_PREFIX, deviceId);
stringRedisTemplate.delete(cacheKey);
log.debug("[CleanOrderConfig] 清除设备索引缓存deviceId={}", deviceId);
} catch (Exception e) {
log.warn("[CleanOrderConfig] 清除设备索引缓存失败deviceId={}", deviceId, e);
}
}
@Override
public AreaDeviceConfigWrapper getConfigWrapperByDeviceIdAndAreaId(Long deviceId, Long areaId) {
if (deviceId == null || areaId == null) {
return null;
}
log.debug("[CleanOrderConfig] 查询设备配置deviceId={}, areaId={}", deviceId, areaId);
// 由于不知道 relationType<70><65><EFBFBD>需要遍历所有类型查找匹配的设备
String[] relationTypes = DEVICE_RELATION_TYPES;
for (String relationType : relationTypes) {
AreaDeviceConfigWrapper wrapper = getConfigByAreaIdAndRelationType(areaId, relationType);
if (wrapper != null && wrapper.getDeviceId() != null
&& wrapper.getDeviceId().equals(deviceId)) {
log.debug("[CleanOrderConfig] 找到设备配置deviceId={}, areaId={}, type={}",
deviceId, areaId, relationType);
return wrapper;
}
}
log.debug("[CleanOrderConfig] 未找到设备配置deviceId={}, areaId={}", deviceId, areaId);
return null;
}
@Override
public void evictCache(Long deviceId) {
evictDeviceIndexCache(deviceId);
log.debug("[CleanOrderConfig] 已清除设备缓存deviceId={}", deviceId);
}
@Override
public void evictAreaCache(Long areaId) {
// 区域配置缓存由 Ops 模块管理
log.debug("[CleanOrderConfig] evictAreaCache 由 Ops 模块管理areaId={}", areaId);
}
@Override
public CleanOrderIntegrationConfig convertConfig(java.util.Map<String, Object> configData) {
if (configData == null || configData.isEmpty()) {
return null;
}
return JsonUtils.parseObject(JsonUtils.toJsonString(configData), CleanOrderIntegrationConfig.class);
}
/**
* 包装配置数据
*/
private AreaDeviceConfigWrapper wrapDto(AreaDeviceDTO dto) {
if (dto == null) {
return null;
}
return new AreaDeviceConfigWrapper(
dto.getDeviceId(),
dto.getDeviceKey(),
dto.getProductId(),
dto.getProductKey(),
dto.getAreaId(),
dto.getRelationType(),
convertConfig(dto.getConfigData())
);
}
/**
* 包装配置数据
*/
private AreaDeviceConfigWrapper wrapDto(DeviceRelationDTO dto) {
if (dto == null) {
return null;
}
return new AreaDeviceConfigWrapper(
dto.getDeviceId(),
dto.getDeviceKey(),
dto.getProductId(),
dto.getProductKey(),
dto.getAreaId(),
dto.getRelationType(),
convertConfig(dto.getConfigData())
);
}
}

View File

@@ -132,17 +132,44 @@ public class BadgeDeviceStatusEventListener {
var waitingTasks = orderQueueService.getWaitingTasksByUserIdFromDb(deviceId);
switch (newStatus) {
case DISPATCHED:
// 工单已推送到工牌,设置工单关联,设备状态转为 BUSY
badgeDeviceStatusService.setCurrentOrder(deviceId, orderId);
// 工单已推送到工牌,设置工单关联和区域信息
// currentAreaId 应该设置为工单所属区域,而非设备物理位置
badgeDeviceStatusService.updateBadgeStatus(deviceId, BadgeDeviceStatusEnum.BUSY, null, "新工单已推送");
log.info("[BadgeDeviceStatusEventListener] 工单已推送,设备状态转为 BUSY: deviceId={}, orderId={}", deviceId,
orderId);
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);
}
break;
case CONFIRMED:
// 设备按键确认,设备保持 BUSY
badgeDeviceStatusService.setCurrentOrder(deviceId, orderId);
log.debug("[BadgeDeviceStatusEventListener] 工单已确认: deviceId={}, orderId={}", deviceId, orderId);
// 设备按键确认,更新工单关联和区域信息
// 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);
}
break;
case ARRIVED:

View File

@@ -279,20 +279,19 @@ public class BadgeDeviceStatusServiceImpl implements BadgeDeviceStatusService, I
statusMap.put("lastHeartbeatTime", now);
}
// 更新实时物理区域信息 (Key2)
// 更新区域信息
// 注意currentAreaId 应该表示当前工单所属区域,而非设备物理位置
// 只有在没有工单时,才同步 IoT 上报的物理位置
if (areaId != null) {
statusMap.put("currentAreaId", areaId);
// 同时更新区域设备索引缓存 (这里注释掉了,保持原样)
// addToAreaIndex(deviceId, areaId);
} else {
// 保持现有实时物理区域信息
Object existingAreaId = currentMap.get("currentAreaId");
if (existingAreaId != null) {
statusMap.put("currentAreaId", existingAreaId);
}
Object existingAreaName = currentMap.get("currentAreaName");
if (existingAreaName != null) {
statusMap.put("currentAreaName", existingAreaName);
// 检查是否有正在执行的工单
Object currentOrderId = currentMap.get("currentOpsOrderId");
if (currentOrderId != null) {
// 有工单:保持 currentAreaId 不变(用工单区域),不覆盖
log.debug("设备 {} 有执行中工单 {},保持 currentAreaId 不变,忽略 IoT 上报的物理位置 {}",
deviceId, currentOrderId, areaId);
} else {
// 无工单:用 IoT 上报的物理位置
statusMap.put("currentAreaId", areaId);
}
}