diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/AbstractTypedCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/AbstractTypedCache.java new file mode 100644 index 0000000..6eac903 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/AbstractTypedCache.java @@ -0,0 +1,51 @@ +package com.zhangy.skyeye.cache; + +import org.springframework.cache.Cache; + +import java.util.Map; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 抽象类型缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/3 14:31 + */ +public abstract class AbstractTypedCache { + + private final Cache cache; + + protected AbstractTypedCache(Cache cache) { + this.cache = cache; + } + + protected void put(String key, Object value) { + cache.put(key, value); + } + + protected T get(String key, Class type) { + return cache.get(key, type); + } + + protected Object getRaw(String key) { + Cache.ValueWrapper wrapper = cache.get(key); + return wrapper == null ? null : wrapper.get(); + } + + protected void evict(String key) { + cache.evict(key); + } + + /** 获取原生 Caffeine Map,用于全量读取 */ + public Map asMap() { + Object nativeCache = cache.getNativeCache(); + if (nativeCache instanceof com.github.benmanes.caffeine.cache.Cache) { + return ((com.github.benmanes.caffeine.cache.Cache) nativeCache).asMap(); + } + return null; + } + + protected Object getNativeCache() { + return cache.getNativeCache(); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/config/CacheBeanConfig.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/config/CacheBeanConfig.java new file mode 100644 index 0000000..819e176 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/config/CacheBeanConfig.java @@ -0,0 +1,100 @@ +package com.zhangy.skyeye.cache.config; + +import com.zhangy.skyeye.cache.publics.UserTokenTypedCache; +import com.zhangy.skyeye.cache.sar.SarTypedCache; +import com.zhangy.skyeye.cache.uav.UavTypedCache; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: Cache Bean 显式注册 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/3 14:15 + */ +@Configuration +public class CacheBeanConfig { + + /* ================= USER ================= */ + @Bean("userTokenNativeCache") + public Cache userTokens(CacheManager manager) { + return require(manager, "user-tokens"); + } + + // TypedCache 封装层 + @Bean + public UserTokenTypedCache userTokensCache( + @Qualifier("userTokenNativeCache") Cache cache) { + return new UserTokenTypedCache(cache); + } + + /* ================= SAR ================= */ + @Bean("sarPermanentNativeCache") + public Cache sarPermanent(CacheManager manager) { + return require(manager, "sar-permanent"); + } + + @Bean + public SarTypedCache sarPermanentCache( + @Qualifier("sarPermanentNativeCache") Cache cache) { + return new SarTypedCache(cache); + } + + @Bean("sarShortNativeCache") + public Cache sarShort(CacheManager manager) { + return require(manager, "sar-short"); + } + + @Bean + public SarTypedCache sarShortCache( + @Qualifier("sarShortNativeCache") Cache cache) { + return new SarTypedCache(cache); + } + + /* ================= UAV ================= */ + @Bean("uavPermanentNativeCache") + public Cache uavPermanent(CacheManager manager) { + return require(manager, "uav-permanent"); + } + + @Bean + public UavTypedCache uavPermanentCache( + @Qualifier("uavPermanentNativeCache") Cache cache) { + return new UavTypedCache(cache); + } + + @Bean("uavShortNativeCache") + public Cache uavShort(CacheManager manager) { + return require(manager, "uav-short"); + } + + @Bean + public UavTypedCache uavShortCache( + @Qualifier("uavShortNativeCache") Cache cache) { + return new UavTypedCache(cache); + } + + /* ================= IMAGE ================= */ + @Bean("imageJoinNativeCache") + public Cache imageJoin(CacheManager manager) { + return require(manager, "image-join-short"); + } + + @Bean + public UserTokenTypedCache imageJoinCache( + @Qualifier("imageJoinNativeCache") Cache cache) { + return new UserTokenTypedCache(cache); + } + + private Cache require(CacheManager manager, String name) { + Cache cache = manager.getCache(name); + if (cache == null) { + throw new IllegalStateException("未找到缓存: " + name); + } + return cache; + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/config/CacheConfig.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/config/CacheConfig.java similarity index 94% rename from backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/config/CacheConfig.java rename to backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/config/CacheConfig.java index d92f63d..d070055 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/config/CacheConfig.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/config/CacheConfig.java @@ -1,4 +1,4 @@ -package com.zhangy.skyeye.publics.config; +package com.zhangy.skyeye.cache.config; import com.github.benmanes.caffeine.cache.Caffeine; import lombok.extern.slf4j.Slf4j; @@ -52,12 +52,12 @@ public class CacheConfig { cacheManager.registerCustomCache("uav-permanent", Caffeine.newBuilder() .maximumSize(5000) // 根据无人机数量调整 .build()); - cacheManager.registerCustomCache("uav-status", Caffeine.newBuilder() + cacheManager.registerCustomCache("uav-short", Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) // 与原 CACHE_EXPIRE_SECONDS 一致 .maximumSize(1000) // 按设备/IP 数量预估 .recordStats() .build()); - cacheManager.registerCustomCache("image-join-state", Caffeine.newBuilder() + cacheManager.registerCustomCache("image-join-short", Caffeine.newBuilder() .expireAfterWrite(24, TimeUnit.HOURS) // 与原 CACHE_EXPIRE_SECOND 一致 .maximumSize(5000) // 航线数量预估 .build()); diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheDebugController.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheDebugController.java new file mode 100644 index 0000000..9d1beb9 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheDebugController.java @@ -0,0 +1,85 @@ +package com.zhangy.skyeye.cache.debug; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Profile; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Set; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 缓存调试工具(统一查看所有缓存) + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/6 12:55 + */ +@RestController +@RequestMapping("/debug/cache") +@RequiredArgsConstructor +@Profile("dev") +public class CacheDebugController { + + private final CacheDebugService cacheDebugService; + + /** + * 查看所有缓存 + */ + @GetMapping("/all") + public Map allCaches() { + return cacheDebugService.allCaches(); + } + + /** + * 查看缓存key + */ + @GetMapping("/keys/{name}") + public Set keys(@PathVariable String name) { + return cacheDebugService.keys(name); + } + + /** + * 查看某个缓存 + */ + @GetMapping("/{name}") + public Map cache(@PathVariable String name) { + return cacheDebugService.cache(name); + } + + /** + * 查看某个key + */ + @GetMapping("/{name}/{key}") + public Object get(@PathVariable String name, + @PathVariable String key) { + return cacheDebugService.get(name, key); + } + + /** + * 删除key + */ + @DeleteMapping("/{name}/{key}") + public String evict(@PathVariable String name, + @PathVariable String key) { + + cacheDebugService.evict(name, key); + return "OK"; + } + + /** + * 清空缓存 + */ + @DeleteMapping("/{name}") + public String clear(@PathVariable String name) { + + cacheDebugService.clear(name); + return "OK"; + } + + /** + * 缓存统计 + */ + @GetMapping("/stats") + public Object stats() { + return cacheDebugService.stats(); + } +} diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheDebugService.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheDebugService.java new file mode 100644 index 0000000..3c29edf --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheDebugService.java @@ -0,0 +1,93 @@ +package com.zhangy.skyeye.cache.debug; + +import com.github.benmanes.caffeine.cache.stats.CacheStats; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 监控逻辑 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/6 13:02 + */ +@Slf4j +@Service +@RequiredArgsConstructor +@Profile("dev") +public class CacheDebugService { + + private final CacheManager cacheManager; + + public Map allCaches() { + Map map = new LinkedHashMap<>(); + for (String name : cacheManager.getCacheNames()) { + map.put(name, cache(name)); + } + return map; + } + + public Map cache(String name) { + Cache cache = cacheManager.getCache(name); + if (!(cache instanceof CaffeineCache)) { + return Collections.emptyMap(); + } + return ((CaffeineCache) cache) + .getNativeCache() + .asMap(); + } + + public Set keys(String name) { + return cache(name).keySet(); + } + + public Object get(String name, Object key) { + Cache cache = cacheManager.getCache(name); + if (cache == null) { + return null; + } + Cache.ValueWrapper value = cache.get(key); + return value == null ? null : value.get(); + } + + public void evict(String name, Object key) { + Cache cache = cacheManager.getCache(name); + if (cache != null) { + cache.evict(key); + } + } + + public void clear(String name) { + Cache cache = cacheManager.getCache(name); + if (cache != null) { + cache.clear(); + } + } + + public Map stats() { + Map result = new LinkedHashMap<>(); + for (String name : cacheManager.getCacheNames()) { + Cache cache = cacheManager.getCache(name); + if (!(cache instanceof CaffeineCache)) { + continue; + } + CaffeineCache caffeine = (CaffeineCache) cache; + CacheStats stats = caffeine.getNativeCache().stats(); + Map info = new HashMap<>(); + info.put("size", caffeine.getNativeCache().asMap().size()); + info.put("hitCount", stats.hitCount()); + info.put("missCount", stats.missCount()); + info.put("hitRate", stats.hitRate()); + info.put("evictionCount", stats.evictionCount()); + result.put(name, info); + } + return result; + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheMonitorAspect.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheMonitorAspect.java new file mode 100644 index 0000000..d2a05de --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheMonitorAspect.java @@ -0,0 +1,48 @@ +package com.zhangy.skyeye.cache.debug; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.*; +import org.springframework.cache.Cache; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 自动监控缓存操作 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/6 13:06 + */ +@Slf4j +@Aspect +@Component +@Profile("dev") +public class CacheMonitorAspect { + + @After("execution(* org.springframework.cache.Cache.put(..))") + public void afterPut(JoinPoint jp) { + Cache cache = (Cache) jp.getTarget(); + Object key = jp.getArgs()[0]; + log.debug("CACHE PUT -> {} : {}", + cache.getName(), + key); + } + + @After("execution(* org.springframework.cache.Cache.get(..))") + public void afterGet(JoinPoint jp) { + Cache cache = (Cache) jp.getTarget(); + Object key = jp.getArgs()[0]; + log.debug("CACHE GET -> {} : {}", + cache.getName(), + key); + } + + @After("execution(* org.springframework.cache.Cache.evict(..))") + public void afterEvict(JoinPoint jp) { + Cache cache = (Cache) jp.getTarget(); + Object key = jp.getArgs()[0]; + log.warn("CACHE EVICT -> {} : {}", + cache.getName(), + key); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheWatcherUltimate.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheWatcherUltimate.java new file mode 100644 index 0000000..e2f71ea --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/debug/CacheWatcherUltimate.java @@ -0,0 +1,53 @@ +package com.zhangy.skyeye.cache.debug; + +import com.github.benmanes.caffeine.cache.Cache; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 实时监控任务 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/6 13:07 + */ +@Component +@RequiredArgsConstructor +@Slf4j +@Profile("dev") +public class CacheWatcherUltimate { + + private final CacheManager cacheManager; + + @Scheduled(fixedDelay = 30000) + public void watchCaches() { + log.info("========== CACHE STATUS BEGIN =========="); + for (String name : cacheManager.getCacheNames()) { + org.springframework.cache.Cache springCache = cacheManager.getCache(name); + if (!(springCache instanceof CaffeineCache)) { + continue; + } + CaffeineCache caffeineCache = (CaffeineCache) springCache; + Cache nativeCache = caffeineCache.getNativeCache(); + Map map = nativeCache.asMap(); + log.info("CACHE [{}] SIZE = {}", name, map.size()); + for (Map.Entry entry : map.entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + log.info( + " KEY = {} | VALUE_TYPE = {}", + key, + value == null ? "null" : value.getClass().getSimpleName() + ); + } + } + log.info("========== CACHE STATUS END =========="); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/image/ImageCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/image/ImageCache.java new file mode 100644 index 0000000..e8c8f13 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/image/ImageCache.java @@ -0,0 +1,36 @@ +package com.zhangy.skyeye.cache.image; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: IMAGE 缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/5 15:56 + */ +@Component +@RequiredArgsConstructor +public class ImageCache { + + private final Map caches; + + private ImageJoinTypedCache getShort() { + return caches.get("imageJoinShortCache"); + } + + public void put(String key, Object value) { + getShort().putValue(key, value); + } + + public T get(String key, Class clazz) { + return getShort().getValue(key, clazz); + } + + public void evict(String key) { + getShort().evictValue(key); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/image/ImageJoinTypedCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/image/ImageJoinTypedCache.java new file mode 100644 index 0000000..bfa4267 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/image/ImageJoinTypedCache.java @@ -0,0 +1,32 @@ +package com.zhangy.skyeye.cache.image; + +import com.zhangy.skyeye.cache.AbstractTypedCache; +import org.springframework.cache.Cache; + +import java.util.Map; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: IMAGE 缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/5 15:55 + */ +public class ImageJoinTypedCache extends AbstractTypedCache { + + public ImageJoinTypedCache(Cache cache) { + super(cache); + } + + public void putValue(String key, Object value) { + super.put(key, value); + } + + public T getValue(String key, Class clazz) { + return super.get(key, clazz); + } + + public void evictValue(String key) { + super.evict(key); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/publics/UserTokenCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/publics/UserTokenCache.java new file mode 100644 index 0000000..e85c168 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/publics/UserTokenCache.java @@ -0,0 +1,50 @@ +package com.zhangy.skyeye.cache.publics; + +import lombok.RequiredArgsConstructor; +import org.springframework.cache.Cache; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 业务聚合层:对外暴露用户 token 操作 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/5 14:06 + */ +@Component +@RequiredArgsConstructor +public class UserTokenCache { + + private final Map caches; + + private UserTokenTypedCache getUserTokenCache() { + return caches.get("userTokensCache"); + } + + // 保存 token + public void storeToken(String tokenKey, String token) { + getUserTokenCache().put(tokenKey, token); + } + + // 获取并验证 token 是否一致 + public boolean validateToken(String tokenKey, String currentToken) { + String stored = getUserTokenCache().get(tokenKey); + return stored != null && stored.equals(currentToken); + } + + // 获取 token + public String getToken(String tokenKey) { + return getUserTokenCache().get(tokenKey); + } + + // 删除 token + public void evictToken(String tokenKey) { + getUserTokenCache().evictValue(tokenKey); + } + + public Object getNativeCache() { + return getUserTokenCache().getNativeCacheObject(); // UserTokenTypedCache 里需要有 getCache() + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/publics/UserTokenTypedCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/publics/UserTokenTypedCache.java new file mode 100644 index 0000000..d0fbdf0 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/publics/UserTokenTypedCache.java @@ -0,0 +1,34 @@ +package com.zhangy.skyeye.cache.publics; + +import com.zhangy.skyeye.cache.AbstractTypedCache; +import org.springframework.cache.Cache; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 安全封装 User Token Cache + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/5 14:14 + */ +public class UserTokenTypedCache extends AbstractTypedCache { + + public UserTokenTypedCache(Cache cache) { + super(cache); + } + + public void put(String key, String token) { + super.put(key, token); + } + + public String get(String key) { + return super.get(key, String.class); + } + + public void evictValue(String key) { + super.evict(key); + } + + public Object getNativeCacheObject() { + return super.getNativeCache(); // UserTokenTypedCache 里需要有 getCache() + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/sar/SarCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/sar/SarCache.java new file mode 100644 index 0000000..81d7f89 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/sar/SarCache.java @@ -0,0 +1,95 @@ +package com.zhangy.skyeye.cache.sar; + +import com.zhangy.skyeye.device.entity.SkyeyePayload; +import com.zhangy.skyeye.jm.dto.JmSarStatusDTO; +import com.zhangy.skyeye.publics.consts.CacheKey; +import com.zhangy.skyeye.sar.dto.SarErrorDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: Sar 缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/3 14:24 + */ +@Component +@RequiredArgsConstructor +public class SarCache { + + private final Map caches; + + private SarTypedCache getPermanent() { + return caches.get("sarPermanentCache"); + } + + private SarTypedCache getShort() { + return caches.get("sarShortCache"); + } + + /* ========== 永久缓存:SAR 载荷操作 ========== */ + // 缓存 SAR 载荷 + public void cacheSar(SkyeyePayload sar) { + getPermanent().putValue(sar.getId().toString(), sar); + } + + // 获取单个 SAR + public SkyeyePayload getOne(Long sarId) { + return getPermanent().getValue(sarId.toString(), SkyeyePayload.class); + } + + // 删除指定 SAR + public void evictSar(Long sarId) { + getPermanent().evictValue(sarId.toString()); + } + + // 获取永久缓存中所有 SAR 对象 + public List getAll() { + Map map = getPermanent().asMap(); + if (map == null || map.isEmpty()) return List.of(); + return map.values().stream() + .filter(v -> v instanceof SkyeyePayload) + .map(v -> (SkyeyePayload) v) + .collect(Collectors.toList()); + } + + /* ========== 连接状态 ========== */ + public void markConnected(String ip) { + getPermanent().putValue(CacheKey.getSarConnected(ip), Boolean.TRUE); + } + + public boolean isConnected(String ip) { + return getPermanent().getValue(CacheKey.getSarConnected(ip), Boolean.class) != null; + } + + public void removeConnection(String ip) { + getPermanent().evictValue(CacheKey.getSarConnected(ip)); + } + + /* ========== 实时状态 ========== */ + public void saveStatus(String ip, JmSarStatusDTO dto) { + getShort().putValue(CacheKey.getSarStatus(ip), dto); + } + + public JmSarStatusDTO getLatestStatus(String ip) { + return getShort().getValue(CacheKey.getSarStatus(ip), JmSarStatusDTO.class); + } + + /* ========== 控制回执 ========== */ + public void saveControlBack(String ip, SarErrorDTO dto) { + getShort().putValue(CacheKey.getSarControlBack(ip), dto); + } + + public SarErrorDTO getControlBack(String ip) { + return getShort().getValue(CacheKey.getSarControlBack(ip), SarErrorDTO.class); + } + + public void clearControlBack(String ip) { + getShort().evictValue(CacheKey.getSarControlBack(ip)); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/sar/SarTypedCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/sar/SarTypedCache.java new file mode 100644 index 0000000..08aaadb --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/sar/SarTypedCache.java @@ -0,0 +1,39 @@ +package com.zhangy.skyeye.cache.sar; + +import com.zhangy.skyeye.cache.AbstractTypedCache; +import org.springframework.cache.Cache; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: 通用 SAR 缓存,可作为永久或短期缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/5 12:06 + */ +public class SarTypedCache extends AbstractTypedCache { + + public SarTypedCache(Cache cache) { + super(cache); + } + + /** + * 类型安全读取 + */ + public T getValue(String key, Class type) { + return super.get(key, type); + } + + /** + * 类型安全写入 + */ + public void putValue(String key, Object value) { + super.put(key, value); + } + + /** + * 类型安全删除 + */ + public void evictValue(String key) { + super.evict(key); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/uav/UavCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/uav/UavCache.java new file mode 100644 index 0000000..2193754 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/uav/UavCache.java @@ -0,0 +1,68 @@ +package com.zhangy.skyeye.cache.uav; + +import com.zhangy.skyeye.cache.sar.SarTypedCache; +import com.zhangy.skyeye.device.entity.SkyeyeUav; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: Uav 缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/4 14:19 + */ +@Component +@RequiredArgsConstructor +public class UavCache { + + private final Map caches; + + private UavTypedCache getPermanent() { + return caches.get("uavPermanentCache"); + } + + private UavTypedCache getShort() { + return caches.get("uavShortCache"); + } + + /* ========== 永久缓存:UAV 操作 ========== */ + public void put(SkyeyeUav uav) { + getPermanent().putValue(uav.getId().toString(), uav); + } + + public SkyeyeUav get(Long id) { + return getPermanent().getValue(id.toString(), SkyeyeUav.class); + } + + public void evict(Long id) { + getPermanent().evictValue(id.toString()); + } + + public List getAll() { + Map map = getPermanent().asMap(); + if (map == null) { + return List.of(); + } + return map.values().stream() + .map(v -> (SkyeyeUav) v) + .collect(Collectors.toList()); + } + + /* ========== 短期缓存 ========== */ + public void putShort(String key, Object value) { + getShort().putValue(key, value); + } + + public T getShort(String key, Class clazz) { + return getShort().getValue(key, clazz); + } + + public void evictShort(String key) { + getShort().evictValue(key); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/uav/UavTypedCache.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/uav/UavTypedCache.java new file mode 100644 index 0000000..978e568 --- /dev/null +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/cache/uav/UavTypedCache.java @@ -0,0 +1,30 @@ +package com.zhangy.skyeye.cache.uav; + +import com.zhangy.skyeye.cache.AbstractTypedCache; +import org.springframework.cache.Cache; + +/** + * @PROJECT_NAME: skyeyesystem_online + * @DESCRIPTION: Uav 缓存,可作为永久或短期缓存 + * @AUTHOR: GuanCheng Long + * @DATE: 2026/3/4 14:20 + */ +public class UavTypedCache extends AbstractTypedCache { + + public UavTypedCache(Cache cache) { + super(cache); + } + + public T getValue(String key, Class type) { + return super.get(key, type); + } + + public void putValue(String key, Object value) { + super.put(key, value); + } + + public void evictValue(String key) { + super.evict(key); + } +} + diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/PayloadServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/PayloadServiceImpl.java index ab38349..7a04ecd 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/PayloadServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/PayloadServiceImpl.java @@ -1,6 +1,7 @@ package com.zhangy.skyeye.device.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.common.extend.dto.PageDTO; import com.zhangy.skyeye.common.extend.dto.QueryDTO; import com.zhangy.skyeye.common.extend.enums.EnumUtil; @@ -15,14 +16,11 @@ import com.zhangy.skyeye.jm.dto.JmJobStatusDTO; import com.zhangy.skyeye.jm.dto.JmSarStatusDTO; import com.zhangy.skyeye.jm.dto.JmUavStatusDTO; import com.zhangy.skyeye.jm.service.JmJobStatusService; -import com.zhangy.skyeye.publics.consts.CacheKey; import com.zhangy.skyeye.publics.consts.ExecStatusEnum; -import com.zhangy.skyeye.publics.utils.CacheUtil; import com.zhangy.skyeye.sar.service.ISarControlService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,47 +31,26 @@ import java.util.stream.Collectors; @Slf4j @Service +@RequiredArgsConstructor public class PayloadServiceImpl implements IPayloadService { - @Autowired - private CacheManager cacheManager; - @Autowired private PayloadMapper payloadMapper; - @Autowired private JmJobStatusService sarJobStatusService; - @Autowired private ISarControlService sarControlService; - private Cache sarPermanentCache; // SAR 专用永久缓存 - private Cache sarShortCache; // 短时状态 + private final SarCache sarCache; @PostConstruct public void initAndCacheAllSar() { - // 初始化缓存实例(获取“盒子”) - sarPermanentCache = cacheManager.getCache("device-permanent"); - sarShortCache = cacheManager.getCache("sar-short-lived"); - // 防护检查(不抛异常,而是日志 + 降级) - if (sarPermanentCache == null) { - log.error("device-permanent 缓存未找到!请检查 CacheConfig 是否正确注册"); - throw new IllegalStateException("device-permanent 缓存未找到"); - } - if (sarShortCache == null) { - log.error("sar-short-lived 缓存未找到!请检查 CacheConfig"); - throw new IllegalStateException("sar-short-lived 缓存未找到"); - } // 开始缓存所有 SAR(如果 permanentCache 为 null,这里会安全跳过) PayloadQueryDTO payloadQueryDTO = new PayloadQueryDTO(); payloadQueryDTO.setType(PayloadTypeEnum.SAR.getCode()); List sarList = selectList(payloadQueryDTO); - if (sarPermanentCache != null) { - sarList.forEach(this::cacheSar); - log.info("SAR 载荷缓存完成,共 {} 条", sarList.size()); - } else { - log.warn("永久缓存不可用,跳过 SAR 预加载"); - } + sarList.forEach(sarCache::cacheSar); + log.info("SAR 载荷缓存完成,共 {} 条", sarList.size()); } @Override @@ -81,34 +58,25 @@ public class PayloadServiceImpl implements IPayloadService { return payloadMapper.selectPage(param); } - private void cacheSar(SkyeyePayload e) { - if (sarPermanentCache != null) { - sarPermanentCache.put(e.getId().toString(), e); - } - } - @Override public List getSar(Long... sarId) { if (ObjectUtil.isNotEmpty(sarId)) { return Arrays.stream(sarId) - .map(id -> CacheUtil.get(sarPermanentCache, id.toString(), SkyeyePayload.class)) + .map(sarCache::getOne) .filter(Objects::nonNull) .collect(Collectors.toList()); } // 获取所有 SAR(全量从缓存读取) - com.github.benmanes.caffeine.cache.Cache nativeCache = - (com.github.benmanes.caffeine.cache.Cache) sarPermanentCache.getNativeCache(); - - return nativeCache.asMap().values().stream() - .filter(v -> v instanceof SkyeyePayload) - .map(v -> (SkyeyePayload) v) + return sarCache.getAll().stream() + .filter(Objects::nonNull) .collect(Collectors.toList()); + } @Override public SkyeyePayload getOne(Long payloadId) { - return CacheUtil.get(sarPermanentCache, payloadId.toString(), SkyeyePayload.class); + return sarCache.getOne(payloadId); } @Override @@ -131,11 +99,10 @@ public class PayloadServiceImpl implements IPayloadService { .map(JmUavStatusDTO::getSarId) // 正确取 SAR ID .collect(Collectors.toSet()); // 筛选出未在任务中且已连接的雷达 - List payloadList = sarList.stream() + return sarList.stream() .filter(sar -> !jobSarSet.contains(sar.getId()) - && CacheUtil.get(sarShortCache, CacheKey.getSarStatus(sar.getIp()), JmSarStatusDTO.class) != null) + && sarCache.getLatestStatus(sar.getIp()) != null) .collect(Collectors.toList()); - return payloadList; } @Override @@ -159,7 +126,7 @@ public class PayloadServiceImpl implements IPayloadService { } else { payloadMapper.insert(e); } - cacheSar(e); + sarCache.cacheSar(e); return e; } @@ -173,9 +140,7 @@ public class PayloadServiceImpl implements IPayloadService { payloadMapper.update(e); // 若是sar则更新缓存 List list = selectById(PayloadTypeEnum.SAR, e.getId()); - if (ObjectUtil.isNotEmpty(list)) { - cacheSar(list.get(0)); - } + if (ObjectUtil.isNotEmpty(list)) sarCache.cacheSar(list.get(0)); return e; } @@ -219,18 +184,17 @@ public class PayloadServiceImpl implements IPayloadService { } payloadMapper.deleteLogic(id); - Arrays.stream(id).forEach(i -> sarPermanentCache.evict(i.toString())); + Arrays.stream(id).forEach(sarCache::evictSar); } @Override public JmSarStatusDTO getLastStatus(String payloadIp) { - String statusKey = CacheKey.getSarStatus(payloadIp); - JmSarStatusDTO status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class); + JmSarStatusDTO status = sarCache.getLatestStatus(payloadIp); if (status == null) { try { sarControlService.connect(payloadIp); TimeUnit.SECONDS.sleep(1); - status = CacheUtil.get(sarShortCache, statusKey, JmSarStatusDTO.class); + status = sarCache.getLatestStatus(payloadIp); } catch (InterruptedException ex) { throw new ServiceException("获取雷达状态失败:" + ex.getMessage()); } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/UavServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/UavServiceImpl.java index 4d3e2f3..9f353d4 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/UavServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/device/service/impl/UavServiceImpl.java @@ -1,6 +1,7 @@ package com.zhangy.skyeye.device.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.zhangy.skyeye.cache.uav.UavCache; import com.zhangy.skyeye.common.extend.dto.PageDTO; import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.util.ObjectUtil; @@ -10,10 +11,8 @@ import com.zhangy.skyeye.device.service.IUavService; import com.zhangy.skyeye.jm.dto.JmUavStatusDTO; import com.zhangy.skyeye.jm.service.JmJobStatusService; import com.zhangy.skyeye.publics.consts.ExecStatusEnum; -import com.zhangy.skyeye.publics.utils.CacheUtil; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -25,6 +24,7 @@ import java.util.Set; import java.util.stream.Collectors; @Service +@RequiredArgsConstructor public class UavServiceImpl implements IUavService { @Autowired @@ -33,26 +33,11 @@ public class UavServiceImpl implements IUavService { @Autowired private JmJobStatusService sarJobStatusService; - @Autowired - private CacheManager cacheManager; - - private Cache permanentCache; - - @PostConstruct - public void initCache() { - permanentCache = cacheManager.getCache("sar-permanent"); - if (permanentCache == null) { - throw new IllegalStateException("永久缓存 sar-permanent 未找到,请检查 CacheConfig 配置"); - } - } + private final UavCache uavCache; @PostConstruct public void cacheAll() { - selectList(null).forEach(this::cache); - } - - private void cache(SkyeyeUav e) { - permanentCache.put(e.getId().toString(), e); + selectList(null).forEach(uavCache::put); } @Override @@ -74,17 +59,17 @@ public class UavServiceImpl implements IUavService { public List get(Long... id) { if (ObjectUtil.isNotEmpty(id)) { return Arrays.stream(id) - .map(i -> CacheUtil.get(permanentCache, i.toString(), SkyeyeUav.class)) + .map(uavCache::get) .filter(Objects::nonNull) .collect(Collectors.toList()); } // 全量获取 - return CacheUtil.getAll(permanentCache, SkyeyeUav.class); + return uavCache.getAll(); } @Override public SkyeyeUav getOne(Long id) { - SkyeyeUav uav = (SkyeyeUav) permanentCache.get(id.toString()); + SkyeyeUav uav = uavCache.get(id); if (uav != null) { return uav; } @@ -95,7 +80,7 @@ public class UavServiceImpl implements IUavService { throw new ServiceException("无效的无人机ID:" + id); } uav = list.get(0); - cache(uav); + uavCache.put(uav); return uav; } @@ -128,7 +113,7 @@ public class UavServiceImpl implements IUavService { } else { uavMapper.insert(e); } - cache(e); + uavCache.put(e); return e; } @@ -140,7 +125,7 @@ public class UavServiceImpl implements IUavService { // 若是sar则更新缓存 List list = selectById(e.getId()); if (ObjectUtil.isNotEmpty(list)) { - cache(list.get(0)); + uavCache.put(list.get(0)); } return e; } @@ -179,6 +164,6 @@ public class UavServiceImpl implements IUavService { } } uavMapper.deleteLogic(ids); - Arrays.stream(ids).forEach(id -> permanentCache.evict(id.toString())); + Arrays.stream(ids).forEach(uavCache::evict); } } \ No newline at end of file diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/task/JmTaskScheduler.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/task/JmTaskScheduler.java index 880b906..c6148f0 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/task/JmTaskScheduler.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/task/JmTaskScheduler.java @@ -3,9 +3,8 @@ package com.zhangy.skyeye.jm.task; import com.zhangy.skyeye.device.entity.SkyeyePayload; import com.zhangy.skyeye.device.service.IPayloadService; import com.zhangy.skyeye.jm.dto.JmSarStatusDTO; -import com.zhangy.skyeye.publics.consts.CacheKey; import com.zhangy.skyeye.publics.service.ISysLoginService; -import com.zhangy.skyeye.publics.utils.CacheUtil; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.sar.enums.SarControlTypeEnum; import com.zhangy.skyeye.sar.exception.SarConnectException; import com.zhangy.skyeye.sar.service.ISarControlService; @@ -13,13 +12,11 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; import java.util.List; +import java.util.Objects; /** * 任务管理定时任务 @@ -42,16 +39,7 @@ public class JmTaskScheduler { private IPayloadService payloadService; @Autowired - private CacheManager cacheManager; - - private Cache sarPermanentCache; - private Cache sarShortCache; - - @PostConstruct - public void initCaches() { - sarPermanentCache = cacheManager.getCache("sar-permanent"); - sarShortCache = cacheManager.getCache("sar-short"); - } + private SarCache sarCache; // ← 直接用领域缓存 /** * 每1秒向前端推送雷达状态信息,断开连接则所有数据置0返回 @@ -118,11 +106,11 @@ public class JmTaskScheduler { sarList.forEach(sar -> { String ip = sar.getIp(); // 判断是否已连接:看 shortCache 里是否有该 ip 的状态(最近有状态包) - boolean hasStatus = CacheUtil.get(sarShortCache, CacheKey.getSarStatus(ip), JmSarStatusDTO.class) != null; + JmSarStatusDTO status = sarCache.getLatestStatus(ip); // 判断是否已执行连接指令:看 permanentCache 里是否有该 ip 的连接标志 - boolean hasConnectedFlag = sarPermanentCache.get(ip) != null; + boolean hasConnectedFlag = sarCache.isConnected(ip); // 如果缺少状态 或 缺少连接标志,则尝试连接 - if (!hasConnectedFlag || !hasStatus) { + if (!hasConnectedFlag || Objects.isNull(status)) { try { controlInfoService.sendUdp(ip, SarControlTypeEnum.CONNECT); log.info("尝试连接 SAR IP: {}", ip); diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/advice/AuthInterceptor.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/advice/AuthInterceptor.java index 1938809..b4237c2 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/advice/AuthInterceptor.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/advice/AuthInterceptor.java @@ -2,6 +2,7 @@ package com.zhangy.skyeye.publics.advice; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; +import com.zhangy.skyeye.cache.publics.UserTokenCache; import com.zhangy.skyeye.common.extend.anno.IgnoreAuth; import com.zhangy.skyeye.common.extend.exception.AuthException; import com.zhangy.skyeye.common.extend.util.JsonUtil; @@ -10,8 +11,6 @@ import com.zhangy.skyeye.publics.dto.UserDTO; import com.zhangy.skyeye.publics.utils.SecurityUtil; import com.zhangy.skyeye.publics.utils.SpringContextUtil; import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; @@ -38,14 +37,8 @@ import java.util.Objects; @Component public class AuthInterceptor implements AsyncHandlerInterceptor { - private CacheManager getCacheManager() { - return SpringContextUtil.getBean(CacheManager.class); - } - @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - CacheManager cacheManager = getCacheManager(); - Objects.requireNonNull(cacheManager, "CacheManager 未被 Spring 注入,请检查配置"); // 放行 CORS 预检请求(OPTIONS),由后续 Filter 统一处理跨域头 if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) { return true; @@ -65,6 +58,10 @@ public class AuthInterceptor implements AsyncHandlerInterceptor { if (method.isAnnotationPresent(IgnoreAuth.class)) { return true; } + + // 注入 UserTokenCache + UserTokenCache userTokenCache = SpringContextUtil.getBean(UserTokenCache.class); + // 获取并校验 token String token = SecurityUtil.getToken(request); if (StrUtil.isBlank(token)) { @@ -94,13 +91,8 @@ public class AuthInterceptor implements AsyncHandlerInterceptor { if (StrUtil.isBlank(tokenKey)) { throw new AuthException("用户信息异常,请重新登录"); } - Cache cache = cacheManager.getCache("user-tokens"); - // 从 Caffeine 获取 - if (cache == null) { - throw new AuthException("登录状态已失效,请重新登录"); - } // CaffeineCache 中的 token 与当前请求不一致 → 多设备登录,被踢 - String storedToken = cache.get(tokenKey, String.class); + String storedToken = userTokenCache.getToken(tokenKey); if (storedToken == null) { // 显式处理 null,即 token 已失效或被移除 throw new AuthException("登录状态已失效,请重新登录"); @@ -111,7 +103,7 @@ public class AuthInterceptor implements AsyncHandlerInterceptor { throw new AuthException("该账号已在其他设备登录,请重新登录"); } // 续期:Caffeine 的 expireAfterWrite 是从最后写入开始算,所以 put 一下即可续期 - Objects.requireNonNull(cacheManager.getCache("user-tokens")).put(tokenKey, storedToken); + userTokenCache.storeToken(tokenKey, storedToken); return true; } } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/controller/SysUserController.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/controller/SysUserController.java index 5d673c8..cebec03 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/controller/SysUserController.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/controller/SysUserController.java @@ -2,6 +2,7 @@ package com.zhangy.skyeye.publics.controller; import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONUtil; +import com.zhangy.skyeye.cache.publics.UserTokenCache; import com.zhangy.skyeye.common.extend.anno.IgnoreAuth; import com.zhangy.skyeye.common.extend.anno.OperationLog; import com.zhangy.skyeye.common.extend.dto.PageDTO; @@ -10,7 +11,6 @@ import com.zhangy.skyeye.common.extend.util.MessageUtils; import com.zhangy.skyeye.common.extend.util.ObjectUtil; import com.zhangy.skyeye.common.pojo.result.Result; import com.zhangy.skyeye.common.utils.JwtUtil; -import com.zhangy.skyeye.redis.utils.RedisUtil; import com.zhangy.skyeye.publics.consts.CacheKey; import com.zhangy.skyeye.publics.dto.RegisterDTO; import com.zhangy.skyeye.publics.dto.SysUserPwdDTO; @@ -19,7 +19,7 @@ import com.zhangy.skyeye.publics.entity.SysLog; import com.zhangy.skyeye.publics.entity.SysUser; import com.zhangy.skyeye.publics.service.ISysUserService; import com.zhangy.skyeye.publics.utils.SecurityUtil; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -30,125 +30,124 @@ import java.util.List; @Validated @RestController -@AllArgsConstructor +@RequiredArgsConstructor @Slf4j @RequestMapping("/user") public class SysUserController { - @Autowired - private ISysUserService sysUserService; + @Autowired + private ISysUserService sysUserService; - @Autowired - private RedisUtil redisUtil; + private final UserTokenCache userTokenCache; - /** token失效时间(秒) */ - private final int TOKEN_EXPIRE = 60 * 60 * 24; + /** + * 登录接口 + * + * @return + */ + @IgnoreAuth + @OperationLog(value = "登录", type = SysLog.TYPE_USER) + @PostMapping("/login") + public Result login(@RequestBody RegisterDTO registerDTO) { + SysUser user = sysUserService.selectByAccount(registerDTO.getUsername(), registerDTO.getPassword().toLowerCase()); + if (user == null) { + throw ServiceException.noLog(MessageUtils.message("user.login.error")); + } + UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class); + String payload = JSONUtil.toJsonStr(userDTO); // 保持原逻辑 + String accessToken = JwtUtil.sign(payload, user.getPassword()); + String tokenKey = userDTO.getTokenKey(); // skyeye:user:token:id@account + userTokenCache.storeToken(tokenKey, accessToken); + userDTO.setToken(accessToken); + return Result.successData(MessageUtils.message("user.login.success"), userDTO); + } - /** - * 登录接口 - * @return - */ - @IgnoreAuth - @OperationLog(value = "登录", type = SysLog.TYPE_USER) - @PostMapping("/login") - public Result login(@RequestBody RegisterDTO registerDTO) { - SysUser user = sysUserService.selectByAccount(registerDTO.getUsername(), registerDTO.getPassword().toLowerCase()); - if (user == null) { - throw ServiceException.noLog(MessageUtils.message("user.login.error")); - } - UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class); - String key = JSONUtil.toJsonStr(userDTO); - String accessToken = JwtUtil.sign(key, user.getPassword()); - redisUtil.set(userDTO.getTokenKey(), accessToken, TOKEN_EXPIRE); - userDTO.setToken(accessToken); - return Result.successData(MessageUtils.message("user.login.success"), userDTO); - } + /** + * 修改密码 + * + * @param dto + * @return + */ + @PostMapping("/updpwd") + public Object updatePwd(@Valid SysUserPwdDTO dto) { + List list = sysUserService.selectById(dto.getUserId()); + if (ObjectUtil.isEmpty(list)) { + throw ServiceException.noLog(MessageUtils.message("user.updpwd.error")); + } + SysUser user = list.get(0); + if (user.getPassword().equals(dto.getOldPwd())) { + throw ServiceException.noLog(MessageUtils.message("user.updpwd.nochg")); + } + user.setPassword(dto.getNewPwd()); + sysUserService.update(user); + return MessageUtils.message("user.updpwd.success"); + } - /** - * 修改密码 - * @param dto - * @return - */ - @PostMapping("/updpwd") - public Object updatePwd(@Valid SysUserPwdDTO dto) { - List list = sysUserService.selectById(dto.getUserId()); - if (ObjectUtil.isEmpty(list)) { - throw ServiceException.noLog(MessageUtils.message("user.updpwd.error")); - } - SysUser user = list.get(0); - if (user.getPassword().equals(dto.getOldPwd())) { - throw ServiceException.noLog(MessageUtils.message("user.updpwd.nochg")); - } - user.setPassword(dto.getNewPwd()); - sysUserService.update(user); - return MessageUtils.message("user.updpwd.success"); - } - - /** - * 退出接口 - */ - @IgnoreAuth - @OperationLog(value = "退出登录", type = SysLog.TYPE_USER) - @RequestMapping("/logout") - @GetMapping - public Result logout() { - UserDTO userDTO = SecurityUtil.getUser(); - if (userDTO != null) { - String key = CacheKey.getToekn(userDTO.getId(), userDTO.getAccount()); - redisUtil.del(key); - } - return Result.status(true); - } + /** + * 退出接口 + */ + @IgnoreAuth + @OperationLog(value = "退出登录", type = SysLog.TYPE_USER) + @RequestMapping("/logout") + @GetMapping + public Result logout() { + UserDTO userDTO = SecurityUtil.getUser(); + if (userDTO != null) { + String tokenKey = CacheKey.getToken(userDTO.getId(), userDTO.getAccount()); + userTokenCache.evictToken(tokenKey); + } + return Result.status(true); + } - /** - * 分页查询 - */ - @RequestMapping("/page") - public Object selectPage(@Valid @RequestBody PageDTO param) { - return sysUserService.selectPage(param); - } + /** + * 分页查询 + */ + @RequestMapping("/page") + public Object selectPage(@Valid @RequestBody PageDTO param) { + return sysUserService.selectPage(param); + } - /** - * 列表查询 - */ - @RequestMapping("/list") - public Object selectList(@Valid @RequestBody PageDTO param) { - return sysUserService.selectList(param); - } + /** + * 列表查询 + */ + @RequestMapping("/list") + public Object selectList(@Valid @RequestBody PageDTO param) { + return sysUserService.selectList(param); + } - /** - * 查询详情 - */ - @GetMapping("/detail") - public Object selectById(@RequestParam Long... id) { - return sysUserService.selectById(id); - } + /** + * 查询详情 + */ + @GetMapping("/detail") + public Object selectById(@RequestParam Long... id) { + return sysUserService.selectById(id); + } - /** - * 新增 - */ - @PostMapping("/save") - @OperationLog(value = "用户新增", type = SysLog.TYPE_USER) - public Object insert(@RequestBody SysUser e) { - return sysUserService.insert(e); - } + /** + * 新增 + */ + @PostMapping("/save") + @OperationLog(value = "用户新增", type = SysLog.TYPE_USER) + public Object insert(@RequestBody SysUser e) { + return sysUserService.insert(e); + } - /** - * 修改 - */ - @PostMapping("/update") - @OperationLog(value = "用户修改", type = SysLog.TYPE_USER) - public Object update(@RequestBody SysUser e) { - return sysUserService.update(e); - } + /** + * 修改 + */ + @PostMapping("/update") + @OperationLog(value = "用户修改", type = SysLog.TYPE_USER) + public Object update(@RequestBody SysUser e) { + return sysUserService.update(e); + } - /** - * 删除 - */ - @OperationLog(value = "用户删除", type = SysLog.TYPE_USER) - @PostMapping("/remove") - public Object delete(@RequestBody Long... id) { - return sysUserService.delete(id); - } + /** + * 删除 + */ + @OperationLog(value = "用户删除", type = SysLog.TYPE_USER) + @PostMapping("/remove") + public Object delete(@RequestBody Long... id) { + return sysUserService.delete(id); + } } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/service/impl/SysLoginServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/service/impl/SysLoginServiceImpl.java index 5d5b592..b57ba30 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/service/impl/SysLoginServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/service/impl/SysLoginServiceImpl.java @@ -1,44 +1,25 @@ package com.zhangy.skyeye.publics.service.impl; +import com.zhangy.skyeye.cache.publics.UserTokenCache; import com.zhangy.skyeye.publics.service.ISysLoginService; import com.zhangy.skyeye.publics.utils.CacheUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; - @Service +@RequiredArgsConstructor public class SysLoginServiceImpl implements ISysLoginService { - @Autowired - private CacheManager cacheManager; - - private Cache userTokensCache; - - @PostConstruct - public void initCache() { - userTokensCache = cacheManager.getCache("user-tokens"); - if (userTokensCache == null) { - throw new IllegalStateException("user-tokens 缓存未找到,请检查 CacheConfig 配置"); - } - } + private final UserTokenCache userTokenCache; @Override public boolean hasLogged() { - if (userTokensCache == null) { - return false; - } // 获取底层 Caffeine Cache - return CacheUtil.size(userTokensCache) > 0; + return CacheUtil.size(userTokenCache) > 0; } @Override public int countLogged() { - if (userTokensCache == null) { - return 0; - } - return (int) CacheUtil.size(userTokensCache); + return (int) CacheUtil.size(userTokenCache); } } \ No newline at end of file diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/utils/CacheUtil.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/utils/CacheUtil.java index b915979..dac98b3 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/utils/CacheUtil.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/publics/utils/CacheUtil.java @@ -1,5 +1,6 @@ package com.zhangy.skyeye.publics.utils; +import com.zhangy.skyeye.cache.publics.UserTokenCache; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.Cache; import org.springframework.stereotype.Component; @@ -58,5 +59,16 @@ public class CacheUtil { return 0; } } + + public static long size(UserTokenCache tokenCache) { + if (tokenCache == null) return 0; + Object nativeCache = tokenCache.getNativeCache(); + if (nativeCache instanceof com.github.benmanes.caffeine.cache.Cache) { + com.github.benmanes.caffeine.cache.Cache caffeineCache = + (com.github.benmanes.caffeine.cache.Cache) nativeCache; + return caffeineCache.estimatedSize(); + } + return 0; + } } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/context/SarTaskContextProviderImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/context/SarTaskContextProviderImpl.java index e300706..6bd3f2e 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/context/SarTaskContextProviderImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/context/SarTaskContextProviderImpl.java @@ -1,15 +1,11 @@ package com.zhangy.skyeye.sar.context; +import com.zhangy.skyeye.cache.uav.UavCache; import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO; import com.zhangy.skyeye.jm.dto.JmUavStatusDTO; -import com.zhangy.skyeye.publics.utils.CacheUtil; import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; - /** * @PROJECT_NAME: skyeyesystem * @DESCRIPTION: SAR 任务上下文提供者实现(基于 Redis 缓存,避免循环依赖) @@ -22,20 +18,10 @@ import javax.annotation.PostConstruct; @Component public class SarTaskContextProviderImpl implements SarTaskContextProvider { - private final CacheManager cacheManager; + private final UavCache uavCache; - private Cache uavStatusCache; - - public SarTaskContextProviderImpl(CacheManager cacheManager) { - this.cacheManager = cacheManager; - } - - @PostConstruct - public void initCache() { - this.uavStatusCache = cacheManager.getCache("uav-status"); - if (this.uavStatusCache == null) { - log.error("缓存 uav-status 未找到,请检查 CacheConfig 配置"); - } + public SarTaskContextProviderImpl(UavCache uavCache) { + this.uavCache = uavCache; } @Override @@ -44,9 +30,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider { log.warn("获取 uav 状态失败:payloadIp 为空"); return null; } - String key = "sar:context:uav:" + payloadIp; // 保持原 key 格式 - return CacheUtil.get(uavStatusCache, key, JmUavStatusDTO.class); + return uavCache.getShort(key, JmUavStatusDTO.class); } @Override @@ -82,7 +67,7 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider { String key = "sar:context:uav:" + payloadIp; try { - uavStatusCache.put(key, uavStatus); // 自动过期 10 分钟 + uavCache.putShort(key, uavStatus);// 自动过期 10 分钟 log.debug("已更新缓存:key={}, jobExecId={}", key, uavStatus.getJobExecId()); } catch (Exception e) { log.error("更新 SAR uav 状态缓存失败,ip={}", payloadIp, e); @@ -97,7 +82,7 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider { return; } String key = "sar:context:uav:" + payloadIp; - uavStatusCache.evict(key); + uavCache.evictShort(key); log.debug("SAR uav 缓存已清理:{}", key); } } \ No newline at end of file diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlConnectStrategy.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlConnectStrategy.java index 4f5509a..0a386ac 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlConnectStrategy.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlConnectStrategy.java @@ -1,19 +1,15 @@ package com.zhangy.skyeye.sar.control; -import com.zhangy.skyeye.sar.cache.SarCache; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.sar.dto.SarControlDTO; import com.zhangy.skyeye.sar.dto.SarControlParamDTO; import com.zhangy.skyeye.sar.enums.SarControlTypeEnum; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; import java.util.Collections; -import java.util.Date; import java.util.List; /** @@ -26,23 +22,11 @@ import java.util.List; @RequiredArgsConstructor public class SarControlConnectStrategy implements ISarControlStrategy { - private final CacheManager cacheManager; - - // 不加 final,因为在 PostConstruct 中初始化 - private Cache permanentCache; - - @PostConstruct - public void initCache() { - this.permanentCache = cacheManager.getCache("sar-permanent"); - if (this.permanentCache == null) { - log.error("永久缓存 sar-payload-permanent 未找到,请检查 CacheConfig 配置"); - } - } + private final SarCache sarCache; @Override public boolean supports(SarControlParamDTO param) { - SarControlTypeEnum controlType = param.getControlType(); - return controlType == SarControlTypeEnum.CONNECT; + return param.getControlType() == SarControlTypeEnum.CONNECT; } @Override @@ -54,12 +38,9 @@ public class SarControlConnectStrategy implements ISarControlStrategy { @Override public void sendPost(SarControlDTO sar) { - // 建立连接后,记录连接标志(value 可以是 Date、字符串 "connected" 或 boolean true,随你业务) - if (permanentCache != null) { - permanentCache.put(sar.getIp(), new Date()); - } else { - log.warn("无法记录连接标志,永久缓存未初始化"); - } + // 建立连接后,记录连接标志 + sarCache.markConnected(sar.getIp()); + log.info("SAR [{}] 已标记为连接状态", sar.getIp()); } @Override diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlContext.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlContext.java index aab35b8..7018a70 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlContext.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlContext.java @@ -1,9 +1,9 @@ package com.zhangy.skyeye.sar.control; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.common.extend.exception.ServiceException; import com.zhangy.skyeye.common.extend.util.ObjectUtil; import com.zhangy.skyeye.publics.consts.CacheKey; -import com.zhangy.skyeye.publics.utils.CacheUtil; import com.zhangy.skyeye.sar.dto.SarControlDTO; import com.zhangy.skyeye.sar.dto.SarControlPackDTO; import com.zhangy.skyeye.sar.dto.SarControlParamDTO; @@ -14,8 +14,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @@ -23,7 +21,6 @@ import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; -import java.net.SocketTimeoutException; import java.util.List; import java.util.stream.Collectors; @@ -61,7 +58,7 @@ public class SarControlContext { private final int POLLING_INTERVAL = 100; @Autowired - private CacheManager cacheManager; + private SarCache sarCache; /** * 应答超时 @@ -170,17 +167,11 @@ public class SarControlContext { * 轮询等待缓存中的回执(带超时) */ private SarErrorDTO waitForResponse(String cacheKey, String ip, SarControlTypeEnum controlType) { - Cache sarShortCache = cacheManager.getCache("sar-short"); - if (sarShortCache == null) { - log.error("无法获取 sar-short cache"); - return null; - } long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < ANSWER_TIMEOUT) { - SarErrorDTO dto = CacheUtil.get(sarShortCache, cacheKey, SarErrorDTO.class); + SarErrorDTO dto = sarCache.getControlBack(ip); // 如果 SarCache 封装了泛型方法可以直接用 if (dto != null) { - // 读后即删,防止重复消费 - sarShortCache.evict(cacheKey); + sarCache.saveStatus(cacheKey, null); // 清空 shortCache 对应的 key log.info("收到雷达回执 | ip:{} | 控制类型:{} | 状态:{}", ip, controlType, dto.getExecStatus()); return dto; } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlDisconnectStrategy.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlDisconnectStrategy.java index 6f163db..5d1986e 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlDisconnectStrategy.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlDisconnectStrategy.java @@ -1,17 +1,14 @@ package com.zhangy.skyeye.sar.control; -import com.zhangy.skyeye.publics.consts.CacheKey; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.sar.dto.SarControlDTO; import com.zhangy.skyeye.sar.dto.SarControlParamDTO; import com.zhangy.skyeye.sar.enums.SarControlTypeEnum; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; import java.util.Collections; import java.util.List; @@ -24,16 +21,7 @@ import java.util.List; @RequiredArgsConstructor public class SarControlDisconnectStrategy implements ISarControlStrategy { - private final CacheManager cacheManager; - - private Cache sarShortCache; - private Cache sarPermanentCache; - - @PostConstruct - public void init() { - this.sarShortCache = cacheManager.getCache("sar-short"); - this.sarPermanentCache = cacheManager.getCache("sar-permanent"); - } + private final SarCache sarCache; @Override public boolean supports(SarControlParamDTO param) { @@ -50,13 +38,10 @@ public class SarControlDisconnectStrategy implements ISarControlStrategy { @Override public void sendPost(SarControlDTO sar) { - String connectKey = CacheKey.getSarStatus(sar.getIp()); - if (sarShortCache != null) { - sarShortCache.evict(connectKey); - } - if (sarPermanentCache != null) { - sarPermanentCache.evict(sar.getIp()); - } + // 清理短时状态 + sarCache.removeConnection(sar.getIp()); // ← 从 permanentCache 移除连接标志 + sarCache.saveStatus(sar.getIp(), null); // ← 等同于清空 shortCache 中的状态 + log.info("已断开 SAR IP: {},缓存清理完成", sar.getIp()); } @Override diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/listen/SarStatusListener.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/listen/SarStatusListener.java index b099be5..e04fac7 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/listen/SarStatusListener.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/listen/SarStatusListener.java @@ -1,19 +1,18 @@ package com.zhangy.skyeye.sar.listen; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.jm.dto.JmSarStatusDTO; import com.zhangy.skyeye.jm.service.JmJobStatusService; -import com.zhangy.skyeye.publics.consts.CacheKey; import com.zhangy.skyeye.sar.dto.SarErrorDTO; import com.zhangy.skyeye.sar.dto.SarStatusPackDTO; import com.zhangy.skyeye.sar.enums.SarErrorTypeEnum; import com.zhangy.skyeye.sar.task.CircularBufferQueue; import com.zhangy.skyeye.sar.task.DiscardOldestPolicyWithLog; import com.zhangy.skyeye.sar.task.PriorityThreadFactory; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Component; import java.io.IOException; @@ -26,6 +25,7 @@ import java.util.concurrent.TimeUnit; */ @Slf4j @Component +@RequiredArgsConstructor public class SarStatusListener extends SarAbstractListener { /** @@ -50,8 +50,7 @@ public class SarStatusListener extends SarAbstractListener { @Value("${skyeye.sar.udp.status.connect-timeout:15}") private int connectTimeout; - @Autowired - private CacheManager cacheManager; + private final SarCache sarCache; @Autowired private JmJobStatusService sarJobStatusService; @@ -82,19 +81,13 @@ public class SarStatusListener extends SarAbstractListener { log.warn("状态包校验失败,已丢弃。"); return; } - Cache sarShortCache = cacheManager.getCache("sar-short"); - if (sarShortCache == null) { - log.error("sar-short 缓存未找到!请检查 CacheConfig"); - throw new IllegalStateException("sar-short 缓存未找到"); - } // 错误包 → 回执 int errorStatus = packDTO.getErrorPacketStatus(); if (errorStatus == 1) { SarErrorDTO info = packDTO.getDeviceErrorInfo(); if (info.getErrorPacketType() == SarErrorTypeEnum.RESULT) { log.debug("收到回执包:{}", ip); - String controlBackKey = CacheKey.getSarControlBack(ip); - sarShortCache.put(controlBackKey, info); // 写入,自动 2s 过期 + sarCache.saveControlBack(ip, info); // 使用 SarCache,类型安全,自动短期缓存,自动 2s 过期 } } // 状态包 @@ -103,9 +96,8 @@ public class SarStatusListener extends SarAbstractListener { JmSarStatusDTO info = packDTO.getDeviceStatusInfo(); log.debug("sar开机状态:{}", info.getIsBoot()); sarJobStatusService.update(ip, info); - String connectKey = CacheKey.getSarStatus(ip); - sarShortCache.put(connectKey, info); // 写入,自动过期 + sarCache.saveStatus(ip, info); // 使用 SarCache,类型安全,自动短期缓存,自动过期 } - log.debug("----------------------状态包解析完毕"); + log.debug("状态包解析完毕"); } } \ No newline at end of file diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarControlServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarControlServiceImpl.java index 9564f8d..d453c24 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarControlServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarControlServiceImpl.java @@ -5,20 +5,15 @@ import com.zhangy.skyeye.common.extend.util.JsonUtil; import com.zhangy.skyeye.jm.dto.JmJobDTO; import com.zhangy.skyeye.jm.dto.JmSarStatusDTO; import com.zhangy.skyeye.jm.entity.JmJobPayload; -import com.zhangy.skyeye.publics.consts.CacheKey; -import com.zhangy.skyeye.publics.utils.CacheUtil; +import com.zhangy.skyeye.cache.sar.SarCache; import com.zhangy.skyeye.sar.control.SarControlContext; import com.zhangy.skyeye.sar.dto.SarControlParamDTO; import com.zhangy.skyeye.sar.enums.SarControlTypeEnum; import com.zhangy.skyeye.sar.service.ISarControlService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; -import java.util.Objects; import java.util.Optional; import static com.zhangy.skyeye.jm.consts.JmJobModeEnum.CRUISE; @@ -32,21 +27,7 @@ import static com.zhangy.skyeye.jm.consts.JmJobModeEnum.CRUISE; public class SarControlServiceImpl implements ISarControlService { private final SarControlContext udpSendContext; - private final CacheManager cacheManager; // final 字段 - private Cache sarShortCache; - private Cache sarPermanentCache; - - @PostConstruct - public void init() { - this.sarPermanentCache = cacheManager.getCache("sar-permanent"); - this.sarShortCache = cacheManager.getCache("sar-short"); - if (sarShortCache == null) { - log.error("sar-short cache 未找到!"); - } - if (sarPermanentCache == null) { - log.error("sar-permanent cache 未找到!"); - } - } + private final SarCache sarCache; @Override public void sendUdp(JmJobDTO job) { @@ -100,8 +81,10 @@ public class SarControlServiceImpl implements ISarControlService { @Override public void turnOn(String ip) { - if (Objects.nonNull(sarPermanentCache.get(CacheKey.getSarConnected(ip))) && - Objects.nonNull(sarPermanentCache.get(CacheKey.getSarStatus(ip)))) { + // 用领域缓存判断连接和状态 + boolean connected = sarCache.isConnected(ip); + JmSarStatusDTO status = sarCache.getLatestStatus(ip); + if (!connected || status == null) { throw new ServiceException("请先加电并连接sar"); } SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.TURNON); @@ -110,8 +93,10 @@ public class SarControlServiceImpl implements ISarControlService { @Override public void endAll(String ip) { - if (Objects.nonNull(sarPermanentCache.get(CacheKey.getSarConnected(ip))) && - Objects.nonNull(sarPermanentCache.get(CacheKey.getSarStatus(ip)))) { + // 用领域缓存判断连接和状态 + boolean connected = sarCache.isConnected(ip); + JmSarStatusDTO status = sarCache.getLatestStatus(ip); + if (!connected || status == null) { throw new ServiceException("请先加电并连接sar"); } SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.ENDALL); @@ -120,7 +105,6 @@ public class SarControlServiceImpl implements ISarControlService { @Override public JmSarStatusDTO getLatestStatus(String ip) { - String connectKey = CacheKey.getSarStatus(ip); - return CacheUtil.get(sarShortCache, connectKey, JmSarStatusDTO.class); + return sarCache.getLatestStatus(ip); } } diff --git a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarImageServiceImpl.java b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarImageServiceImpl.java index 92803c1..1f55d94 100644 --- a/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarImageServiceImpl.java +++ b/backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarImageServiceImpl.java @@ -1,5 +1,6 @@ package com.zhangy.skyeye.sar.service.impl; +import com.zhangy.skyeye.cache.image.ImageCache; import com.zhangy.skyeye.common.extend.util.MathUtil; import com.zhangy.skyeye.common.extend.util.ObjectUtil; import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO; @@ -10,24 +11,19 @@ import com.zhangy.skyeye.jm.service.JmImageService; import com.zhangy.skyeye.jm.service.JmJobStatusService; import com.zhangy.skyeye.publics.consts.FileTypeEnum; import com.zhangy.skyeye.publics.service.SysFileTypeService; -import com.zhangy.skyeye.publics.utils.CacheUtil; import com.zhangy.skyeye.publics.utils.ImageUtil; import com.zhangy.skyeye.publics.utils.OpenCVUtil; import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO; import com.zhangy.skyeye.sar.service.ISarImageService; import com.zhangy.skyeye.sar.service.SarWsAsyncService; -import com.zhangy.skyeye.sar.util.RadarDisplayOptions; import com.zhangy.skyeye.sar.util.SarImageToneAdjuster; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.opencv.core.Mat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; -import javax.annotation.PostConstruct; import java.io.File; import java.util.Date; import java.util.List; @@ -49,7 +45,7 @@ public class SarImageServiceImpl implements ISarImageService { private SarWsAsyncService sarWsAsyncService; @Autowired - private CacheManager cacheManager; + private ImageCache imageCache; // 图片最大宽度,前端说和电脑有关,4096保险点,一般是4096 8192 16384 @Value("${skyeye.sar.image.max:4096}") @@ -60,16 +56,6 @@ public class SarImageServiceImpl implements ISarImageService { // 当前帧号 private final String CACHE_FIELD_CURR_FRAME_NO = "currFrameNo"; - private Cache joinStateCache; - - @PostConstruct - public void initCache() { - joinStateCache = cacheManager.getCache("image-join-state"); - if (joinStateCache == null) { - log.error("缓存 image-join-state 未找到,请检查 CacheConfig"); - } - } - /** * 获取基准图像信息 * @@ -87,15 +73,15 @@ public class SarImageServiceImpl implements ISarImageService { base = new JmImage(); base.setImageNo(1); // 存起始帧号 - joinStateCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo); + imageCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo); return base; } // 情况2:如果最后一张还能拼图,则直接返回继续拼 JmImage last = imageList.get(imageList.size() - 1); - Integer startFrameNo = CacheUtil.get(joinStateCache, cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, Integer.class); + Integer startFrameNo = imageCache.get(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, Integer.class); int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1); int surplusNum = (IMG_MAX_WITH - currWidth) / singleWidth; // 还可以拼图片数 - Integer baseNo = CacheUtil.get(joinStateCache, cachePrefix + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class); + Integer baseNo = imageCache.get(cachePrefix + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class); if (startFrameNo == null || currWidth < IMG_MAX_WITH || baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) { @@ -105,7 +91,7 @@ public class SarImageServiceImpl implements ISarImageService { // 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件 log.info("当前宽度:{} > {} 重新拼接,当前帧号{}作为首帧", currWidth, IMG_MAX_WITH, frameNo); base = new JmImage(); - joinStateCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo); + imageCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo); base.setImageNo(last.getImageNo() + 1); return base; } @@ -173,7 +159,7 @@ public class SarImageServiceImpl implements ISarImageService { System.out.println("帧:" + frameNo); // 4.保存基准图(同步),用于下次拼接 - Integer baseNo = CacheUtil.get(joinStateCache, "jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class); + Integer baseNo = imageCache.get("jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class); boolean lostImage = baseNo != null && (frameNo - baseNo > 1); // 判断是否丢图 String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, airlineExecId + "-" + base.getImageNo() + "-base.png"); @@ -186,7 +172,7 @@ public class SarImageServiceImpl implements ISarImageService { } //modCoord(imageFrame, lostImage, currAirline); - joinStateCache.put("jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, frameNo); + imageCache.put("jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, frameNo); // ### 亮度调整,用于可靠udp版本图像,固定使用系数0.5 // 拆分多张图片,去掉自适应调整 if (IMG_MAX_WITH > 20000) {