fix: 修复cache包被.gitignore误删除导致的编译错误

- 修复 .gitignore 中 cache/ 规则导致ljwx-boot中cache包被忽略的问题
- 改为更精确的缓存文件忽略规则:**/cache/*.log, **/cache/*.json, **/cache/*.tmp
- 强制添加必要的缓存服务类:
  * HighPerformanceQueryService.java - 高性能查询服务
  * RedisRelationCacheService.java - Redis关系缓存服务
  * CacheConfiguration.java - 缓存配置类
  * ICacheWarmupService.java - 缓存预热接口
  * RedisCacheWarmupService.java - Redis缓存预热实现
- 解决TUserHealthDataServiceImpl.java编译依赖问题

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
This commit is contained in:
2025-09-02 07:51:16 +08:00
co-authored by Claude
parent 2cb26c18ff
commit f93b9fad43
5 changed files with 1691 additions and 0 deletions
@@ -0,0 +1,33 @@
package com.ljwx.common.cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 缓存配置类
* 启用异步和定时任务支持
*/
@Slf4j
@Configuration
@EnableAsync
@EnableScheduling
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(name = "ljwx.cache.enabled", havingValue = "true", matchIfMissing = true)
public class CacheConfiguration {
public CacheConfiguration() {
log.info("🚀 Redis缓存高性能查询服务已启用");
log.info("📊 支持的功能:");
log.info(" - 租户级别查询优化");
log.info(" - 用户-部门关联查询优化");
log.info(" - 用户-设备关联查询优化");
log.info(" - 部门-设备关联查询优化");
log.info(" - 业务数据缓存优化");
log.info(" - 自动缓存预热和定时刷新");
log.info(" - 批量查询性能优化");
}
}
@@ -0,0 +1,676 @@
package com.ljwx.common.cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 高性能关联查询服务
* 基于Redis缓存和闭包表实现毫秒级多维度关联查询
*/
@Slf4j
@Service
public class HighPerformanceQueryService {
@Autowired
private RedisRelationCacheService cacheService;
@Autowired
private JdbcTemplate jdbcTemplate;
private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// =====================================================
// 租户级别高效查询
// =====================================================
/**
* 获取租户下所有用户(优先从缓存)
* 性能目标: <5ms
*/
public Set<Long> getTenantUsers(Long customerId) {
StopWatch stopWatch = new StopWatch("getTenantUsers");
stopWatch.start("cache-lookup");
// 1. 尝试从缓存获取
Set<Long> cachedUsers = cacheService.getTenantUsers(customerId);
if (!CollectionUtils.isEmpty(cachedUsers)) {
stopWatch.stop();
log.debug("从缓存获取租户用户: customerId={}, users={}, time={}ms",
customerId, cachedUsers.size(), stopWatch.getTotalTimeMillis());
return cachedUsers;
}
stopWatch.start("db-query");
// 2. 缓存未命中,查询数据库
String sql = """
SELECT id FROM sys_user
WHERE customer_id = ? AND is_deleted = 0 AND status = '1'
""";
List<Long> userIds = jdbcTemplate.queryForList(sql, Long.class, customerId);
Set<Long> users = new HashSet<>(userIds);
stopWatch.stop();
// 3. 更新缓存
if (!users.isEmpty()) {
cacheService.cacheTenantUsers(customerId, users);
}
log.debug("从数据库获取租户用户: customerId={}, users={}, time={}ms",
customerId, users.size(), stopWatch.getTotalTimeMillis());
return users;
}
/**
* 获取租户下所有部门(基于闭包表缓存)
* 性能目标: <5ms
*/
public Set<Long> getTenantOrgs(Long customerId) {
// 1. 尝试从缓存获取
Set<Long> cachedOrgs = cacheService.getTenantOrgs(customerId);
if (!CollectionUtils.isEmpty(cachedOrgs)) {
return cachedOrgs;
}
// 2. 查询数据库
String sql = """
SELECT id FROM sys_org_units
WHERE customer_id = ? AND is_deleted = 0 AND status = '1'
""";
List<Long> orgIds = jdbcTemplate.queryForList(sql, Long.class, customerId);
Set<Long> orgs = new HashSet<>(orgIds);
// 3. 更新缓存
if (!orgs.isEmpty()) {
cacheService.cacheTenantOrgs(customerId, orgs);
}
return orgs;
}
// =====================================================
// 用户-部门关联查询(基于闭包表优化)
// =====================================================
/**
* 获取用户所属所有部门(包含层级关系)
* 性能目标: <3ms
*/
public Set<Long> getUserOrgs(Long userId) {
// 1. 尝试从缓存获取
Set<Long> cachedOrgs = cacheService.getUserOrgs(userId);
if (!CollectionUtils.isEmpty(cachedOrgs)) {
return cachedOrgs;
}
// 2. 基于闭包表查询用户所属的所有层级部门
String sql = """
SELECT DISTINCT c.ancestor_id as org_id
FROM sys_user_org uo
INNER JOIN sys_org_closure c ON uo.org_id = c.descendant_id
WHERE uo.user_id = ? AND uo.is_deleted = 0
""";
List<Long> orgIds = jdbcTemplate.queryForList(sql, Long.class, userId);
Set<Long> orgs = new HashSet<>(orgIds);
// 3. 缓存结果
cacheService.cacheUserOrgs(userId, orgs);
return orgs;
}
/**
* 获取部门下所有用户(包含子部门用户)
* 性能目标: <3ms
*/
public Set<Long> getOrgUsers(Long orgId) {
// 1. 尝试从缓存获取
Set<Long> cachedUsers = cacheService.getOrgUsers(orgId);
if (!CollectionUtils.isEmpty(cachedUsers)) {
return cachedUsers;
}
// 2. 基于闭包表查询部门及其子部门的所有用户
String sql = """
SELECT DISTINCT uo.user_id
FROM sys_org_closure c
INNER JOIN sys_user_org uo ON c.descendant_id = uo.org_id
INNER JOIN sys_user u ON uo.user_id = u.id
WHERE c.ancestor_id = ?
AND uo.is_deleted = 0
AND u.is_deleted = 0
AND u.status = '1'
""";
List<Long> userIds = jdbcTemplate.queryForList(sql, Long.class, orgId);
Set<Long> users = new HashSet<>(userIds);
// 3. 缓存结果
cacheService.cacheOrgUsers(orgId, users);
return users;
}
/**
* 获取部门所有后代部门(基于闭包表)
* 性能目标: <2ms
*/
public Set<Long> getOrgDescendants(Long orgId) {
// 1. 尝试从缓存获取
Set<Long> cachedDescendants = cacheService.getOrgDescendants(orgId);
if (!CollectionUtils.isEmpty(cachedDescendants)) {
return cachedDescendants;
}
// 2. 查询闭包表
String sql = """
SELECT descendant_id FROM sys_org_closure
WHERE ancestor_id = ? AND depth > 0
""";
List<Long> descendantIds = jdbcTemplate.queryForList(sql, Long.class, orgId);
Set<Long> descendants = new HashSet<>(descendantIds);
// 3. 缓存结果
cacheService.cacheOrgDescendants(orgId, descendants);
return descendants;
}
// =====================================================
// 用户-设备关联查询
// =====================================================
/**
* 获取用户关联的所有设备
* 性能目标: <3ms
*/
public Set<String> getUserDevices(Long userId) {
// 1. 尝试从缓存获取
Set<String> cachedDevices = cacheService.getUserDevices(userId);
if (!CollectionUtils.isEmpty(cachedDevices)) {
return cachedDevices;
}
// 2. 查询数据库
String sql = """
SELECT du.device_sn
FROM t_device_user du
INNER JOIN t_device_info d ON du.device_sn = d.serial_number
WHERE du.user_id = ?
AND du.status = 'BIND'
AND d.status = 'online'
""";
List<String> deviceSns = jdbcTemplate.queryForList(sql, String.class, userId);
Set<String> devices = new HashSet<>(deviceSns);
// 3. 缓存结果
cacheService.cacheUserDevices(userId, devices);
return devices;
}
/**
* 获取设备关联的用户
* 性能目标: <2ms
*/
public Long getDeviceUser(String deviceSn) {
// 1. 尝试从缓存获取
Long cachedUser = cacheService.getDeviceUser(deviceSn);
if (cachedUser != null) {
return cachedUser;
}
// 2. 查询数据库
String sql = """
SELECT du.user_id
FROM t_device_user du
INNER JOIN sys_user u ON du.user_id = u.id
WHERE du.device_sn = ?
AND du.status = 'BIND'
AND u.is_deleted = 0
""";
List<Long> userIds = jdbcTemplate.queryForList(sql, Long.class, deviceSn);
Long userId = userIds.isEmpty() ? null : userIds.get(0);
// 3. 缓存结果
if (userId != null) {
cacheService.cacheDeviceUser(deviceSn, userId);
}
return userId;
}
// =====================================================
// 部门-设备关联查询
// =====================================================
/**
* 获取部门下所有设备(包含子部门设备)
* 性能目标: <5ms
*/
public Set<String> getOrgDevices(Long orgId) {
// 1. 尝试从缓存获取
Set<String> cachedDevices = cacheService.getOrgDevices(orgId);
if (!CollectionUtils.isEmpty(cachedDevices)) {
return cachedDevices;
}
// 2. 基于闭包表查询部门及其子部门的所有设备
String sql = """
SELECT DISTINCT du.device_sn
FROM sys_org_closure c
INNER JOIN sys_user_org uo ON c.descendant_id = uo.org_id
INNER JOIN sys_user u ON uo.user_id = u.id
INNER JOIN t_device_user du ON u.id = du.user_id
INNER JOIN t_device_info d ON du.device_sn = d.serial_number
WHERE c.ancestor_id = ?
AND uo.is_deleted = 0
AND u.is_deleted = 0
AND du.status = 'BIND'
AND d.status = 'online'
""";
List<String> deviceSns = jdbcTemplate.queryForList(sql, String.class, orgId);
Set<String> devices = new HashSet<>(deviceSns);
// 3. 缓存结果
cacheService.cacheOrgDevices(orgId, devices);
return devices;
}
/**
* 获取设备所属部门
* 性能目标: <3ms
*/
public Long getDeviceOrg(String deviceSn) {
// 1. 尝试从缓存获取
Long cachedOrg = cacheService.getDeviceOrg(deviceSn);
if (cachedOrg != null) {
return cachedOrg;
}
// 2. 查询数据库
String sql = """
SELECT uo.org_id
FROM t_device_user du
INNER JOIN sys_user u ON du.user_id = u.id
INNER JOIN sys_user_org uo ON u.id = uo.user_id
WHERE du.device_sn = ?
AND du.status = 'BIND'
AND u.is_deleted = 0
AND uo.is_deleted = 0
LIMIT 1
""";
List<Long> orgIds = jdbcTemplate.queryForList(sql, Long.class, deviceSn);
Long orgId = orgIds.isEmpty() ? null : orgIds.get(0);
// 3. 缓存结果
if (orgId != null) {
cacheService.cacheDeviceOrg(deviceSn, orgId);
}
return orgId;
}
// =====================================================
// 业务数据关联查询
// =====================================================
/**
* 获取用户最新健康数据
* 性能目标: <3ms
*/
public Map<String, Object> getUserLatestHealth(Long userId) {
// 1. 尝试从缓存获取
Map<String, Object> cachedHealth = cacheService.getUserLatestHealth(userId);
if (!CollectionUtils.isEmpty(cachedHealth)) {
return cachedHealth;
}
// 2. 查询数据库
String sql = """
SELECT heart_rate, pressure_high, pressure_low, blood_oxygen,
temperature, step, calorie, timestamp
FROM t_user_health_data
WHERE user_id = ?
AND timestamp >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND is_deleted = 0
ORDER BY timestamp DESC
LIMIT 1
""";
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql, userId);
Map<String, Object> healthData = results.isEmpty() ? new HashMap<>() : results.get(0);
// 3. 缓存结果
if (!healthData.isEmpty()) {
cacheService.cacheUserLatestHealth(userId, healthData);
}
return healthData;
}
/**
* 获取用户活跃告警
* 性能目标: <3ms
*/
public Set<Long> getUserActiveAlerts(Long userId) {
// 1. 尝试从缓存获取
Set<Long> cachedAlerts = cacheService.getUserActiveAlerts(userId);
if (!CollectionUtils.isEmpty(cachedAlerts)) {
return cachedAlerts;
}
// 2. 查询数据库
String sql = """
SELECT id FROM t_alert_info
WHERE user_id = ?
AND alert_status IN ('PENDING', 'IN_PROGRESS')
AND create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
""";
List<Long> alertIds = jdbcTemplate.queryForList(sql, Long.class, userId);
Set<Long> alerts = new HashSet<>(alertIds);
// 3. 缓存结果
cacheService.cacheUserActiveAlerts(userId, alerts);
return alerts;
}
/**
* 获取部门健康数据汇总
* 性能目标: <5ms
*/
public Map<String, Object> getOrgHealthSummary(Long orgId) {
// 1. 尝试从缓存获取
Map<String, Object> cachedSummary = cacheService.getOrgHealthSummary(orgId);
if (!CollectionUtils.isEmpty(cachedSummary)) {
return cachedSummary;
}
// 2. 基于闭包表查询部门健康数据汇总
String sql = """
SELECT COUNT(h.id) as total_records,
COUNT(DISTINCT h.user_id) as total_users,
AVG(h.heart_rate) as avg_heart_rate,
AVG(h.pressure_high) as avg_pressure_high,
AVG(h.pressure_low) as avg_pressure_low,
AVG(h.blood_oxygen) as avg_blood_oxygen,
SUM(h.step) as total_steps,
SUM(h.calorie) as total_calories
FROM sys_org_closure c
INNER JOIN sys_user_org uo ON c.descendant_id = uo.org_id
INNER JOIN sys_user u ON uo.user_id = u.id
INNER JOIN t_user_health_data h ON u.id = h.user_id
WHERE c.ancestor_id = ?
AND uo.is_deleted = 0
AND u.is_deleted = 0
AND h.timestamp >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND h.is_deleted = 0
""";
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql, orgId);
Map<String, Object> summary = results.isEmpty() ? new HashMap<>() : results.get(0);
// 3. 缓存结果
if (!summary.isEmpty()) {
cacheService.cacheOrgHealthSummary(orgId, summary);
}
return summary;
}
// =====================================================
// 批量查询优化
// =====================================================
/**
* 批量获取用户设备关系
* 性能目标: <10ms for 100 users
*/
public Map<Long, Set<String>> batchGetUserDevices(Set<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return new HashMap<>();
}
StopWatch stopWatch = new StopWatch("batchGetUserDevices");
stopWatch.start("cache-batch-lookup");
// 1. 批量从缓存获取
Map<Long, Set<String>> result = cacheService.batchGetUserDevices(userIds);
// 2. 找出缓存未命中的用户
Set<Long> missingUsers = userIds.stream()
.filter(userId -> !result.containsKey(userId))
.collect(Collectors.toSet());
stopWatch.stop();
if (!missingUsers.isEmpty()) {
stopWatch.start("db-batch-query");
// 3. 批量查询数据库
String inClause = missingUsers.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
String sql = String.format("""
SELECT du.user_id, GROUP_CONCAT(du.device_sn) as device_sns
FROM t_device_user du
INNER JOIN t_device_info d ON du.device_sn = d.serial_number
WHERE du.user_id IN (%s)
AND du.status = 'BIND'
AND d.status = 'online'
GROUP BY du.user_id
""", inClause);
List<Map<String, Object>> dbResults = jdbcTemplate.queryForList(sql);
// 4. 处理查询结果并更新缓存
for (Map<String, Object> row : dbResults) {
Long userId = ((Number) row.get("user_id")).longValue();
String deviceSnsStr = (String) row.get("device_sns");
if (deviceSnsStr != null) {
Set<String> devices = Arrays.stream(deviceSnsStr.split(","))
.collect(Collectors.toSet());
result.put(userId, devices);
// 异步更新缓存
CompletableFuture.runAsync(() ->
cacheService.cacheUserDevices(userId, devices));
}
}
stopWatch.stop();
}
log.debug("批量获取用户设备关系: users={}, cache_hits={}, db_queries={}, time={}ms",
userIds.size(), userIds.size() - missingUsers.size(), missingUsers.size(),
stopWatch.getTotalTimeMillis());
return result;
}
/**
* 批量获取部门用户关系
* 性能目标: <15ms for 50 orgs
*/
public Map<Long, Set<Long>> batchGetOrgUsers(Set<Long> orgIds) {
if (CollectionUtils.isEmpty(orgIds)) {
return new HashMap<>();
}
// 1. 批量从缓存获取
Map<Long, Set<Long>> result = cacheService.batchGetOrgUsers(orgIds);
// 2. 找出缓存未命中的部门
Set<Long> missingOrgs = orgIds.stream()
.filter(orgId -> !result.containsKey(orgId))
.collect(Collectors.toSet());
if (!missingOrgs.isEmpty()) {
// 3. 并行查询数据库
missingOrgs.parallelStream().forEach(orgId -> {
Set<Long> users = getOrgUsers(orgId); // 这个方法内部会处理缓存
if (!users.isEmpty()) {
result.put(orgId, users);
}
});
}
return result;
}
// =====================================================
// 复合查询场景
// =====================================================
/**
* 获取用户完整关系信息(用户-部门-设备-健康数据)
* 性能目标: <8ms
*/
public Map<String, Object> getUserCompleteRelations(Long userId) {
Map<String, Object> result = new HashMap<>();
// 并行获取各种关系数据
CompletableFuture<Set<Long>> orgsFuture = CompletableFuture
.supplyAsync(() -> getUserOrgs(userId));
CompletableFuture<Set<String>> devicesFuture = CompletableFuture
.supplyAsync(() -> getUserDevices(userId));
CompletableFuture<Map<String, Object>> healthFuture = CompletableFuture
.supplyAsync(() -> getUserLatestHealth(userId));
CompletableFuture<Set<Long>> alertsFuture = CompletableFuture
.supplyAsync(() -> getUserActiveAlerts(userId));
// 等待所有异步任务完成
CompletableFuture.allOf(orgsFuture, devicesFuture, healthFuture, alertsFuture).join();
try {
result.put("orgs", orgsFuture.get());
result.put("devices", devicesFuture.get());
result.put("latestHealth", healthFuture.get());
result.put("activeAlerts", alertsFuture.get());
result.put("timestamp", LocalDateTime.now().format(TIMESTAMP_FORMAT));
} catch (Exception e) {
log.error("获取用户完整关系信息失败: userId={}", userId, e);
}
return result;
}
/**
* 获取部门完整关系信息(部门-用户-设备-健康汇总)
* 性能目标: <10ms
*/
public Map<String, Object> getOrgCompleteRelations(Long orgId) {
Map<String, Object> result = new HashMap<>();
// 并行获取各种关系数据
CompletableFuture<Set<Long>> usersFuture = CompletableFuture
.supplyAsync(() -> getOrgUsers(orgId));
CompletableFuture<Set<String>> devicesFuture = CompletableFuture
.supplyAsync(() -> getOrgDevices(orgId));
CompletableFuture<Set<Long>> descendantsFuture = CompletableFuture
.supplyAsync(() -> getOrgDescendants(orgId));
CompletableFuture<Map<String, Object>> healthSummaryFuture = CompletableFuture
.supplyAsync(() -> getOrgHealthSummary(orgId));
// 等待所有异步任务完成
CompletableFuture.allOf(usersFuture, devicesFuture, descendantsFuture, healthSummaryFuture).join();
try {
result.put("users", usersFuture.get());
result.put("devices", devicesFuture.get());
result.put("descendants", descendantsFuture.get());
result.put("healthSummary", healthSummaryFuture.get());
result.put("timestamp", LocalDateTime.now().format(TIMESTAMP_FORMAT));
} catch (Exception e) {
log.error("获取部门完整关系信息失败: orgId={}", orgId, e);
}
return result;
}
// =====================================================
// 缓存管理工具方法
// =====================================================
/**
* 刷新用户相关缓存
*/
public void refreshUserCache(Long userId) {
log.info("刷新用户相关缓存: userId={}", userId);
// 清除旧缓存
cacheService.evictUserCache(userId);
// 预热新缓存
CompletableFuture.runAsync(() -> {
getUserOrgs(userId);
getUserDevices(userId);
getUserLatestHealth(userId);
getUserActiveAlerts(userId);
});
}
/**
* 刷新部门相关缓存
*/
public void refreshOrgCache(Long orgId) {
log.info("刷新部门相关缓存: orgId={}", orgId);
// 清除旧缓存
cacheService.evictOrgCache(orgId);
// 预热新缓存
CompletableFuture.runAsync(() -> {
getOrgUsers(orgId);
getOrgDevices(orgId);
getOrgDescendants(orgId);
getOrgHealthSummary(orgId);
});
}
/**
* 刷新设备相关缓存
*/
public void refreshDeviceCache(String deviceSn) {
log.info("刷新设备相关缓存: deviceSn={}", deviceSn);
// 清除旧缓存
cacheService.evictDeviceCache(deviceSn);
// 预热新缓存
CompletableFuture.runAsync(() -> {
getDeviceUser(deviceSn);
getDeviceOrg(deviceSn);
});
}
}
@@ -0,0 +1,44 @@
package com.ljwx.common.cache;
import java.util.concurrent.CompletableFuture;
/**
* 缓存预热服务接口
*/
public interface ICacheWarmupService {
/**
* 手动触发缓存预热
*/
void manualWarmup();
/**
* 获取预热状态
*/
boolean isWarmupCompleted();
/**
* 定时刷新热点数据缓存
*/
void refreshHotDataCache();
/**
* 预热租户关系数据
*/
CompletableFuture<Void> warmupTenantRelations();
/**
* 预热用户-部门关系
*/
CompletableFuture<Void> warmupUserOrgRelations();
/**
* 预热设备关系数据
*/
CompletableFuture<Void> warmupDeviceRelations();
/**
* 预热组织层级结构
*/
CompletableFuture<Void> warmupOrgHierarchy();
}
@@ -0,0 +1,484 @@
package com.ljwx.common.cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
/**
* Redis缓存预热和更新服务
* 系统启动时自动预热核心关系数据,定时更新热点数据
*/
@Slf4j
@Service
public class RedisCacheWarmupService implements ApplicationRunner, ICacheWarmupService {
@Autowired
private RedisRelationCacheService relationCacheService;
@Autowired
private JdbcTemplate jdbcTemplate;
// 分批处理大小
private static final int BATCH_SIZE = 1000;
// 预热状态追踪
private boolean warmupCompleted = false;
/**
* 应用启动后自动执行缓存预热
*/
@Override
public void run(ApplicationArguments args) {
log.info("开始执行Redis缓存预热...");
long startTime = System.currentTimeMillis();
try {
// 并行执行预热任务
CompletableFuture<Void> tenantWarmup = warmupTenantRelations();
CompletableFuture<Void> userOrgWarmup = warmupUserOrgRelations();
CompletableFuture<Void> deviceWarmup = warmupDeviceRelations();
CompletableFuture<Void> orgHierarchyWarmup = warmupOrgHierarchy();
// 等待所有预热任务完成
CompletableFuture.allOf(tenantWarmup, userOrgWarmup, deviceWarmup, orgHierarchyWarmup).join();
warmupCompleted = true;
long duration = System.currentTimeMillis() - startTime;
log.info("Redis缓存预热完成,耗时: {}ms", duration);
} catch (Exception e) {
log.error("Redis缓存预热失败", e);
}
}
/**
* 预热租户关系数据
*/
@Override
@Async
public CompletableFuture<Void> warmupTenantRelations() {
log.info("开始预热租户关系数据...");
try {
// 1. 预热租户-用户关系
String userSql = """
SELECT customer_id, GROUP_CONCAT(id) as user_ids
FROM sys_user
WHERE is_deleted = 0 AND status = '1'
GROUP BY customer_id
""";
List<Map<String, Object>> tenantUsers = jdbcTemplate.queryForList(userSql);
for (Map<String, Object> row : tenantUsers) {
Long customerId = ((Number) row.get("customer_id")).longValue();
String userIdsStr = (String) row.get("user_ids");
if (userIdsStr != null) {
Set<Long> userIds = Arrays.stream(userIdsStr.split(","))
.map(Long::valueOf)
.collect(Collectors.toSet());
relationCacheService.cacheTenantUsers(customerId, userIds);
}
}
// 2. 预热租户-部门关系
String orgSql = """
SELECT customer_id, GROUP_CONCAT(id) as org_ids
FROM sys_org_units
WHERE is_deleted = 0 AND status = '1'
GROUP BY customer_id
""";
List<Map<String, Object>> tenantOrgs = jdbcTemplate.queryForList(orgSql);
for (Map<String, Object> row : tenantOrgs) {
Long customerId = ((Number) row.get("customer_id")).longValue();
String orgIdsStr = (String) row.get("org_ids");
if (orgIdsStr != null) {
Set<Long> orgIds = Arrays.stream(orgIdsStr.split(","))
.map(Long::valueOf)
.collect(Collectors.toSet());
relationCacheService.cacheTenantOrgs(customerId, orgIds);
}
}
log.info("租户关系数据预热完成: {} 个租户", tenantUsers.size());
} catch (Exception e) {
log.error("租户关系数据预热失败", e);
}
return CompletableFuture.completedFuture(null);
}
/**
* 预热用户-部门关系(基于闭包表)
*/
@Override
@Async
public CompletableFuture<Void> warmupUserOrgRelations() {
log.info("开始预热用户-部门关系数据...");
try {
// 1. 预热用户-部门关系
String userOrgSql = """
SELECT uo.user_id, GROUP_CONCAT(uo.org_id) as org_ids
FROM sys_user_org uo
INNER JOIN sys_user u ON uo.user_id = u.id
WHERE uo.is_deleted = 0 AND u.is_deleted = 0
GROUP BY uo.user_id
""";
List<Map<String, Object>> userOrgs = jdbcTemplate.queryForList(userOrgSql);
for (Map<String, Object> row : userOrgs) {
Long userId = ((Number) row.get("user_id")).longValue();
String orgIdsStr = (String) row.get("org_ids");
if (orgIdsStr != null) {
Set<Long> orgIds = Arrays.stream(orgIdsStr.split(","))
.map(Long::valueOf)
.collect(Collectors.toSet());
relationCacheService.cacheUserOrgs(userId, orgIds);
}
}
// 2. 预热部门-用户关系(包含子部门用户)
String orgUserSql = """
SELECT o.id as org_id,
GROUP_CONCAT(DISTINCT u.id) as direct_user_ids
FROM sys_org_units o
LEFT JOIN sys_user_org uo ON o.id = uo.org_id AND uo.is_deleted = 0
LEFT JOIN sys_user u ON uo.user_id = u.id AND u.is_deleted = 0
WHERE o.is_deleted = 0 AND o.status = '1'
GROUP BY o.id
""";
List<Map<String, Object>> orgUsers = jdbcTemplate.queryForList(orgUserSql);
for (Map<String, Object> row : orgUsers) {
Long orgId = ((Number) row.get("org_id")).longValue();
String userIdsStr = (String) row.get("direct_user_ids");
// 获取包含子部门的所有用户
Set<Long> allUserIds = getAllOrgUsers(orgId);
if (!allUserIds.isEmpty()) {
relationCacheService.cacheOrgUsers(orgId, allUserIds);
}
}
log.info("用户-部门关系数据预热完成: {} 个用户, {} 个部门", userOrgs.size(), orgUsers.size());
} catch (Exception e) {
log.error("用户-部门关系数据预热失败", e);
}
return CompletableFuture.completedFuture(null);
}
/**
* 预热设备关系数据
*/
@Override
@Async
public CompletableFuture<Void> warmupDeviceRelations() {
log.info("开始预热设备关系数据...");
try {
// 1. 预热用户-设备关系 (解决字符集冲突问题)
String userDeviceSql = """
SELECT du.user_id, GROUP_CONCAT(du.device_sn) as device_sns
FROM t_device_user du
INNER JOIN sys_user u ON du.user_id = u.id
INNER JOIN t_device_info d ON du.device_sn COLLATE utf8mb4_general_ci = d.serial_number
WHERE u.is_deleted = 0 AND d.status = 'online'
GROUP BY du.user_id
""";
List<Map<String, Object>> userDevices = jdbcTemplate.queryForList(userDeviceSql);
for (Map<String, Object> row : userDevices) {
Long userId = ((Number) row.get("user_id")).longValue();
String deviceSnsStr = (String) row.get("device_sns");
if (deviceSnsStr != null) {
Set<String> deviceSns = Arrays.stream(deviceSnsStr.split(","))
.collect(Collectors.toSet());
relationCacheService.cacheUserDevices(userId, deviceSns);
}
}
// 2. 预热设备-用户关系 (解决字符集冲突问题)
String deviceUserSql = """
SELECT du.device_sn, du.user_id
FROM t_device_user du
INNER JOIN sys_user u ON du.user_id = u.id
INNER JOIN t_device_info d ON du.device_sn COLLATE utf8mb4_general_ci = d.serial_number
WHERE u.is_deleted = 0 AND d.status = 'online'
""";
List<Map<String, Object>> deviceUsers = jdbcTemplate.queryForList(deviceUserSql);
for (Map<String, Object> row : deviceUsers) {
String deviceSn = (String) row.get("device_sn");
Long userId = ((Number) row.get("user_id")).longValue();
relationCacheService.cacheDeviceUser(deviceSn, userId);
}
// 3. 预热设备-部门关系 (解决字符集冲突问题)
String deviceOrgSql = """
SELECT d.serial_number, uo.org_id
FROM t_device_info d
INNER JOIN t_device_user du ON d.serial_number = du.device_sn COLLATE utf8mb4_general_ci
INNER JOIN sys_user u ON du.user_id = u.id
INNER JOIN sys_user_org uo ON u.id = uo.user_id
WHERE d.status = 'online' AND u.is_deleted = 0 AND uo.is_deleted = 0
""";
List<Map<String, Object>> deviceOrgs = jdbcTemplate.queryForList(deviceOrgSql);
for (Map<String, Object> row : deviceOrgs) {
String deviceSn = (String) row.get("serial_number");
Long orgId = ((Number) row.get("org_id")).longValue();
relationCacheService.cacheDeviceOrg(deviceSn, orgId);
}
// 4. 预热部门-设备关系(包含子部门设备)
preloadOrgDevices();
log.info("设备关系数据预热完成: {} 个用户设备, {} 个设备用户", userDevices.size(), deviceUsers.size());
} catch (Exception e) {
log.error("设备关系数据预热失败", e);
}
return CompletableFuture.completedFuture(null);
}
/**
* 预热组织层级结构(基于闭包表)
*/
@Override
@Async
public CompletableFuture<Void> warmupOrgHierarchy() {
log.info("开始预热组织层级结构数据...");
try {
// 预热部门后代关系
String descendantsSql = """
SELECT ancestor_id, GROUP_CONCAT(descendant_id) as descendant_ids
FROM sys_org_closure
WHERE depth > 0
GROUP BY ancestor_id
""";
List<Map<String, Object>> orgDescendants = jdbcTemplate.queryForList(descendantsSql);
for (Map<String, Object> row : orgDescendants) {
Long ancestorId = ((Number) row.get("ancestor_id")).longValue();
String descendantIdsStr = (String) row.get("descendant_ids");
if (descendantIdsStr != null) {
Set<Long> descendantIds = Arrays.stream(descendantIdsStr.split(","))
.map(Long::valueOf)
.collect(Collectors.toSet());
relationCacheService.cacheOrgDescendants(ancestorId, descendantIds);
}
}
log.info("组织层级结构预热完成: {} 个层级关系", orgDescendants.size());
} catch (Exception e) {
log.error("组织层级结构预热失败", e);
}
return CompletableFuture.completedFuture(null);
}
/**
* 定时更新热点数据缓存 - 每5分钟执行
*/
@Override
@Scheduled(fixedRate = 300000)
public void refreshHotDataCache() {
if (!warmupCompleted) {
return;
}
log.debug("开始刷新热点数据缓存...");
try {
// 1. 刷新用户最新健康数据
refreshUserLatestHealthData();
// 2. 刷新用户活跃告警数据
refreshUserActiveAlerts();
// 3. 刷新部门健康数据汇总
refreshOrgHealthSummary();
} catch (Exception e) {
log.error("热点数据缓存刷新失败", e);
}
}
/**
* 刷新用户最新健康数据
*/
private void refreshUserLatestHealthData() {
String sql = """
SELECT h.user_id,
h.heart_rate,
h.pressure_high,
h.pressure_low,
h.blood_oxygen,
h.temperature,
h.timestamp
FROM t_user_health_data h
INNER JOIN (
SELECT user_id, MAX(timestamp) as latest_time
FROM t_user_health_data
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND is_deleted = 0
GROUP BY user_id
) latest ON h.user_id = latest.user_id AND h.timestamp = latest.latest_time
WHERE h.is_deleted = 0
""";
List<Map<String, Object>> latestHealthData = jdbcTemplate.queryForList(sql);
for (Map<String, Object> row : latestHealthData) {
Long userId = ((Number) row.get("user_id")).longValue();
Map<String, Object> healthData = new HashMap<>(row);
healthData.remove("user_id");
relationCacheService.cacheUserLatestHealth(userId, healthData);
}
log.debug("刷新用户最新健康数据: {} 条", latestHealthData.size());
}
/**
* 刷新用户活跃告警数据
*/
private void refreshUserActiveAlerts() {
String sql = """
SELECT user_id, GROUP_CONCAT(id) as alert_ids
FROM t_alert_info
WHERE alert_status IN ('PENDING', 'IN_PROGRESS')
AND create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY user_id
""";
List<Map<String, Object>> activeAlerts = jdbcTemplate.queryForList(sql);
for (Map<String, Object> row : activeAlerts) {
Long userId = ((Number) row.get("user_id")).longValue();
String alertIdsStr = (String) row.get("alert_ids");
if (alertIdsStr != null) {
Set<Long> alertIds = Arrays.stream(alertIdsStr.split(","))
.map(Long::valueOf)
.collect(Collectors.toSet());
relationCacheService.cacheUserActiveAlerts(userId, alertIds);
}
}
log.debug("刷新用户活跃告警数据: {} 个用户", activeAlerts.size());
}
/**
* 刷新部门健康数据汇总
*/
private void refreshOrgHealthSummary() {
String sql = """
SELECT uo.org_id,
COUNT(h.id) as total_records,
AVG(h.heart_rate) as avg_heart_rate,
AVG(h.pressure_high) as avg_pressure_high,
AVG(h.pressure_low) as avg_pressure_low,
AVG(h.blood_oxygen) as avg_blood_oxygen
FROM sys_user_org uo
INNER JOIN sys_user u ON uo.user_id = u.id
LEFT JOIN t_user_health_data h ON u.id = h.user_id
AND h.timestamp >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND h.is_deleted = 0
WHERE uo.is_deleted = 0 AND u.is_deleted = 0
GROUP BY uo.org_id
HAVING total_records > 0
""";
List<Map<String, Object>> orgSummaries = jdbcTemplate.queryForList(sql);
for (Map<String, Object> row : orgSummaries) {
Long orgId = ((Number) row.get("org_id")).longValue();
Map<String, Object> summary = new HashMap<>(row);
summary.remove("org_id");
relationCacheService.cacheOrgHealthSummary(orgId, summary);
}
log.debug("刷新部门健康数据汇总: {} 个部门", orgSummaries.size());
}
/**
* 获取部门及其子部门的所有用户
*/
private Set<Long> getAllOrgUsers(Long orgId) {
// 使用闭包表获取所有后代部门
String sql = """
SELECT DISTINCT u.id
FROM sys_org_closure c
INNER JOIN sys_user_org uo ON c.descendant_id = uo.org_id
INNER JOIN sys_user u ON uo.user_id = u.id
WHERE c.ancestor_id = ? AND uo.is_deleted = 0 AND u.is_deleted = 0
""";
List<Long> userIds = jdbcTemplate.queryForList(sql, Long.class, orgId);
return new HashSet<>(userIds);
}
/**
* 预加载部门设备关系
*/
private void preloadOrgDevices() {
// 获取所有部门ID
List<Long> orgIds = jdbcTemplate.queryForList(
"SELECT id FROM sys_org_units WHERE is_deleted = 0", Long.class);
for (Long orgId : orgIds) {
// 获取部门及其子部门的所有设备 (解决字符集冲突问题)
String sql = """
SELECT DISTINCT d.serial_number
FROM sys_org_closure c
INNER JOIN sys_user_org uo ON c.descendant_id = uo.org_id
INNER JOIN sys_user u ON uo.user_id = u.id
INNER JOIN t_device_user du ON u.id = du.user_id
INNER JOIN t_device_info d ON du.device_sn COLLATE utf8mb4_general_ci = d.serial_number
WHERE c.ancestor_id = ?
AND uo.is_deleted = 0
AND u.is_deleted = 0
AND d.status = 'online'
""";
List<String> deviceSns = jdbcTemplate.queryForList(sql, String.class, orgId);
if (!deviceSns.isEmpty()) {
relationCacheService.cacheOrgDevices(orgId, new HashSet<>(deviceSns));
}
}
}
/**
* 手动触发缓存预热
*/
@Override
public void manualWarmup() {
log.info("手动触发缓存预热...");
warmupCompleted = false;
run(null);
}
/**
* 获取预热状态
*/
@Override
public boolean isWarmupCompleted() {
return warmupCompleted;
}
}
@@ -0,0 +1,454 @@
package com.ljwx.common.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Redis关系缓存服务 - 高性能多维度关联查询
* 基于闭包表和Redis实现毫秒级查询响应
*/
@Service
public class RedisRelationCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =====================================================
// 缓存KEY常量定义
// =====================================================
// 租户相关
private static final String TENANT_USERS = "tenant:users:"; // 租户-用户映射
private static final String TENANT_ORGS = "tenant:orgs:"; // 租户-部门映射
private static final String TENANT_DEVICES = "tenant:devices:"; // 租户-设备映射
// 用户关联
private static final String USER_ORGS = "user:orgs:"; // 用户-部门映射
private static final String USER_DEVICES = "user:devices:"; // 用户-设备映射
private static final String USER_POSITIONS = "user:positions:"; // 用户-岗位映射
// 部门关联
private static final String ORG_USERS = "org:users:"; // 部门-用户映射
private static final String ORG_DEVICES = "org:devices:"; // 部门-设备映射
private static final String ORG_DESCENDANTS = "org:descendants:"; // 部门-后代映射
private static final String ORG_ANCESTORS = "org:ancestors:"; // 部门-祖先映射
// 设备关联
private static final String DEVICE_USER = "device:user:"; // 设备-用户映射
private static final String DEVICE_ORG = "device:org:"; // 设备-部门映射
// 业务数据关联
private static final String USER_HEALTH_LATEST = "user:health:latest:"; // 用户最新健康数据
private static final String ORG_HEALTH_SUMMARY = "org:health:summary:"; // 部门健康数据汇总
private static final String USER_ALERTS_ACTIVE = "user:alerts:active:"; // 用户活跃告警
private static final String ORG_ALERTS_SUMMARY = "org:alerts:summary:"; // 部门告警汇总
// 缓存过期时间(秒)
private static final long CACHE_EXPIRE_SHORT = 300; // 5分钟 - 频繁变更数据
private static final long CACHE_EXPIRE_MEDIUM = 1800; // 30分钟 - 一般关系数据
private static final long CACHE_EXPIRE_LONG = 3600; // 1小时 - 相对稳定数据
// =====================================================
// 租户级别查询 - 顶层缓存
// =====================================================
/**
* 获取租户下所有用户ID列表
*/
public Set<Long> getTenantUsers(Long customerId) {
String key = TENANT_USERS + customerId;
Set<Object> userIds = redisTemplate.opsForSet().members(key);
return convertToLongSet(userIds);
}
/**
* 缓存租户用户关系
*/
public void cacheTenantUsers(Long customerId, Set<Long> userIds) {
String key = TENANT_USERS + customerId;
if (!CollectionUtils.isEmpty(userIds)) {
redisTemplate.opsForSet().add(key, userIds.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_LONG, TimeUnit.SECONDS);
}
}
/**
* 获取租户下所有部门ID列表
*/
public Set<Long> getTenantOrgs(Long customerId) {
String key = TENANT_ORGS + customerId;
Set<Object> orgIds = redisTemplate.opsForSet().members(key);
return convertToLongSet(orgIds);
}
/**
* 缓存租户部门关系
*/
public void cacheTenantOrgs(Long customerId, Set<Long> orgIds) {
String key = TENANT_ORGS + customerId;
if (!CollectionUtils.isEmpty(orgIds)) {
redisTemplate.opsForSet().add(key, orgIds.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_LONG, TimeUnit.SECONDS);
}
}
// =====================================================
// 用户-部门关联查询 (基于闭包表优化)
// =====================================================
/**
* 获取用户所属部门列表(包含层级关系)
*/
public Set<Long> getUserOrgs(Long userId) {
String key = USER_ORGS + userId;
Set<Object> orgIds = redisTemplate.opsForSet().members(key);
return convertToLongSet(orgIds);
}
/**
* 缓存用户部门关系
*/
public void cacheUserOrgs(Long userId, Set<Long> orgIds) {
String key = USER_ORGS + userId;
redisTemplate.delete(key); // 清除旧数据
if (!CollectionUtils.isEmpty(orgIds)) {
redisTemplate.opsForSet().add(key, orgIds.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
}
/**
* 获取部门下所有用户(包含子部门用户)
*/
public Set<Long> getOrgUsers(Long orgId) {
String key = ORG_USERS + orgId;
Set<Object> userIds = redisTemplate.opsForSet().members(key);
return convertToLongSet(userIds);
}
/**
* 缓存部门用户关系(包含子部门)
*/
public void cacheOrgUsers(Long orgId, Set<Long> userIds) {
String key = ORG_USERS + orgId;
redisTemplate.delete(key);
if (!CollectionUtils.isEmpty(userIds)) {
redisTemplate.opsForSet().add(key, userIds.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
}
/**
* 获取部门所有后代部门ID(基于闭包表)
*/
public Set<Long> getOrgDescendants(Long orgId) {
String key = ORG_DESCENDANTS + orgId;
Set<Object> descendants = redisTemplate.opsForSet().members(key);
return convertToLongSet(descendants);
}
/**
* 缓存部门后代关系
*/
public void cacheOrgDescendants(Long orgId, Set<Long> descendants) {
String key = ORG_DESCENDANTS + orgId;
redisTemplate.delete(key);
if (!CollectionUtils.isEmpty(descendants)) {
redisTemplate.opsForSet().add(key, descendants.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_LONG, TimeUnit.SECONDS); // 层级关系相对稳定
}
}
// =====================================================
// 用户-设备关联查询
// =====================================================
/**
* 获取用户关联的设备列表
*/
public Set<String> getUserDevices(Long userId) {
String key = USER_DEVICES + userId;
Set<Object> deviceSns = redisTemplate.opsForSet().members(key);
return convertToStringSet(deviceSns);
}
/**
* 缓存用户设备关系
*/
public void cacheUserDevices(Long userId, Set<String> deviceSns) {
String key = USER_DEVICES + userId;
redisTemplate.delete(key);
if (!CollectionUtils.isEmpty(deviceSns)) {
redisTemplate.opsForSet().add(key, deviceSns.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
}
/**
* 获取设备关联的用户
*/
public Long getDeviceUser(String deviceSn) {
String key = DEVICE_USER + deviceSn;
Object userId = redisTemplate.opsForValue().get(key);
return userId != null ? Long.valueOf(userId.toString()) : null;
}
/**
* 缓存设备用户关系
*/
public void cacheDeviceUser(String deviceSn, Long userId) {
String key = DEVICE_USER + deviceSn;
redisTemplate.opsForValue().set(key, userId, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
// =====================================================
// 部门-设备关联查询
// =====================================================
/**
* 获取部门下所有设备(包含子部门设备)
*/
public Set<String> getOrgDevices(Long orgId) {
String key = ORG_DEVICES + orgId;
Set<Object> deviceSns = redisTemplate.opsForSet().members(key);
return convertToStringSet(deviceSns);
}
/**
* 缓存部门设备关系
*/
public void cacheOrgDevices(Long orgId, Set<String> deviceSns) {
String key = ORG_DEVICES + orgId;
redisTemplate.delete(key);
if (!CollectionUtils.isEmpty(deviceSns)) {
redisTemplate.opsForSet().add(key, deviceSns.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
}
/**
* 获取设备所属部门
*/
public Long getDeviceOrg(String deviceSn) {
String key = DEVICE_ORG + deviceSn;
Object orgId = redisTemplate.opsForValue().get(key);
return orgId != null ? Long.valueOf(orgId.toString()) : null;
}
/**
* 缓存设备部门关系
*/
public void cacheDeviceOrg(String deviceSn, Long orgId) {
String key = DEVICE_ORG + deviceSn;
redisTemplate.opsForValue().set(key, orgId, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
// =====================================================
// 业务数据缓存查询
// =====================================================
/**
* 获取用户最新健康数据
*/
public Map<String, Object> getUserLatestHealth(Long userId) {
String key = USER_HEALTH_LATEST + userId;
Map<Object, Object> healthData = redisTemplate.opsForHash().entries(key);
return convertToStringObjectMap(healthData);
}
/**
* 缓存用户最新健康数据
*/
public void cacheUserLatestHealth(Long userId, Map<String, Object> healthData) {
String key = USER_HEALTH_LATEST + userId;
if (!CollectionUtils.isEmpty(healthData)) {
redisTemplate.opsForHash().putAll(key, healthData);
redisTemplate.expire(key, CACHE_EXPIRE_SHORT, TimeUnit.SECONDS);
}
}
/**
* 获取用户活跃告警列表
*/
public Set<Long> getUserActiveAlerts(Long userId) {
String key = USER_ALERTS_ACTIVE + userId;
Set<Object> alertIds = redisTemplate.opsForSet().members(key);
return convertToLongSet(alertIds);
}
/**
* 缓存用户活跃告警
*/
public void cacheUserActiveAlerts(Long userId, Set<Long> alertIds) {
String key = USER_ALERTS_ACTIVE + userId;
redisTemplate.delete(key);
if (!CollectionUtils.isEmpty(alertIds)) {
redisTemplate.opsForSet().add(key, alertIds.toArray());
redisTemplate.expire(key, CACHE_EXPIRE_SHORT, TimeUnit.SECONDS);
}
}
/**
* 获取部门健康数据汇总
*/
public Map<String, Object> getOrgHealthSummary(Long orgId) {
String key = ORG_HEALTH_SUMMARY + orgId;
Map<Object, Object> summary = redisTemplate.opsForHash().entries(key);
return convertToStringObjectMap(summary);
}
/**
* 缓存部门健康数据汇总
*/
public void cacheOrgHealthSummary(Long orgId, Map<String, Object> summary) {
String key = ORG_HEALTH_SUMMARY + orgId;
if (!CollectionUtils.isEmpty(summary)) {
redisTemplate.opsForHash().putAll(key, summary);
redisTemplate.expire(key, CACHE_EXPIRE_MEDIUM, TimeUnit.SECONDS);
}
}
// =====================================================
// 批量操作优化
// =====================================================
/**
* 批量获取用户设备关系
*/
public Map<Long, Set<String>> batchGetUserDevices(Set<Long> userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return new HashMap<>();
}
Map<Long, Set<String>> result = new HashMap<>();
// 逐个查询,因为Redis Set操作不支持批量获取members
for (Long userId : userIds) {
Set<String> devices = getUserDevices(userId);
if (!devices.isEmpty()) {
result.put(userId, devices);
}
}
return result;
}
/**
* 批量获取部门用户关系
*/
public Map<Long, Set<Long>> batchGetOrgUsers(Set<Long> orgIds) {
if (CollectionUtils.isEmpty(orgIds)) {
return new HashMap<>();
}
Map<Long, Set<Long>> result = new HashMap<>();
for (Long orgId : orgIds) {
Set<Long> users = getOrgUsers(orgId);
if (!CollectionUtils.isEmpty(users)) {
result.put(orgId, users);
}
}
return result;
}
// =====================================================
// 缓存失效和清理
// =====================================================
/**
* 清除用户相关缓存
*/
public void evictUserCache(Long userId) {
Set<String> keys = redisTemplate.keys("*:*:" + userId + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
}
/**
* 清除部门相关缓存
*/
public void evictOrgCache(Long orgId) {
Set<String> keys = redisTemplate.keys("*:*:" + orgId + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
}
/**
* 清除设备相关缓存
*/
public void evictDeviceCache(String deviceSn) {
Set<String> keys = redisTemplate.keys("*:*:" + deviceSn + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
}
/**
* 清除租户相关缓存
*/
public void evictTenantCache(Long customerId) {
Set<String> keys = redisTemplate.keys("tenant:*:" + customerId + "*");
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
}
// =====================================================
// 工具方法
// =====================================================
private Set<Long> convertToLongSet(Set<Object> objects) {
if (CollectionUtils.isEmpty(objects)) {
return new HashSet<>();
}
return objects.stream()
.map(obj -> Long.valueOf(obj.toString()))
.collect(Collectors.toSet());
}
private Set<String> convertToStringSet(Set<Object> objects) {
if (CollectionUtils.isEmpty(objects)) {
return new HashSet<>();
}
return objects.stream()
.map(Object::toString)
.collect(Collectors.toSet());
}
private Map<String, Object> convertToStringObjectMap(Map<Object, Object> objectMap) {
if (CollectionUtils.isEmpty(objectMap)) {
return new HashMap<>();
}
return objectMap.entrySet().stream()
.collect(Collectors.toMap(
entry -> entry.getKey().toString(),
Map.Entry::getValue
));
}
/**
* 检查缓存是否存在
*/
public boolean cacheExists(String keyPrefix, Object id) {
String key = keyPrefix + id;
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
/**
* 获取缓存剩余过期时间
*/
public long getCacheExpire(String keyPrefix, Object id) {
String key = keyPrefix + id;
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
}