feat(iot): 优化保洁工单配置缓存服务

- 新增空值缓存机制,防止缓存穿透
- 新增区域+类型配置查询缓存
- 统一缓存 TTL 为 5 分钟,空值缓存 60 秒
- 新增缓存失效方法 evictAreaCache

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-01-25 09:38:36 +08:00
parent 0a5fe13e68
commit 0ee7611a7d

View File

@@ -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<OpsAreaDeviceRelationDO> 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);
}
}