From d6276dc21f4a84670fcbcda1d1a8ed49c54a5b19 Mon Sep 17 00:00:00 2001 From: mashili Date: Sun, 24 Aug 2025 17:12:42 +0800 Subject: [PATCH] =?UTF-8?q?20250808=E4=BC=98=E5=8C=96-=E5=8D=95=E4=BD=93?= =?UTF-8?q?=E7=94=B5=E6=B1=A0=E8=A1=A8=E6=8B=86=E5=88=86=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xzzn/common/core/redis/RedisCache.java | 69 ++++++++++++++++-- .../utils/AbstractBatteryDataProcessor.java | 71 +++++++++---------- .../mapper/ems/EmsBatteryDataDayMapper.xml | 2 +- .../mapper/ems/EmsBatteryDataHourMapper.xml | 2 +- .../mapper/ems/EmsBatteryDataMonthMapper.xml | 2 +- 5 files changed, 102 insertions(+), 44 deletions(-) diff --git a/ems-common/src/main/java/com/xzzn/common/core/redis/RedisCache.java b/ems-common/src/main/java/com/xzzn/common/core/redis/RedisCache.java index a8af534..99829d4 100644 --- a/ems-common/src/main/java/com/xzzn/common/core/redis/RedisCache.java +++ b/ems-common/src/main/java/com/xzzn/common/core/redis/RedisCache.java @@ -7,10 +7,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.BoundSetOperations; -import org.springframework.data.redis.core.HashOperations; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ValueOperations; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component; /** @@ -287,4 +285,67 @@ public class RedisCache redisTemplate.opsForValue().multiSet(cacheMap); } + /** + * 批量设置键值对,并为每个键设置相同的过期时间 + * @param keyValueMap 键值对集合 + * @param timeout 过期时间 + * @param unit 时间单位 + */ + public void multiSetWithExpire(Map keyValueMap, long timeout, TimeUnit unit) { + if (keyValueMap.isEmpty()) { + return; // 空集合直接返回 + } + + // 使用Redis管道批量执行命令 + redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + // 循环处理每个键值对 + for (Map.Entry entry : keyValueMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + // 1. 设置键值对 + operations.opsForValue().set(key, value); + // 2. 设置过期时间 + operations.expire(key, timeout, unit); + } + return null; // 管道操作无需返回值 + } + }); + } + + /** + * 删除指定站点的所有历史测试数据缓存(最精准) + * @param testSiteId 测试数据所属的站点ID(如"test-site-001") + */ + public void deleteTestCacheBySite(String testSiteId) { + System.out.println("开删除缓存redis:" + testSiteId + ""); + if (testSiteId == null || testSiteId.isEmpty()) { + return; + } + + // 构造键前缀:测试站点ID开头的所有缓存键(无论粒度和时间) + String pattern = testSiteId + "_*"; + deleteCacheByPattern(pattern); + } + + /** + * 根据键前缀批量删除缓存(核心方法) + * @param pattern 键匹配模式(支持Redis的通配符:*表示任意字符,?表示单个字符) + */ + public void deleteCacheByPattern(String pattern) { + try { + // 1. 查找所有匹配的键(注意:keys命令在大数据量时可能阻塞Redis,建议生产环境用scan) + Set keys = redisTemplate.keys(pattern); + if (keys == null || keys.isEmpty()) { + return; + } + + // 2. 批量删除匹配的键 + long deleteCount = redisTemplate.delete(keys); + System.out.println("删除缓存数据:" + deleteCount + ""); + } catch (Exception e) { + } + } } diff --git a/ems-system/src/main/java/com/xzzn/ems/utils/AbstractBatteryDataProcessor.java b/ems-system/src/main/java/com/xzzn/ems/utils/AbstractBatteryDataProcessor.java index 2dcc4a1..ba0ec22 100644 --- a/ems-system/src/main/java/com/xzzn/ems/utils/AbstractBatteryDataProcessor.java +++ b/ems-system/src/main/java/com/xzzn/ems/utils/AbstractBatteryDataProcessor.java @@ -28,6 +28,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -95,7 +96,6 @@ public abstract class AbstractBatteryDataProcessor { // 传递分片索引和是否最后一个分片的标记 ioExecutor.execute(() -> processShard(shard, isLastShard)); } - //shards.forEach(shard -> ioExecutor.execute(() -> processShard(shard))); } /** @@ -245,20 +245,11 @@ public abstract class AbstractBatteryDataProcessor { public void batchUpdateHourCache(List hourDataList) { if (hourDataList.isEmpty()) return; - // 1. 构建缓存键值对(批量操作提升效率) - Map tempCacheMap = new HashMap<>(hourDataList.size()); - for (EmsBatteryDataHour data : hourDataList) { - EmsBatteryData cacheData = new EmsBatteryData(); - BeanUtils.copyProperties(data, cacheData); - // 2. 生成唯一缓存键(与数据库唯一键一致) - String cacheKey = generateCacheKey(cacheData, "hour"); - // 3. 存储最高温度和最后更新时间(两个独立缓存项) - tempCacheMap.put(cacheKey, data.getTemperature().toString()); - } + Map tempCacheMap = buildCacheMap(hourDataList, "hour"); try { - // 4. 批量设置缓存(Redis批量操作减少网络交互) - redisCache.multiSet(tempCacheMap); + // 批量设置缓存 + redisCache.multiSetWithExpire(tempCacheMap, 1, TimeUnit.HOURS); } catch (Exception e) { log.error("批量更新小时级缓存失败", e); // 缓存更新失败不影响主流程,但需记录日志便于排查 @@ -343,20 +334,11 @@ public abstract class AbstractBatteryDataProcessor { public void batchUpdateDayCache(List dayDataList) { if (dayDataList.isEmpty()) return; - // 1. 构建缓存键值对(批量操作提升效率) - Map tempCacheMap = new HashMap<>(dayDataList.size()); - for (EmsBatteryDataDay data : dayDataList) { - EmsBatteryData cacheData = new EmsBatteryData(); - BeanUtils.copyProperties(data, cacheData); - // 2. 生成唯一缓存键(与数据库唯一键一致) - String cacheKey = generateCacheKey(cacheData, "day"); - // 3. 存储最高温度和最后更新时间(两个独立缓存项) - tempCacheMap.put(cacheKey, data.getTemperature().toString()); - } + Map tempCacheMap = buildCacheMap(dayDataList, "hour"); try { - // 4. 批量设置缓存(Redis批量操作减少网络交互) - redisCache.multiSet(tempCacheMap); + // 批量设置缓存 + redisCache.multiSetWithExpire(tempCacheMap, 24, TimeUnit.HOURS); } catch (Exception e) { log.error("批量更新小时级缓存失败", e); // 缓存更新失败不影响主流程,但需记录日志便于排查 @@ -454,20 +436,11 @@ public abstract class AbstractBatteryDataProcessor { public void batchUpdateMonthCache(List monthDataList) { if (monthDataList.isEmpty()) return; - // 1. 构建缓存键值对(批量操作提升效率) - Map tempCacheMap = new HashMap<>(monthDataList.size()); - for (EmsBatteryDataMonth data : monthDataList) { - EmsBatteryData cacheData = new EmsBatteryData(); - BeanUtils.copyProperties(data, cacheData); - // 2. 生成唯一缓存键(与数据库唯一键一致) - String cacheKey = generateCacheKey(cacheData, "month"); - // 3. 存储最高温度和最后更新时间(两个独立缓存项) - tempCacheMap.put(cacheKey, data.getTemperature().toString()); - } + Map tempCacheMap = buildCacheMap(monthDataList, "hour"); try { - // 4. 批量设置缓存(Redis批量操作减少网络交互) - redisCache.multiSet(tempCacheMap); + // 批量设置缓存 + redisCache.multiSetWithExpire(tempCacheMap, 24*30, TimeUnit.HOURS); } catch (Exception e) { log.error("批量更新月级缓存失败", e); // 缓存更新失败不影响主流程,但需记录日志便于排查 @@ -659,6 +632,30 @@ public abstract class AbstractBatteryDataProcessor { } } + // 通用构建缓存键值对的方法(根据不同数据类型适配) + private Map buildCacheMap(List dataList, String granularity) { + Map cacheMap = new HashMap<>(dataList.size()); + for (T data : dataList) { + EmsBatteryData cacheData = new EmsBatteryData(); + BeanUtils.copyProperties(data, cacheData); + String cacheKey = generateCacheKey(cacheData, granularity); + + // 根据数据类型获取温度值(实际项目中可通过接口或反射统一处理) + String temperature; + if (data instanceof EmsBatteryDataHour) { + temperature = ((EmsBatteryDataHour) data).getTemperature().toString(); + } else if (data instanceof EmsBatteryDataDay) { + temperature = ((EmsBatteryDataDay) data).getTemperature().toString(); + } else if (data instanceof EmsBatteryDataMonth) { + temperature = ((EmsBatteryDataMonth) data).getTemperature().toString(); + } else { + throw new IllegalArgumentException("不支持的数据类型"); + } + cacheMap.put(cacheKey, temperature); + } + return cacheMap; + } + // 新增定时任务,每1分钟强制刷新一次队列(防止数据长期积压) @Scheduled(fixedRate = 60000) // 1分钟 = 60000毫秒 protected void scheduledFlushBatch() { diff --git a/ems-system/src/main/resources/mapper/ems/EmsBatteryDataDayMapper.xml b/ems-system/src/main/resources/mapper/ems/EmsBatteryDataDayMapper.xml index d1c9bc5..be2f7b8 100644 --- a/ems-system/src/main/resources/mapper/ems/EmsBatteryDataDayMapper.xml +++ b/ems-system/src/main/resources/mapper/ems/EmsBatteryDataDayMapper.xml @@ -195,7 +195,7 @@ soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc), soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh), inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance), - update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by) + update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by), temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature) diff --git a/ems-system/src/main/resources/mapper/ems/EmsBatteryDataHourMapper.xml b/ems-system/src/main/resources/mapper/ems/EmsBatteryDataHourMapper.xml index aff9a5c..2f2f886 100644 --- a/ems-system/src/main/resources/mapper/ems/EmsBatteryDataHourMapper.xml +++ b/ems-system/src/main/resources/mapper/ems/EmsBatteryDataHourMapper.xml @@ -196,7 +196,7 @@ soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc), soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh), inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance), - update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by) + update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by), temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature) \ No newline at end of file diff --git a/ems-system/src/main/resources/mapper/ems/EmsBatteryDataMonthMapper.xml b/ems-system/src/main/resources/mapper/ems/EmsBatteryDataMonthMapper.xml index 87f6b2b..4d066dd 100644 --- a/ems-system/src/main/resources/mapper/ems/EmsBatteryDataMonthMapper.xml +++ b/ems-system/src/main/resources/mapper/ems/EmsBatteryDataMonthMapper.xml @@ -196,7 +196,7 @@ soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc), soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh), inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance), - update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by) + update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by), temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature) \ No newline at end of file