Remove Redis and optimize initialization code

This commit is contained in:
longguancheng 2026-03-06 14:14:10 +08:00
parent 27dfb2d878
commit bff31311de
29 changed files with 1034 additions and 395 deletions

View File

@ -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> T get(String key, Class<T> 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<Object, Object> asMap() {
Object nativeCache = cache.getNativeCache();
if (nativeCache instanceof com.github.benmanes.caffeine.cache.Cache) {
return ((com.github.benmanes.caffeine.cache.Cache<Object, Object>) nativeCache).asMap();
}
return null;
}
protected Object getNativeCache() {
return cache.getNativeCache();
}
}

View File

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

View File

@ -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());

View File

@ -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<String, Object> allCaches() {
return cacheDebugService.allCaches();
}
/**
* 查看缓存key
*/
@GetMapping("/keys/{name}")
public Set<Object> keys(@PathVariable String name) {
return cacheDebugService.keys(name);
}
/**
* 查看某个缓存
*/
@GetMapping("/{name}")
public Map<Object, Object> 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();
}
}

View File

@ -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<String, Object> allCaches() {
Map<String, Object> map = new LinkedHashMap<>();
for (String name : cacheManager.getCacheNames()) {
map.put(name, cache(name));
}
return map;
}
public Map<Object, Object> cache(String name) {
Cache cache = cacheManager.getCache(name);
if (!(cache instanceof CaffeineCache)) {
return Collections.emptyMap();
}
return ((CaffeineCache) cache)
.getNativeCache()
.asMap();
}
public Set<Object> 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<String, Object> stats() {
Map<String, Object> 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<String, Object> 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;
}
}

View File

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

View File

@ -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<Object, Object> nativeCache = caffeineCache.getNativeCache();
Map<Object, Object> map = nativeCache.asMap();
log.info("CACHE [{}] SIZE = {}", name, map.size());
for (Map.Entry<Object, Object> 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 ==========");
}
}

View File

@ -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<String, ImageJoinTypedCache> caches;
private ImageJoinTypedCache getShort() {
return caches.get("imageJoinShortCache");
}
public void put(String key, Object value) {
getShort().putValue(key, value);
}
public <T> T get(String key, Class<T> clazz) {
return getShort().getValue(key, clazz);
}
public void evict(String key) {
getShort().evictValue(key);
}
}

View File

@ -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> T getValue(String key, Class<T> clazz) {
return super.get(key, clazz);
}
public void evictValue(String key) {
super.evict(key);
}
}

View File

@ -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<String, UserTokenTypedCache> 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()
}
}

View File

@ -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()
}
}

View File

@ -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<String, SarTypedCache> 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<SkyeyePayload> getAll() {
Map<Object, Object> 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));
}
}

View File

@ -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> T getValue(String key, Class<T> 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);
}
}

View File

@ -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<String, UavTypedCache> 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<SkyeyeUav> getAll() {
Map<Object, Object> 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> T getShort(String key, Class<T> clazz) {
return getShort().getValue(key, clazz);
}
public void evictShort(String key) {
getShort().evictValue(key);
}
}

View File

@ -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> T getValue(String key, Class<T> 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);
}
}

View File

@ -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<SkyeyePayload> sarList = selectList(payloadQueryDTO);
if (sarPermanentCache != null) {
sarList.forEach(this::cacheSar);
sarList.forEach(sarCache::cacheSar);
log.info("SAR 载荷缓存完成,共 {} 条", sarList.size());
} else {
log.warn("永久缓存不可用,跳过 SAR 预加载");
}
}
@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<SkyeyePayload> 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<Object, Object> nativeCache =
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) 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<SkyeyePayload> 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<SkyeyePayload> 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());
}

View File

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

View File

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

View File

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

View File

@ -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,7 +30,7 @@ import java.util.List;
@Validated
@RestController
@AllArgsConstructor
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/user")
public class SysUserController {
@ -38,14 +38,11 @@ public class SysUserController {
@Autowired
private ISysUserService sysUserService;
@Autowired
private RedisUtil redisUtil;
/** token失效时间 */
private final int TOKEN_EXPIRE = 60 * 60 * 24;
private final UserTokenCache userTokenCache;
/**
* 登录接口
*
* @return
*/
@IgnoreAuth
@ -57,15 +54,17 @@ public class SysUserController {
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);
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);
}
/**
* 修改密码
*
* @param dto
* @return
*/
@ -94,8 +93,8 @@ public class SysUserController {
public Result logout() {
UserDTO userDTO = SecurityUtil.getUser();
if (userDTO != null) {
String key = CacheKey.getToekn(userDTO.getId(), userDTO.getAccount());
redisUtil.del(key);
String tokenKey = CacheKey.getToken(userDTO.getId(), userDTO.getAccount());
userTokenCache.evictToken(tokenKey);
}
return Result.status(true);
}

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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("状态包解析完毕");
}
}

View File

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

View File

@ -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) {