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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user