diff --git a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java index 4227203..4ce922e 100644 --- a/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java +++ b/viewsh-module-iot/viewsh-module-iot-server/src/main/java/com/viewsh/module/iot/service/integration/clean/CleanOrderIntegrationConfigServiceImpl.java @@ -27,6 +27,8 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra */ private static final String CONFIG_DEVICE_KEY_PATTERN = "iot:clean:config:device:%s"; private static final String CONFIG_AREA_KEY_PATTERN = "iot:clean:config:area:%s"; + private static final String CONFIG_WRAPPER_KEY_PATTERN = "iot:clean:config:wrapper:%s"; + private static final String CONFIG_AREA_TYPE_KEY_PATTERN = "iot:clean:config:area:%s:type:%s"; /** * 配置缓存 TTL(秒) @@ -34,6 +36,11 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra * 5 分钟自动过期 */ private static final int CONFIG_CACHE_TTL_SECONDS = 300; + + /** + * 空值缓存标记(防止缓存穿透) + */ + private static final String NULL_CACHE_VALUE = "NULL"; @Resource private OpsAreaDeviceRelationMapper relationMapper; @@ -111,44 +118,106 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) { log.debug("[CleanOrderConfig] 查询单个区域配置:areaId={}, relationType={}", areaId, relationType); + // 1. 尝试从缓存读取 + String cacheKey = formatAreaTypeKey(areaId, relationType); + String cachedValue = stringRedisTemplate.opsForValue().get(cacheKey); + + if (cachedValue != null) { + if (NULL_CACHE_VALUE.equals(cachedValue)) { + log.debug("[CleanOrderConfig] 命中空值缓存:areaId={}, relationType={}", areaId, relationType); + return null; + } + log.debug("[CleanOrderConfig] 命中区域类型缓存:areaId={}, relationType={}", areaId, relationType); + return JsonUtils.parseObject(cachedValue, AreaDeviceConfigWrapper.class); + } + + // 2. 从数据库查询 List relations = relationMapper.selectListByAreaIdAndRelationType(areaId, relationType); if (relations.isEmpty()) { + // 缓存空值,防止缓存穿透 + stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE_VALUE, 60, TimeUnit.SECONDS); return null; } // 返回第一个启用的配置 - return relations.stream() + AreaDeviceConfigWrapper wrapper = relations.stream() .filter(r -> r.getEnabled()) .findFirst() .map(this::wrapConfig) .orElse(null); + + // 3. 写入缓存 + if (wrapper != null) { + stringRedisTemplate.opsForValue().set( + cacheKey, + JsonUtils.toJsonString(wrapper), + CONFIG_CACHE_TTL_SECONDS, + TimeUnit.SECONDS + ); + } else { + // 缓存空值 + stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE_VALUE, 60, TimeUnit.SECONDS); + } + + return wrapper; } @Override public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) { log.debug("[CleanOrderConfig] 查询设备完整配置:deviceId={}", deviceId); + // 1. 尝试从缓存读取 + String cacheKey = formatWrapperKey(deviceId); + String cachedValue = stringRedisTemplate.opsForValue().get(cacheKey); + + if (cachedValue != null) { + if (NULL_CACHE_VALUE.equals(cachedValue)) { + log.debug("[CleanOrderConfig] 命中空值缓存:deviceId={}", deviceId); + return null; + } + log.debug("[CleanOrderConfig] 命中 Wrapper 缓存:deviceId={}", deviceId); + return JsonUtils.parseObject(cachedValue, AreaDeviceConfigWrapper.class); + } + + // 2. 从数据库查询 OpsAreaDeviceRelationDO relation = relationMapper.selectByDeviceId(deviceId); if (relation == null || !relation.getEnabled()) { + // 缓存空值,防止缓存穿透(TTL 较短:60秒) + stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE_VALUE, 60, TimeUnit.SECONDS); return null; } - return wrapConfig(relation); + AreaDeviceConfigWrapper wrapper = wrapConfig(relation); + + // 3. 写入缓存 + stringRedisTemplate.opsForValue().set( + cacheKey, + JsonUtils.toJsonString(wrapper), + CONFIG_CACHE_TTL_SECONDS, + TimeUnit.SECONDS + ); + + return wrapper; } @Override public void evictCache(Long deviceId) { - String cacheKey = formatDeviceKey(deviceId); - stringRedisTemplate.delete(cacheKey); + // 清除设备相关的所有缓存 + stringRedisTemplate.delete(formatDeviceKey(deviceId)); + stringRedisTemplate.delete(formatWrapperKey(deviceId)); log.info("[CleanOrderConfig] 清除设备配置缓存:deviceId={}", deviceId); } @Override public void evictAreaCache(Long areaId) { - String cacheKey = formatAreaKey(areaId); - stringRedisTemplate.delete(cacheKey); + // 清除区域相关的所有缓存(包括各类型) + stringRedisTemplate.delete(formatAreaKey(areaId)); + // 清除常用类型缓存 + stringRedisTemplate.delete(formatAreaTypeKey(areaId, "TRAFFIC_COUNTER")); + stringRedisTemplate.delete(formatAreaTypeKey(areaId, "BEACON")); + stringRedisTemplate.delete(formatAreaTypeKey(areaId, "BADGE")); log.info("[CleanOrderConfig] 清除区域配置缓存:areaId={}", areaId); } @@ -174,10 +243,24 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra return String.format(CONFIG_DEVICE_KEY_PATTERN, deviceId); } + /** + * 格式化设备配置包装器缓存 Key + */ + private static String formatWrapperKey(Long deviceId) { + return String.format(CONFIG_WRAPPER_KEY_PATTERN, deviceId); + } + /** * 格式化区域配置缓存 Key */ private static String formatAreaKey(Long areaId) { return String.format(CONFIG_AREA_KEY_PATTERN, areaId); } + + /** + * 格式化区域+类型配置缓存 Key + */ + private static String formatAreaTypeKey(Long areaId, String relationType) { + return String.format(CONFIG_AREA_TYPE_KEY_PATTERN, areaId, relationType); + } }