Compare commits
21 Commits
main
...
dev_202601
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46d40b7a3 | ||
|
|
1da7eff80e | ||
|
|
bff31311de | ||
|
|
27dfb2d878 | ||
|
|
29a81bea9b | ||
|
|
90d8311840 | ||
|
|
a3b29f6b90 | ||
|
|
d3ab2f2278 | ||
|
|
8d84e52e44 | ||
|
|
f775c6f173 | ||
|
|
5946d692c5 | ||
|
|
f2f829a2f8 | ||
|
|
6ce25eda79 | ||
|
|
75aeed56be | ||
|
|
c1575d47dd | ||
|
|
f34844f110 | ||
|
|
77e6de4c8b | ||
|
|
af1cf9c662 | ||
|
|
e82b63c8a8 | ||
|
|
b0e3347d6b | ||
|
|
b2f61137a9 |
@ -12,6 +12,14 @@
|
|||||||
<artifactId>skyeye-service-manager</artifactId>
|
<artifactId>skyeye-service-manager</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.fastjson2</groupId>
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
<artifactId>fastjson2</artifactId>
|
<artifactId>fastjson2</artifactId>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.zhangy.skyeye;
|
package com.zhangy.skyeye;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@ -9,8 +10,10 @@ import java.io.File;
|
|||||||
|
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@MapperScan("com.zhangy.skyeye.**.mapper")
|
@MapperScan("com.zhangy.skyeye.**.mapper")
|
||||||
@SpringBootApplication(scanBasePackages = "com.zhangy.**")
|
@SpringBootApplication(scanBasePackages = "com.zhangy")
|
||||||
|
@Slf4j
|
||||||
public class SEApplication {
|
public class SEApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
String projectRoot = System.getProperty("user.dir");
|
String projectRoot = System.getProperty("user.dir");
|
||||||
// String libPath = projectRoot + "/library/logisen/GMTI";
|
// String libPath = projectRoot + "/library/logisen/GMTI";
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package com.zhangy.skyeye.cache.config;
|
||||||
|
|
||||||
|
import com.zhangy.skyeye.cache.image.ImageJoinTypedCache;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================= IMAGE ================= */
|
||||||
|
@Bean("imageJoinShortNativeCache")
|
||||||
|
public Cache imageJoinShort(CacheManager manager) {
|
||||||
|
return require(manager, "image-join-short");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ImageJoinTypedCache imageJoinShortCache(
|
||||||
|
@Qualifier("imageJoinShortNativeCache") Cache cache) {
|
||||||
|
return new ImageJoinTypedCache(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cache require(CacheManager manager, String name) {
|
||||||
|
Cache cache = manager.getCache(name);
|
||||||
|
if (cache == null) {
|
||||||
|
throw new IllegalStateException("未找到缓存: " + name);
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package com.zhangy.skyeye.cache.config;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @PROJECT_NAME: skyeyesystem_online
|
||||||
|
* @DESCRIPTION: 缓存配置
|
||||||
|
* @AUTHOR: GuanCheng Long
|
||||||
|
* @DATE: 2026/1/30 12:51
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
@Slf4j
|
||||||
|
public class CacheConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public CacheManager cacheManager() {
|
||||||
|
log.info("【重要】CacheConfig 被加载,开始创建 CacheManager");
|
||||||
|
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||||
|
cacheManager.setCaffeine(Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(24, TimeUnit.HOURS) // 与原来 TOKEN_EXPIRE 一致(24小时)
|
||||||
|
.maximumSize(10000) // 根据用户量调整,防止 OOM
|
||||||
|
.recordStats() // 可监控命中率
|
||||||
|
// 可加 .removalListener(...) 监听移除事件
|
||||||
|
);
|
||||||
|
// 专门给 token 一个独立的 cache name,便于细粒度管理
|
||||||
|
cacheManager.registerCustomCache("user-tokens",
|
||||||
|
Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(24, TimeUnit.HOURS)
|
||||||
|
.maximumSize(5000) // 预计活跃用户数
|
||||||
|
.build());
|
||||||
|
// 永久缓存:SAR 载荷专用(不设过期)
|
||||||
|
cacheManager.registerCustomCache("sar-permanent", Caffeine.newBuilder()
|
||||||
|
.maximumSize(5000) // 根据 SAR 载荷数量调整
|
||||||
|
.build());
|
||||||
|
// 短时状态/连接(已存在,确认过期时间)
|
||||||
|
cacheManager.registerCustomCache("sar-short", Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(3, TimeUnit.SECONDS) // 建议 3~5 秒,防边界丢失
|
||||||
|
.maximumSize(2000)
|
||||||
|
.build());
|
||||||
|
// 永久缓存:UAV 专用(不设过期)
|
||||||
|
cacheManager.registerCustomCache("uav-permanent", Caffeine.newBuilder()
|
||||||
|
.maximumSize(5000) // 根据无人机数量调整
|
||||||
|
.build());
|
||||||
|
cacheManager.registerCustomCache("uav-short", Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(10, TimeUnit.MINUTES) // 与原 CACHE_EXPIRE_SECONDS 一致
|
||||||
|
.maximumSize(1000) // 按设备/IP 数量预估
|
||||||
|
.recordStats()
|
||||||
|
.build());
|
||||||
|
cacheManager.registerCustomCache("image-join-short", Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(24, TimeUnit.HOURS) // 与原 CACHE_EXPIRE_SECOND 一致
|
||||||
|
.maximumSize(5000) // 航线数量预估
|
||||||
|
.build());
|
||||||
|
log.info("【重要】CacheManager 创建完成,缓存列表:{}", cacheManager.getCacheNames());
|
||||||
|
return cacheManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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 ==========");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package com.zhangy.skyeye.cache.publics;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package com.zhangy.skyeye.device.service.impl;
|
package com.zhangy.skyeye.device.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
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.PageDTO;
|
||||||
import com.zhangy.skyeye.common.extend.dto.QueryDTO;
|
import com.zhangy.skyeye.common.extend.dto.QueryDTO;
|
||||||
import com.zhangy.skyeye.common.extend.enums.EnumUtil;
|
import com.zhangy.skyeye.common.extend.enums.EnumUtil;
|
||||||
@ -15,68 +16,67 @@ import com.zhangy.skyeye.jm.dto.JmJobStatusDTO;
|
|||||||
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
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.consts.ExecStatusEnum;
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import com.zhangy.skyeye.sar.service.ISarControlService;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class PayloadServiceImpl implements IPayloadService {
|
public class PayloadServiceImpl implements IPayloadService {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisTemplate redisTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PayloadMapper payloadMapper;
|
private PayloadMapper payloadMapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmJobStatusService sarJobStatusService;
|
private JmJobStatusService sarJobStatusService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISarControlService sarControlService;
|
private ISarControlService sarControlService;
|
||||||
|
|
||||||
|
private final SarCache sarCache;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void initAndCacheAllSar() {
|
||||||
|
// 开始缓存所有 SAR(如果 permanentCache 为 null,这里会安全跳过)
|
||||||
|
PayloadQueryDTO payloadQueryDTO = new PayloadQueryDTO();
|
||||||
|
payloadQueryDTO.setType(PayloadTypeEnum.SAR.getCode());
|
||||||
|
List<SkyeyePayload> sarList = selectList(payloadQueryDTO);
|
||||||
|
sarList.forEach(sarCache::cacheSar);
|
||||||
|
log.info("SAR 载荷缓存完成,共 {} 条", sarList.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<SkyeyePayload> selectPage(PageDTO param) {
|
public IPage<SkyeyePayload> selectPage(PageDTO param) {
|
||||||
return payloadMapper.selectPage(param);
|
return payloadMapper.selectPage(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void cacheAllSar() {
|
|
||||||
PayloadQueryDTO payloadQueryDTO = new PayloadQueryDTO();
|
|
||||||
payloadQueryDTO.setType(PayloadTypeEnum.SAR.getCode());
|
|
||||||
List<SkyeyePayload> sarList = selectList(payloadQueryDTO);
|
|
||||||
sarList.forEach(this::cacheSar);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cacheSar(SkyeyePayload e) {
|
|
||||||
redisTemplate.opsForHash().put(CacheKey.DEVICE_SAR, e.getId().toString(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SkyeyePayload> getSar(Long... sarId) {
|
public List<SkyeyePayload> getSar(Long... sarId) {
|
||||||
if (ObjectUtil.isNotEmpty(sarId)) {
|
if (ObjectUtil.isNotEmpty(sarId)) {
|
||||||
return redisTemplate.opsForHash().multiGet(CacheKey.DEVICE_SAR, Arrays.asList(sarId));
|
return Arrays.stream(sarId)
|
||||||
|
.map(sarCache::getOne)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
return redisTemplate.opsForHash().values(CacheKey.DEVICE_SAR);
|
|
||||||
|
// 获取所有 SAR(全量从缓存读取)
|
||||||
|
return sarCache.getAll().stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SkyeyePayload getOne(Long payloadId) {
|
public SkyeyePayload getOne(Long payloadId) {
|
||||||
SkyeyePayload p = (SkyeyePayload) redisTemplate.opsForHash().get(CacheKey.DEVICE_SAR, payloadId.toString());
|
return sarCache.getOne(payloadId);
|
||||||
// 若有其它种类载荷,则判空,继续查询
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -84,10 +84,6 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
return payloadMapper.selectList(param);
|
return payloadMapper.selectList(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SkyeyePayload> getEnableList() {
|
public List<SkyeyePayload> getEnableList() {
|
||||||
// 从缓存查询所有sar
|
// 从缓存查询所有sar
|
||||||
@ -98,12 +94,14 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
// 从内存查询所有任务中的雷达
|
// 从内存查询所有任务中的雷达
|
||||||
Collection<JmJobStatusDTO> statusVos = sarJobStatusService.getAll();
|
Collection<JmJobStatusDTO> statusVos = sarJobStatusService.getAll();
|
||||||
Set<Long> jobSarSet = statusVos.stream()
|
Set<Long> jobSarSet = statusVos.stream()
|
||||||
.flatMap(job -> job.getUavMap().values().stream().filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
|
.flatMap(job -> job.getUavMap().values().stream()
|
||||||
.map(JmUavStatusDTO::getSarId)
|
.filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
|
||||||
|
.map(JmUavStatusDTO::getSarId) // 正确取 SAR ID
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
// 筛选出未在任务中且已连接的雷达
|
// 筛选出未在任务中且已连接的雷达
|
||||||
return sarList.stream()
|
return sarList.stream()
|
||||||
.filter(sar -> !jobSarSet.contains(sar.getId()) && redisUtil.hasKey(CacheKey.getSarConnect(sar.getIp())))
|
.filter(sar -> !jobSarSet.contains(sar.getId())
|
||||||
|
&& sarCache.getLatestStatus(sar.getIp()) != null)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +126,7 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
} else {
|
} else {
|
||||||
payloadMapper.insert(e);
|
payloadMapper.insert(e);
|
||||||
}
|
}
|
||||||
cacheSar(e);
|
sarCache.cacheSar(e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,9 +140,7 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
payloadMapper.update(e);
|
payloadMapper.update(e);
|
||||||
// 若是sar则更新缓存
|
// 若是sar则更新缓存
|
||||||
List<SkyeyePayload> list = selectById(PayloadTypeEnum.SAR, e.getId());
|
List<SkyeyePayload> list = selectById(PayloadTypeEnum.SAR, e.getId());
|
||||||
if (ObjectUtil.isNotEmpty(list)) {
|
if (ObjectUtil.isNotEmpty(list)) sarCache.cacheSar(list.get(0));
|
||||||
cacheSar(list.get(0));
|
|
||||||
}
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,38 +167,42 @@ public class PayloadServiceImpl implements IPayloadService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void delete(Long... id) {
|
public void delete(Long... id) {
|
||||||
// 查询任务中的sar
|
|
||||||
List<Long> inJobSarIds = sarJobStatusService.getAll()
|
List<Long> inJobSarIds = sarJobStatusService.getAll()
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(vo -> vo.getUavMap().values().stream())
|
.flatMap(vo -> vo.getUavMap().values().stream())
|
||||||
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
|
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
|
||||||
.map(JmUavStatusDTO::getSarId)
|
.map(JmUavStatusDTO::getSarId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// 任务中的不可删除
|
|
||||||
for (Long sarId : id) {
|
for (Long sarId : id) {
|
||||||
if (inJobSarIds.contains(sarId)) {
|
if (inJobSarIds.contains(sarId)) {
|
||||||
throw ServiceException.noLog("[" + getSar(sarId).get(0).getName() + "]在执行任务,不可删除");
|
List<SkyeyePayload> sar = getSar(sarId);
|
||||||
|
if (!sar.isEmpty()) {
|
||||||
|
throw ServiceException.noLog("[" + sar.get(0).getName() + "]在执行任务,不可删除");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadMapper.deleteLogic(id);
|
payloadMapper.deleteLogic(id);
|
||||||
redisTemplate.opsForHash().delete(CacheKey.DEVICE_SAR, id);
|
Arrays.stream(id).forEach(sarCache::evictSar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JmSarStatusDTO getLastStatus(String payloadIp) {
|
public JmSarStatusDTO getLastStatus(String payloadIp) {
|
||||||
String statusKey = CacheKey.getSarConnect(payloadIp);
|
JmSarStatusDTO status = sarCache.getLatestStatus(payloadIp);
|
||||||
if (!redisUtil.hasKey(statusKey)) {
|
if (status == null) {
|
||||||
try {
|
try {
|
||||||
sarControlService.connect(payloadIp);
|
sarControlService.connect(payloadIp);
|
||||||
TimeUnit.SECONDS.sleep(1);
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
status = sarCache.getLatestStatus(payloadIp);
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
throw new ServiceException("获取雷达状态失败:" + ex.getMessage());
|
throw new ServiceException("获取雷达状态失败:" + ex.getMessage());
|
||||||
}
|
}
|
||||||
if (!redisUtil.hasKey(statusKey)) {
|
if (status == null) {
|
||||||
throw ServiceException.noLog("无法连接到SAR载荷[" + payloadIp + "]!");
|
throw ServiceException.noLog("无法连接到SAR载荷[" + payloadIp + "]!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (JmSarStatusDTO) redisUtil.get(statusKey);
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.zhangy.skyeye.device.service.impl;
|
package com.zhangy.skyeye.device.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
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.dto.PageDTO;
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
||||||
@ -9,77 +10,77 @@ import com.zhangy.skyeye.device.mapper.UavMapper;
|
|||||||
import com.zhangy.skyeye.device.service.IUavService;
|
import com.zhangy.skyeye.device.service.IUavService;
|
||||||
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
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.consts.ExecStatusEnum;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class UavServiceImpl implements IUavService {
|
public class UavServiceImpl implements IUavService {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UavMapper uavMapper;
|
private UavMapper uavMapper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmJobStatusService sarJobStatusService;
|
private JmJobStatusService sarJobStatusService;
|
||||||
|
|
||||||
@Autowired
|
private final UavCache uavCache;
|
||||||
private RedisTemplate redisTemplate;
|
|
||||||
|
@PostConstruct
|
||||||
|
public void cacheAll() {
|
||||||
|
selectList(null).forEach(uavCache::put);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<SkyeyeUav> selectPage(PageDTO param) {
|
public IPage<SkyeyeUav> selectPage(PageDTO param) {
|
||||||
return uavMapper.selectPage(param);
|
return uavMapper.selectPage(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SkyeyeUav> selectList(PageDTO param) {
|
public List<SkyeyeUav> selectList(PageDTO param) {
|
||||||
return uavMapper.selectList(param);
|
return uavMapper.selectList(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SkyeyeUav> selectById(Long... id) {
|
public List<SkyeyeUav> selectById(Long... id) {
|
||||||
return uavMapper.selectById(id);
|
return uavMapper.selectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void cacheAll() {
|
|
||||||
selectList(null).forEach(e -> {
|
|
||||||
cache(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cache(SkyeyeUav e) {
|
|
||||||
redisTemplate.opsForHash().put(CacheKey.DEVICE_UAV, e.getId(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SkyeyeUav> get(Long... id) {
|
public List<SkyeyeUav> get(Long... id) {
|
||||||
if (ObjectUtil.isNotEmpty(id)) {
|
if (ObjectUtil.isNotEmpty(id)) {
|
||||||
return redisTemplate.opsForHash().multiGet(CacheKey.DEVICE_UAV, Arrays.asList(id));
|
return Arrays.stream(id)
|
||||||
|
.map(uavCache::get)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
return redisTemplate.opsForHash().values(CacheKey.DEVICE_UAV);
|
// 全量获取
|
||||||
|
return uavCache.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SkyeyeUav getOne(Long id) {
|
public SkyeyeUav getOne(Long id) {
|
||||||
List<SkyeyeUav> list = get(id);
|
SkyeyeUav uav = uavCache.get(id);
|
||||||
if (ObjectUtil.isNotEmpty(list)) {
|
if (uav != null) {
|
||||||
return list.get(0);
|
return uav;
|
||||||
}
|
}
|
||||||
list = uavMapper.selectById(id);
|
|
||||||
|
// 缓存 miss,从 DB 查并回填
|
||||||
|
List<SkyeyeUav> list = uavMapper.selectById(id);
|
||||||
if (ObjectUtil.isEmpty(list)) {
|
if (ObjectUtil.isEmpty(list)) {
|
||||||
throw new ServiceException("无效的无人机ID:" + id);
|
throw new ServiceException("无效的无人机ID:" + id);
|
||||||
}
|
}
|
||||||
SkyeyeUav uav = list.get(0);
|
uav = list.get(0);
|
||||||
cache(uav);
|
uavCache.put(uav);
|
||||||
return uav;
|
return uav;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +93,9 @@ public class UavServiceImpl implements IUavService {
|
|||||||
}
|
}
|
||||||
// 从内存查询所有任务中的无人机(暂时用SAR状态判断)
|
// 从内存查询所有任务中的无人机(暂时用SAR状态判断)
|
||||||
Set<Long> jobUavSet = sarJobStatusService.getAll().stream()
|
Set<Long> jobUavSet = sarJobStatusService.getAll().stream()
|
||||||
.flatMap(job -> job.getUavMap().values().stream().filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
|
.flatMap(job -> job.getUavMap().values().stream()
|
||||||
.map(JmUavStatusDTO::getSarId)
|
.filter(uav -> uav.getSarStatus() != ExecStatusEnum.OVER))
|
||||||
|
.map(JmUavStatusDTO::getUavId) // 注意:这里是 uavId,不是 sarId
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
// 筛选出未在任务中的无人机
|
// 筛选出未在任务中的无人机
|
||||||
return uavList.stream()
|
return uavList.stream()
|
||||||
@ -110,11 +112,11 @@ public class UavServiceImpl implements IUavService {
|
|||||||
uavMapper.update(e);
|
uavMapper.update(e);
|
||||||
} else {
|
} else {
|
||||||
uavMapper.insert(e);
|
uavMapper.insert(e);
|
||||||
cache(e);
|
|
||||||
}
|
}
|
||||||
|
uavCache.put(e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public SkyeyeUav update(SkyeyeUav e) {
|
public SkyeyeUav update(SkyeyeUav e) {
|
||||||
@ -123,13 +125,14 @@ public class UavServiceImpl implements IUavService {
|
|||||||
// 若是sar则更新缓存
|
// 若是sar则更新缓存
|
||||||
List<SkyeyeUav> list = selectById(e.getId());
|
List<SkyeyeUav> list = selectById(e.getId());
|
||||||
if (ObjectUtil.isNotEmpty(list)) {
|
if (ObjectUtil.isNotEmpty(list)) {
|
||||||
cache(list.get(0));
|
uavCache.put(list.get(0));
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存前校验
|
* 保存前校验
|
||||||
|
*
|
||||||
* @param e
|
* @param e
|
||||||
* @return 返回已逻辑删除的载荷
|
* @return 返回已逻辑删除的载荷
|
||||||
*/
|
*/
|
||||||
@ -144,16 +147,15 @@ public class UavServiceImpl implements IUavService {
|
|||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void delete(Long... ids) {
|
public void delete(Long... ids) {
|
||||||
// 查询任务中的设备
|
|
||||||
List<Long> inJobUavIds = sarJobStatusService.getAll()
|
List<Long> inJobUavIds = sarJobStatusService.getAll()
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(vo -> vo.getUavMap().values().stream())
|
.flatMap(vo -> vo.getUavMap().values().stream())
|
||||||
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
|
.filter(vo -> vo.getSarStatus() != ExecStatusEnum.OVER)
|
||||||
.map(vo -> vo.getUavId())
|
.map(JmUavStatusDTO::getUavId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
// 任务中的不可删除
|
// 任务中的不可删除
|
||||||
for (Long id : ids) {
|
for (Long id : ids) {
|
||||||
@ -162,6 +164,6 @@ public class UavServiceImpl implements IUavService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
uavMapper.deleteLogic(ids);
|
uavMapper.deleteLogic(ids);
|
||||||
redisTemplate.opsForHash().delete(CacheKey.DEVICE_UAV, ids);
|
Arrays.stream(ids).forEach(uavCache::evict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,20 +11,14 @@ import com.zhangy.skyeye.jm.dto.JmJobImageDTO;
|
|||||||
import com.zhangy.skyeye.jm.entity.JmImage;
|
import com.zhangy.skyeye.jm.entity.JmImage;
|
||||||
import com.zhangy.skyeye.jm.service.JmImageService;
|
import com.zhangy.skyeye.jm.service.JmImageService;
|
||||||
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
|
import com.zhangy.skyeye.publics.consts.FileTypeEnum;
|
||||||
|
import com.zhangy.skyeye.publics.utils.LocalLockUtil;
|
||||||
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
||||||
import org.opencv.core.Core;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.io.File;
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
|
import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
|
||||||
@ -33,12 +27,12 @@ import static com.zhangy.skyeye.publics.consts.CacheKey.SAR_IMAGE_UPLOAD_LOCK;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/sar/image")
|
@RequestMapping("/sar/image")
|
||||||
public class JmImageController {
|
public class JmImageController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmImageService sarImageService;
|
private JmImageService sarImageService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisTemplate redisTemplate;
|
private LocalLockUtil localLockUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询
|
* 分页查询
|
||||||
@ -47,16 +41,16 @@ public class JmImageController {
|
|||||||
public Object selectPage(@Valid @RequestBody JmImagePageDTO param) {
|
public Object selectPage(@Valid @RequestBody JmImagePageDTO param) {
|
||||||
return sarImageService.selectPage(param);
|
return sarImageService.selectPage(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按任务查询低精度图像
|
* 按任务查询低精度图像
|
||||||
*/
|
*/
|
||||||
@GetMapping("/queryByJob")
|
@GetMapping("/queryByJob")
|
||||||
public Object selectList(@RequestParam Long jobId) {
|
public Object selectList(@RequestParam Long jobId) {
|
||||||
return sarImageService.selectByJob(FileTypeEnum.SAR_IMAGE_LOW, jobId);
|
return sarImageService.selectByJob(FileTypeEnum.SAR_IMAGE_LOW, jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询详情
|
* 查询详情
|
||||||
*/
|
*/
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
@ -80,31 +74,19 @@ public class JmImageController {
|
|||||||
@PostMapping("/addHigh")
|
@PostMapping("/addHigh")
|
||||||
public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) {
|
public Object addHighImage(@Valid @RequestBody JmJobImageDTO dto) {
|
||||||
String lockKey = SAR_IMAGE_UPLOAD_LOCK;
|
String lockKey = SAR_IMAGE_UPLOAD_LOCK;
|
||||||
String requestId = UUID.randomUUID().toString(); // 唯一标识当前请求
|
// 尝试 60 秒获取锁(比原来 1 分钟更灵活)
|
||||||
|
boolean locked = localLockUtil.tryLock(lockKey, 60);
|
||||||
|
if (!locked) {
|
||||||
|
throw ServiceException.noLog("正在上传其它图像,请稍后重试!");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// 原子操作获取锁(设置过期时间30秒)
|
// 执行高精度图像添加逻辑
|
||||||
Boolean locked = redisTemplate.opsForValue().setIfAbsent(
|
|
||||||
lockKey,
|
|
||||||
requestId,
|
|
||||||
1,
|
|
||||||
TimeUnit.MINUTES
|
|
||||||
);
|
|
||||||
if (Boolean.FALSE.equals(locked)) {
|
|
||||||
throw ServiceException.noLog(MessageUtils.message("sar.image.add_highres.eagain"));
|
|
||||||
}
|
|
||||||
sarImageService.addHighImage(dto);
|
sarImageService.addHighImage(dto);
|
||||||
} finally {
|
} finally {
|
||||||
// 仅删除自己的锁(Lua脚本保证原子性)
|
// 释放锁
|
||||||
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then "
|
localLockUtil.unlock(lockKey);
|
||||||
+ "return redis.call('del', KEYS[1]) "
|
|
||||||
+ "else return 0 end";
|
|
||||||
redisTemplate.execute(
|
|
||||||
new DefaultRedisScript<>(luaScript, Long.class),
|
|
||||||
Collections.singletonList(lockKey),
|
|
||||||
requestId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return MessageUtils.message("sar.image.add_highres.success");
|
return "操作完成";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +120,7 @@ public class JmImageController {
|
|||||||
.map(e -> BeanUtil.copyProperties(e, JmImageKtyDTO.class))
|
.map(e -> BeanUtil.copyProperties(e, JmImageKtyDTO.class))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
//System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
//System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
OpenCVUtil.loadNativeDylib();
|
OpenCVUtil.loadNativeDylib();
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.zhangy.skyeye.jm.service.impl;
|
package com.zhangy.skyeye.jm.service.impl;
|
||||||
|
|
||||||
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.MessageUtils;
|
import com.zhangy.skyeye.common.extend.util.MessageUtils;
|
||||||
import com.zhangy.skyeye.jm.dto.*;
|
import com.zhangy.skyeye.jm.dto.*;
|
||||||
@ -14,7 +15,6 @@ import com.zhangy.skyeye.publics.consts.CacheKey;
|
|||||||
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
import com.zhangy.skyeye.publics.consts.ExecStatusEnum;
|
||||||
import com.zhangy.skyeye.publics.consts.WebSocketKey;
|
import com.zhangy.skyeye.publics.consts.WebSocketKey;
|
||||||
import com.zhangy.skyeye.publics.utils.CoordUtil;
|
import com.zhangy.skyeye.publics.utils.CoordUtil;
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import com.zhangy.skyeye.sar.listen.SarImageUdpProcessor;
|
import com.zhangy.skyeye.sar.listen.SarImageUdpProcessor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -46,7 +46,7 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
|
|||||||
private SarImageUdpProcessor imageProcessService;
|
private SarImageUdpProcessor imageProcessService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private SarCache sarCache;
|
||||||
|
|
||||||
// @Autowired
|
// @Autowired
|
||||||
// private ISmpSubscriptService subscriptService;
|
// private ISmpSubscriptService subscriptService;
|
||||||
@ -183,10 +183,9 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
|
|||||||
}
|
}
|
||||||
for (JmJobPayload payload : newUav.getPayloadList()) {
|
for (JmJobPayload payload : newUav.getPayloadList()) {
|
||||||
// 校验雷达连接状态
|
// 校验雷达连接状态
|
||||||
if (!redisUtil.hasKey(CacheKey.getSarConnect(payload.getIp()))) {
|
if (!sarCache.isConnected(payload.getIp())) {
|
||||||
throw ServiceException.warnLog(MessageUtils.message("device.sar.offline", payload.getPayloadName()));
|
throw ServiceException.warnLog(MessageUtils.message("device.sar.offline", payload.getPayloadName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runningPayloadIds.contains(payload.getPayloadId())) {
|
if (runningPayloadIds.contains(payload.getPayloadId())) {
|
||||||
throw ServiceException.warnLog(MessageUtils.message("sar.control.turnon.esarinexec"));
|
throw ServiceException.warnLog(MessageUtils.message("sar.control.turnon.esarinexec"));
|
||||||
}
|
}
|
||||||
@ -295,11 +294,11 @@ public class JmJobStatusServiceImpl implements JmJobStatusService {
|
|||||||
}
|
}
|
||||||
endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null);
|
endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null);
|
||||||
} else if (isBoot == 1 && aStatus == ExecStatusEnum.PROCESSING && overButSending) {
|
} else if (isBoot == 1 && aStatus == ExecStatusEnum.PROCESSING && overButSending) {
|
||||||
// 前一航线的图片没传完,就开始了新航线
|
// 前一航线的图片没传完,就开始了新航线
|
||||||
endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null);
|
endAirline(jobId, jobVo, uavVo, airlineVo, wsVo, nextAirline == null);
|
||||||
if (nextAirline != null) {
|
if (nextAirline != null) {
|
||||||
startAirline(nextAirline, wsVo);
|
startAirline(nextAirline, wsVo);
|
||||||
}
|
}
|
||||||
} // else {
|
} // else {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -1,27 +1,22 @@
|
|||||||
package com.zhangy.skyeye.jm.task;
|
package com.zhangy.skyeye.jm.task;
|
||||||
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import com.zhangy.skyeye.device.entity.SkyeyePayload;
|
import com.zhangy.skyeye.device.entity.SkyeyePayload;
|
||||||
import com.zhangy.skyeye.device.service.IPayloadService;
|
import com.zhangy.skyeye.device.service.IPayloadService;
|
||||||
import com.zhangy.skyeye.jm.dto.JmJobStatusDTO;
|
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.service.ISysLoginService;
|
||||||
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
|
||||||
import com.zhangy.skyeye.sar.exception.SarConnectException;
|
import com.zhangy.skyeye.sar.exception.SarConnectException;
|
||||||
import com.zhangy.skyeye.sar.service.ISarControlService;
|
import com.zhangy.skyeye.sar.service.ISarControlService;
|
||||||
// import com.zhangy.skyeye.smp.dto.SmpUavStatusWsDTO;
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务管理定时任务
|
* 任务管理定时任务
|
||||||
@ -34,18 +29,6 @@ public class JmTaskScheduler {
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean isDebug;
|
private boolean isDebug;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JmJobStatusService jobStatusService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private SimpMessagingTemplate simpMessagingTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
RedisTemplate<String, Object> redisTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysLoginService loginService;
|
private ISysLoginService loginService;
|
||||||
|
|
||||||
@ -56,7 +39,7 @@ public class JmTaskScheduler {
|
|||||||
private IPayloadService payloadService;
|
private IPayloadService payloadService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISarControlService sarControlService;
|
private SarCache sarCache; // ← 直接用领域缓存
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每1秒向前端推送雷达状态信息,断开连接则所有数据置0返回
|
* 每1秒向前端推送雷达状态信息,断开连接则所有数据置0返回
|
||||||
@ -108,28 +91,31 @@ public class JmTaskScheduler {
|
|||||||
/**
|
/**
|
||||||
* 每10秒进行载荷连接判断:
|
* 每10秒进行载荷连接判断:
|
||||||
* 1. 如果有用户登录:
|
* 1. 如果有用户登录:
|
||||||
* 1)未执行连接指令,进行连接
|
* 1)未执行连接指令,进行连接
|
||||||
* 2)已经执行连接指令,但没有获取到sar状态信息,进行重连
|
* 2)已经执行连接指令,但没有获取到sar状态信息,进行重连
|
||||||
* 2. 没有用户在线:
|
* 2. 没有用户在线:
|
||||||
* 无。因为断开连接会先结束任务,不能断开连接。等硬件断电
|
* 无。因为断开连接会先结束任务,不能断开连接。等硬件断电
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedRate = 10000)
|
@Scheduled(fixedRate = 10000)
|
||||||
public void connectSar() {
|
public void connectSar() {
|
||||||
if (!loginService.hasLogged() || isDebug) { // 断开
|
if (!loginService.hasLogged() || isDebug) {
|
||||||
/*Map<Object, Object> map = redisUtil.hmget(CacheKey.SAR_CONNECTED);
|
// 断开逻辑:如果需要,可遍历 permanentCache 或其他方式获取 ip 列表
|
||||||
if (map != null && map.size() > 0) {
|
// 但当前注释掉了,保持原样或删除
|
||||||
map.keySet().forEach(ip -> controlInfoService.sendUdp((String) ip, SarControlTypeEnum.DISCONNECT));
|
} else {
|
||||||
}*/
|
|
||||||
} else { // 连接
|
|
||||||
List<SkyeyePayload> sarList = payloadService.getSar(null);
|
List<SkyeyePayload> sarList = payloadService.getSar(null);
|
||||||
sarList.forEach(sar -> {
|
sarList.forEach(sar -> {
|
||||||
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, sar.getIp()) ||
|
String ip = sar.getIp();
|
||||||
!redisUtil.hasKey(CacheKey.getSarConnect(sar.getIp()))) {
|
// 判断是否已连接:看 shortCache 里是否有该 ip 的状态(最近有状态包)
|
||||||
|
JmSarStatusDTO status = sarCache.getLatestStatus(ip);
|
||||||
|
// 判断是否已执行连接指令:看 permanentCache 里是否有该 ip 的连接标志
|
||||||
|
boolean hasConnectedFlag = sarCache.isConnected(ip);
|
||||||
|
// 如果缺少状态 或 缺少连接标志,则尝试连接
|
||||||
|
if (!hasConnectedFlag || Objects.isNull(status)) {
|
||||||
try {
|
try {
|
||||||
controlInfoService.sendUdp(sar.getIp(), SarControlTypeEnum.CONNECT);
|
controlInfoService.sendUdp(ip, SarControlTypeEnum.CONNECT);
|
||||||
|
log.info("尝试连接 SAR IP: {}", ip);
|
||||||
} catch (SarConnectException ex) {
|
} catch (SarConnectException ex) {
|
||||||
// sar可能没通电,连接失败不做处理
|
log.warn("连接雷达[{}]失败:{}", ip, ex.getMessage());
|
||||||
log.warn("连接雷达[" + sar.getIp() + "]失败:" + ex.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
package com.zhangy.skyeye.publics.advice;
|
package com.zhangy.skyeye.publics.advice;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
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.anno.IgnoreAuth;
|
||||||
import com.zhangy.skyeye.common.extend.exception.AuthException;
|
import com.zhangy.skyeye.common.extend.exception.AuthException;
|
||||||
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
||||||
import com.zhangy.skyeye.common.utils.JwtUtil;
|
import com.zhangy.skyeye.common.utils.JwtUtil;
|
||||||
import com.zhangy.skyeye.common.utils.SpringUtil;
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import com.zhangy.skyeye.publics.utils.SecurityUtil;
|
|
||||||
import com.zhangy.skyeye.publics.dto.UserDTO;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.method.HandlerMethod;
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||||
@ -32,10 +34,9 @@ import java.util.Objects;
|
|||||||
* 6. 续期 Redis 中的 token 有效期
|
* 6. 续期 Redis 中的 token 有效期
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@Component
|
||||||
public class AuthInterceptor implements AsyncHandlerInterceptor {
|
public class AuthInterceptor implements AsyncHandlerInterceptor {
|
||||||
|
|
||||||
private final RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
// 放行 CORS 预检请求(OPTIONS),由后续 Filter 统一处理跨域头
|
// 放行 CORS 预检请求(OPTIONS),由后续 Filter 统一处理跨域头
|
||||||
@ -57,9 +58,13 @@ public class AuthInterceptor implements AsyncHandlerInterceptor {
|
|||||||
if (method.isAnnotationPresent(IgnoreAuth.class)) {
|
if (method.isAnnotationPresent(IgnoreAuth.class)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注入 UserTokenCache
|
||||||
|
UserTokenCache userTokenCache = SpringContextUtil.getBean(UserTokenCache.class);
|
||||||
|
|
||||||
// 获取并校验 token
|
// 获取并校验 token
|
||||||
String token = SecurityUtil.getToken(request);
|
String token = SecurityUtil.getToken(request);
|
||||||
if (token == null || token.trim().isEmpty()) {
|
if (StrUtil.isBlank(token)) {
|
||||||
throw new AuthException("请求未授权:缺少 token");
|
throw new AuthException("请求未授权:缺少 token");
|
||||||
}
|
}
|
||||||
// 解析 token 中的用户名信息
|
// 解析 token 中的用户名信息
|
||||||
@ -81,25 +86,24 @@ public class AuthInterceptor implements AsyncHandlerInterceptor {
|
|||||||
log.error("token 中的用户信息解析失败: {}", usernamePayload, e);
|
log.error("token 中的用户信息解析失败: {}", usernamePayload, e);
|
||||||
throw new AuthException("token 格式错误,请重新登录");
|
throw new AuthException("token 格式错误,请重新登录");
|
||||||
}
|
}
|
||||||
// 获取该用户在 Redis 中存储的 token
|
// 获取该用户在 CaffeineCache 中存储的 token
|
||||||
String redisKey = userDTO.getTokenKey();
|
String tokenKey = userDTO.getTokenKey();
|
||||||
if (redisKey == null || redisKey.trim().isEmpty()) {
|
if (StrUtil.isBlank(tokenKey)) {
|
||||||
throw new AuthException("用户信息异常,请重新登录");
|
throw new AuthException("用户信息异常,请重新登录");
|
||||||
}
|
}
|
||||||
Object storedToken = redisUtil.get(redisKey);
|
// CaffeineCache 中的 token 与当前请求不一致 → 多设备登录,被踢
|
||||||
// Redis 中不存在 → 登录已失效
|
String storedToken = userTokenCache.getToken(tokenKey);
|
||||||
if (storedToken == null) {
|
if (storedToken == null) {
|
||||||
|
// 显式处理 null,即 token 已失效或被移除
|
||||||
throw new AuthException("登录状态已失效,请重新登录");
|
throw new AuthException("登录状态已失效,请重新登录");
|
||||||
}
|
}
|
||||||
// Redis 中的 token 与当前请求不一致 → 多设备登录,被踢
|
if (!Objects.equals(token, storedToken)) {
|
||||||
if (!Objects.equals(token, storedToken.toString())) {
|
log.warn("检测到多端登录冲突,用户: {}, 当前token: {}, storedToken: {}",
|
||||||
log.warn("检测到多端登录冲突,用户: {}, 当前token: {}, redisToken: {}",
|
usernamePayload, token, storedToken);
|
||||||
usernamePayload, token.substring(0, 10) + "...",
|
|
||||||
storedToken.toString().substring(0, 10) + "...");
|
|
||||||
throw new AuthException("该账号已在其他设备登录,请重新登录");
|
throw new AuthException("该账号已在其他设备登录,请重新登录");
|
||||||
}
|
}
|
||||||
// 校验通过 → 续期 Redis token 有效时间
|
// 续期:Caffeine 的 expireAfterWrite 是从最后写入开始算,所以 put 一下即可续期
|
||||||
redisUtil.expire(redisKey, JwtUtil.EXPIRE_TIME / 1000);
|
userTokenCache.storeToken(tokenKey, storedToken);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,112 +1,58 @@
|
|||||||
package com.zhangy.skyeye.publics.consts;
|
package com.zhangy.skyeye.publics.consts;
|
||||||
|
|
||||||
// import com.zhangy.skyeye.smp.dto.SmpWayPointResDTO;
|
|
||||||
|
|
||||||
public class CacheKey {
|
public class CacheKey {
|
||||||
|
|
||||||
/*==========================================
|
/*==================== publics 模块 ====================*/
|
||||||
publics 模块用 skyeye: 前缀
|
// 用户token,30分钟
|
||||||
==========================================*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户token,30分钟
|
|
||||||
*/
|
|
||||||
private static final String USER_TOEKN = "skyeye:user:token:%s@%s";
|
private static final String USER_TOEKN = "skyeye:user:token:%s@%s";
|
||||||
|
|
||||||
/**
|
// 获取token key
|
||||||
* 获取token key
|
public static String getToken(Long userId, String userName) {
|
||||||
*/
|
|
||||||
public static String getToekn(Long userId, String userName) {
|
|
||||||
return String.format(CacheKey.USER_TOEKN, userId, userName);
|
return String.format(CacheKey.USER_TOEKN, userId, userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*==========================================
|
/*==================== 分布式锁 ====================*/
|
||||||
分布式锁用 skyeye:lock: 前缀
|
// 上传sar图像锁
|
||||||
==========================================*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传sar图像锁
|
|
||||||
*/
|
|
||||||
public static final String SAR_IMAGE_UPLOAD_LOCK = "skyeye:lock:image";
|
public static final String SAR_IMAGE_UPLOAD_LOCK = "skyeye:lock:image";
|
||||||
|
|
||||||
/*==========================================
|
/*==================== SAR 模块 ====================*/
|
||||||
sar 模块用 skyeye:sar: 前缀
|
// 控制回包,1秒
|
||||||
==========================================*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 控制回包,1秒
|
|
||||||
*/
|
|
||||||
private static final String SAR_CONTROL_BACK = "skyeye:sar:control";
|
private static final String SAR_CONTROL_BACK = "skyeye:sar:control";
|
||||||
|
// sar状态,1秒
|
||||||
/**
|
private static final String SAR_STATUS = "skyeye:sar:status";
|
||||||
* sar状态,1秒
|
// 已执行连接命令的sar,永久
|
||||||
*/
|
|
||||||
private static final String SAR_STATUS = "skyeye:sar:status:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已执行连接命令的sar,永久
|
|
||||||
*/
|
|
||||||
public static final String SAR_CONNECTED = "skyeye:sar:connected";
|
public static final String SAR_CONNECTED = "skyeye:sar:connected";
|
||||||
|
|
||||||
/**
|
// 获取控制回包key:加ip
|
||||||
* 获取控制回包key:加ip
|
|
||||||
*/
|
|
||||||
public static String getSarControlBack(String ip) {
|
public static String getSarControlBack(String ip) {
|
||||||
return SAR_CONTROL_BACK + ip;
|
return SAR_CONTROL_BACK + ":" + ip;
|
||||||
|
}
|
||||||
|
// 获取状态key:加ip
|
||||||
|
public static String getSarStatus(String ip) {
|
||||||
|
return SAR_STATUS + ":" + ip;
|
||||||
|
}
|
||||||
|
// 获取已执行连接命令的sar-key:加ip
|
||||||
|
public static String getSarConnected(String ip) {
|
||||||
|
return SAR_CONNECTED + ":" + ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*==================== Device 模块 ====================*/
|
||||||
* 获取状态key:加ip
|
// sar载荷,永久
|
||||||
*/
|
|
||||||
public static String getSarConnect(String ip) {
|
|
||||||
return SAR_STATUS + ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==========================================
|
|
||||||
device 模块用 skyeye:device: 前缀
|
|
||||||
==========================================*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sar载荷,永久
|
|
||||||
*/
|
|
||||||
public static final String DEVICE_SAR = "skyeye:device:sar";
|
public static final String DEVICE_SAR = "skyeye:device:sar";
|
||||||
|
// 无人机,永久
|
||||||
/**
|
|
||||||
* 无人机,永久
|
|
||||||
*/
|
|
||||||
public static final String DEVICE_UAV = "skyeye:device:uav";
|
public static final String DEVICE_UAV = "skyeye:device:uav";
|
||||||
|
|
||||||
/*==========================================
|
/*==================== SMP 模块 ====================*/
|
||||||
smp 模块用 skyeye:smp: 前缀
|
// 运动规划响应,1秒
|
||||||
==========================================*/
|
public static final String SMP_WAYPOINT_RES = "skyeye:smp:waypoint:";
|
||||||
|
// 飞行控制响应,1秒
|
||||||
/**
|
public static final String SMP_FLIGHT_RES = "skyeye:smp:flight:";
|
||||||
* 运动规划响应,1秒
|
// 云台控制响应,1秒
|
||||||
*/
|
public static final String SMP_GIMBALMGR_RES = "skyeye:smp:gimbalmgr:";
|
||||||
//public static final String SMP_WAYPOINT_RES = "skyeye:smp:waypoint:";
|
// 数据订阅响应,1秒
|
||||||
|
public static final String SMP_SUBSCRIPT_RES = "skyeye:smp:subscript:";
|
||||||
/**
|
// 数据订阅回传数据,1秒
|
||||||
* 飞行控制响应,1秒
|
public static final String SMP_SUBSCRIPT_DATA = "skyeye:smp:subscript:";
|
||||||
*/
|
// 相机视频流响应,1秒
|
||||||
//public static final String SMP_FLIGHT_RES = "skyeye:smp:flight:";
|
public static final String SMP_VIDEO_RES = "skyeye:smp:video:";
|
||||||
|
|
||||||
/**
|
|
||||||
* 云台控制响应,1秒
|
|
||||||
*/
|
|
||||||
//public static final String SMP_GIMBALMGR_RES = "skyeye:smp:gimbalmgr:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据订阅响应,1秒
|
|
||||||
*/
|
|
||||||
//public static final String SMP_SUBSCRIPT_RES = "skyeye:smp:subscript:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据订阅回传数据,1秒
|
|
||||||
*/
|
|
||||||
//public static final String SMP_SUBSCRIPT_DATA = "skyeye:smp:subscript:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 相机视频流响应,1秒
|
|
||||||
*/
|
|
||||||
//public static final String SMP_VIDEO_RES = "skyeye:smp:video:";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.zhangy.skyeye.publics.controller;
|
|||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
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.IgnoreAuth;
|
||||||
import com.zhangy.skyeye.common.extend.anno.OperationLog;
|
import com.zhangy.skyeye.common.extend.anno.OperationLog;
|
||||||
import com.zhangy.skyeye.common.extend.dto.PageDTO;
|
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.extend.util.ObjectUtil;
|
||||||
import com.zhangy.skyeye.common.pojo.result.Result;
|
import com.zhangy.skyeye.common.pojo.result.Result;
|
||||||
import com.zhangy.skyeye.common.utils.JwtUtil;
|
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.consts.CacheKey;
|
||||||
import com.zhangy.skyeye.publics.dto.RegisterDTO;
|
import com.zhangy.skyeye.publics.dto.RegisterDTO;
|
||||||
import com.zhangy.skyeye.publics.dto.SysUserPwdDTO;
|
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.entity.SysUser;
|
||||||
import com.zhangy.skyeye.publics.service.ISysUserService;
|
import com.zhangy.skyeye.publics.service.ISysUserService;
|
||||||
import com.zhangy.skyeye.publics.utils.SecurityUtil;
|
import com.zhangy.skyeye.publics.utils.SecurityUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@ -30,125 +30,124 @@ import java.util.List;
|
|||||||
|
|
||||||
@Validated
|
@Validated
|
||||||
@RestController
|
@RestController
|
||||||
@AllArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequestMapping("/user")
|
@RequestMapping("/user")
|
||||||
public class SysUserController {
|
public class SysUserController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysUserService sysUserService;
|
private ISysUserService sysUserService;
|
||||||
|
|
||||||
@Autowired
|
private final UserTokenCache userTokenCache;
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
/** 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
|
*
|
||||||
*/
|
* @param dto
|
||||||
@IgnoreAuth
|
* @return
|
||||||
@OperationLog(value = "登录", type = SysLog.TYPE_USER)
|
*/
|
||||||
@PostMapping("/login")
|
@PostMapping("/updpwd")
|
||||||
public Result login(@RequestBody RegisterDTO registerDTO) {
|
public Object updatePwd(@Valid SysUserPwdDTO dto) {
|
||||||
SysUser user = sysUserService.selectByAccount(registerDTO.getUsername(), registerDTO.getPassword().toLowerCase());
|
List<SysUser> list = sysUserService.selectById(dto.getUserId());
|
||||||
if (user == null) {
|
if (ObjectUtil.isEmpty(list)) {
|
||||||
throw ServiceException.noLog(MessageUtils.message("user.login.error"));
|
throw ServiceException.noLog(MessageUtils.message("user.updpwd.error"));
|
||||||
}
|
}
|
||||||
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
|
SysUser user = list.get(0);
|
||||||
String key = JSONUtil.toJsonStr(userDTO);
|
if (user.getPassword().equals(dto.getOldPwd())) {
|
||||||
String accessToken = JwtUtil.sign(key, user.getPassword());
|
throw ServiceException.noLog(MessageUtils.message("user.updpwd.nochg"));
|
||||||
redisUtil.set(userDTO.getTokenKey(), accessToken, TOKEN_EXPIRE);
|
}
|
||||||
userDTO.setToken(accessToken);
|
user.setPassword(dto.getNewPwd());
|
||||||
return Result.successData(MessageUtils.message("user.login.success"), userDTO);
|
sysUserService.update(user);
|
||||||
}
|
return MessageUtils.message("user.updpwd.success");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改密码
|
* 退出接口
|
||||||
* @param dto
|
*/
|
||||||
* @return
|
@IgnoreAuth
|
||||||
*/
|
@OperationLog(value = "退出登录", type = SysLog.TYPE_USER)
|
||||||
@PostMapping("/updpwd")
|
@RequestMapping("/logout")
|
||||||
public Object updatePwd(@Valid SysUserPwdDTO dto) {
|
@GetMapping
|
||||||
List<SysUser> list = sysUserService.selectById(dto.getUserId());
|
public Result logout() {
|
||||||
if (ObjectUtil.isEmpty(list)) {
|
UserDTO userDTO = SecurityUtil.getUser();
|
||||||
throw ServiceException.noLog(MessageUtils.message("user.updpwd.error"));
|
if (userDTO != null) {
|
||||||
}
|
String tokenKey = CacheKey.getToken(userDTO.getId(), userDTO.getAccount());
|
||||||
SysUser user = list.get(0);
|
userTokenCache.evictToken(tokenKey);
|
||||||
if (user.getPassword().equals(dto.getOldPwd())) {
|
}
|
||||||
throw ServiceException.noLog(MessageUtils.message("user.updpwd.nochg"));
|
return Result.status(true);
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询
|
* 分页查询
|
||||||
*/
|
*/
|
||||||
@RequestMapping("/page")
|
@RequestMapping("/page")
|
||||||
public Object selectPage(@Valid @RequestBody PageDTO param) {
|
public Object selectPage(@Valid @RequestBody PageDTO param) {
|
||||||
return sysUserService.selectPage(param);
|
return sysUserService.selectPage(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 列表查询
|
* 列表查询
|
||||||
*/
|
*/
|
||||||
@RequestMapping("/list")
|
@RequestMapping("/list")
|
||||||
public Object selectList(@Valid @RequestBody PageDTO param) {
|
public Object selectList(@Valid @RequestBody PageDTO param) {
|
||||||
return sysUserService.selectList(param);
|
return sysUserService.selectList(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询详情
|
* 查询详情
|
||||||
*/
|
*/
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
public Object selectById(@RequestParam Long... id) {
|
public Object selectById(@RequestParam Long... id) {
|
||||||
return sysUserService.selectById(id);
|
return sysUserService.selectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增
|
* 新增
|
||||||
*/
|
*/
|
||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
@OperationLog(value = "用户新增", type = SysLog.TYPE_USER)
|
@OperationLog(value = "用户新增", type = SysLog.TYPE_USER)
|
||||||
public Object insert(@RequestBody SysUser e) {
|
public Object insert(@RequestBody SysUser e) {
|
||||||
return sysUserService.insert(e);
|
return sysUserService.insert(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改
|
* 修改
|
||||||
*/
|
*/
|
||||||
@PostMapping("/update")
|
@PostMapping("/update")
|
||||||
@OperationLog(value = "用户修改", type = SysLog.TYPE_USER)
|
@OperationLog(value = "用户修改", type = SysLog.TYPE_USER)
|
||||||
public Object update(@RequestBody SysUser e) {
|
public Object update(@RequestBody SysUser e) {
|
||||||
return sysUserService.update(e);
|
return sysUserService.update(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除
|
* 删除
|
||||||
*/
|
*/
|
||||||
@OperationLog(value = "用户删除", type = SysLog.TYPE_USER)
|
@OperationLog(value = "用户删除", type = SysLog.TYPE_USER)
|
||||||
@PostMapping("/remove")
|
@PostMapping("/remove")
|
||||||
public Object delete(@RequestBody Long... id) {
|
public Object delete(@RequestBody Long... id) {
|
||||||
return sysUserService.delete(id);
|
return sysUserService.delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,26 +10,37 @@ public class UserDTO {
|
|||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/** 用户名 */
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
private String account;
|
private String account;
|
||||||
|
|
||||||
/** 状态 0:禁用 1:正常 */
|
/**
|
||||||
|
* 状态 0:禁用 1:正常
|
||||||
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
/** 用户所有的权限信息 */
|
/**
|
||||||
|
* 用户所有的权限信息
|
||||||
|
*/
|
||||||
private Set<String> authorities;
|
private Set<String> authorities;
|
||||||
|
|
||||||
/** 角色ID */
|
/**
|
||||||
|
* 角色ID
|
||||||
|
*/
|
||||||
private Integer roleId;
|
private Integer roleId;
|
||||||
|
|
||||||
/** Token */
|
/**
|
||||||
|
* Token
|
||||||
|
*/
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取token的缓存键
|
* 获取token的缓存键
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String getTokenKey() {
|
public String getTokenKey() {
|
||||||
return CacheKey.getToekn(id, account);
|
return CacheKey.getToken(id, account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,57 +1,25 @@
|
|||||||
package com.zhangy.skyeye.publics.service.impl;
|
package com.zhangy.skyeye.publics.service.impl;
|
||||||
|
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.cache.publics.UserTokenCache;
|
||||||
import com.zhangy.skyeye.publics.service.ISysLoginService;
|
import com.zhangy.skyeye.publics.service.ISysLoginService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import com.zhangy.skyeye.publics.utils.CacheUtil;
|
||||||
import org.springframework.data.redis.core.Cursor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.redis.core.RedisCallback;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.core.ScanOptions;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class SysLoginServiceImpl implements ISysLoginService {
|
public class SysLoginServiceImpl implements ISysLoginService {
|
||||||
|
|
||||||
@Autowired
|
private final UserTokenCache userTokenCache;
|
||||||
private RedisTemplate redisTemplate;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLogged() {
|
public boolean hasLogged() {
|
||||||
return Boolean.TRUE.equals(redisTemplate.execute((RedisCallback<Boolean>) connection -> {
|
// 获取底层 Caffeine Cache
|
||||||
try (Cursor<byte[]> cursor = connection.scan(
|
return CacheUtil.size(userTokenCache) > 0;
|
||||||
ScanOptions.scanOptions()
|
|
||||||
.match("skyeye:user:token:*")
|
|
||||||
.count(1) // 只需要找到1个匹配项
|
|
||||||
.build())) {
|
|
||||||
return cursor.hasNext();
|
|
||||||
// } catch (IOException ex) {
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new ServiceException("查询当前登录人数失败:" + ex.getMessage());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int countLogged() {
|
public int countLogged() {
|
||||||
return (int) redisTemplate.execute((RedisCallback<Integer>) connection -> {
|
return (int) CacheUtil.size(userTokenCache);
|
||||||
int count = 0;
|
|
||||||
ScanOptions options = ScanOptions.scanOptions()
|
|
||||||
.match("skyeye:user:token:*")
|
|
||||||
.count(100) // 合理的COUNT值
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try (Cursor<byte[]> cursor = connection.scan(options)) {
|
|
||||||
while (cursor.hasNext()) {
|
|
||||||
count++;
|
|
||||||
cursor.next();
|
|
||||||
}
|
|
||||||
// } catch (IOException ex) {
|
|
||||||
} catch (Exception ex) {
|
|
||||||
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @PROJECT_NAME: skyeyesystem_online
|
||||||
|
* @DESCRIPTION: 缓存工具
|
||||||
|
* @AUTHOR: GuanCheng Long
|
||||||
|
* @DATE: 2026/1/30 13:34
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class CacheUtil {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T get(Cache cache, Object key, Class<T> clazz) {
|
||||||
|
if (cache == null) return null;
|
||||||
|
Cache.ValueWrapper wrapper = cache.get(key);
|
||||||
|
if (wrapper == null) return null;
|
||||||
|
Object value = wrapper.get();
|
||||||
|
if (clazz.isInstance(value)) {
|
||||||
|
return (T) value;
|
||||||
|
}
|
||||||
|
// 可选:log.warn("缓存类型不匹配,期望 {},实际 {}", clazz.getName(), value.getClass().getName());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 CacheUtil.java 中新增
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> List<T> getAll(Cache cache, Class<T> clazz) {
|
||||||
|
if (cache == null) return Collections.emptyList();
|
||||||
|
|
||||||
|
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache =
|
||||||
|
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) cache.getNativeCache();
|
||||||
|
|
||||||
|
return nativeCache.asMap().values().stream()
|
||||||
|
.filter(clazz::isInstance)
|
||||||
|
.map(clazz::cast)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long size(Cache springCache) {
|
||||||
|
if (springCache == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
com.github.benmanes.caffeine.cache.Cache<?, ?> nativeCache =
|
||||||
|
(com.github.benmanes.caffeine.cache.Cache<?, ?>) springCache.getNativeCache();
|
||||||
|
return nativeCache.estimatedSize(); // 或 nativeCache.asMap().size()
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("无法获取缓存大小,可能是非 Caffeine 实现", e);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.zhangy.skyeye.publics.utils;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @PROJECT_NAME: skyeyesystem_online
|
||||||
|
* @DESCRIPTION: 本地内存锁工具(替代 Redis 分布式锁,适用于单实例部署)
|
||||||
|
* @AUTHOR: GuanCheng Long
|
||||||
|
* @DATE: 2026/1/30 13:14
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class LocalLockUtil {
|
||||||
|
|
||||||
|
// Caffeine 缓存:key=锁名, value=ReentrantLock
|
||||||
|
private static final Cache<String, ReentrantLock> LOCK_CACHE = Caffeine.newBuilder()
|
||||||
|
.expireAfterAccess(5, TimeUnit.MINUTES) // 锁闲置 5 分钟自动清理
|
||||||
|
.maximumSize(100) // 最多 100 个锁
|
||||||
|
.build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取或创建锁,并尝试获取
|
||||||
|
*
|
||||||
|
* @param lockKey 锁的 key
|
||||||
|
* @param timeoutSeconds 尝试获取锁的超时时间(秒)
|
||||||
|
* @return true=获取成功,false=获取失败(被占用)
|
||||||
|
*/
|
||||||
|
public boolean tryLock(String lockKey, long timeoutSeconds) {
|
||||||
|
ReentrantLock lock = LOCK_CACHE.get(lockKey, k -> new ReentrantLock());
|
||||||
|
try {
|
||||||
|
return lock.tryLock(timeoutSeconds, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放锁(仅释放自己的)
|
||||||
|
*/
|
||||||
|
public void unlock(String lockKey) {
|
||||||
|
ReentrantLock lock = LOCK_CACHE.getIfPresent(lockKey);
|
||||||
|
if (lock != null && lock.isHeldByCurrentThread()) {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.zhangy.skyeye.publics.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class SpringContextUtil implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||||
|
applicationContext = context;
|
||||||
|
log.info("SpringContextUtil 初始化完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> clazz) {
|
||||||
|
return applicationContext.getBean(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,20 +1,16 @@
|
|||||||
package com.zhangy.skyeye.sar.context;
|
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.JmAirlineStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* @PROJECT_NAME: skyeyesystem
|
* @PROJECT_NAME: skyeyesystem
|
||||||
* @DESCRIPTION: SAR 任务上下文提供者实现(基于 Redis 缓存,避免循环依赖)
|
* @DESCRIPTION: SAR 任务上下文提供者实现(基于 Redis 缓存,避免循环依赖)
|
||||||
* - JmJobStatusServiceImpl 在更新状态时,主动把 JmUavStatusDTO 推送到 Redis
|
* - JmJobStatusServiceImpl 在更新状态时,主动把 JmUavStatusDTO 推送到 Redis
|
||||||
* - 此类只从 Redis 读取,不注入 JmJobStatusService
|
* - 此类只从 Redis 读取,不注入 JmJobStatusService
|
||||||
* @AUTHOR: GuanCheng Long
|
* @AUTHOR: GuanCheng Long
|
||||||
* @DATE: 2026/1/21 1:10
|
* @DATE: 2026/1/21 1:10
|
||||||
*/
|
*/
|
||||||
@ -22,14 +18,11 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
||||||
|
|
||||||
@Autowired
|
private final UavCache uavCache;
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
// Redis 键前缀
|
public SarTaskContextProviderImpl(UavCache uavCache) {
|
||||||
private static final String UAV_STATUS_KEY_PREFIX = "sar:context:uav:";
|
this.uavCache = uavCache;
|
||||||
|
}
|
||||||
// 缓存过期时间(秒)
|
|
||||||
private static final long CACHE_EXPIRE_SECONDS = 600; // 10 分钟
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JmUavStatusDTO getCurrentUav(String payloadIp) {
|
public JmUavStatusDTO getCurrentUav(String payloadIp) {
|
||||||
@ -37,16 +30,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
|||||||
log.warn("获取 uav 状态失败:payloadIp 为空");
|
log.warn("获取 uav 状态失败:payloadIp 为空");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
String key = "sar:context:uav:" + payloadIp; // 保持原 key 格式
|
||||||
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
|
return uavCache.getShort(key, JmUavStatusDTO.class);
|
||||||
Object obj = redisUtil.get(key);
|
|
||||||
|
|
||||||
if (obj instanceof JmUavStatusDTO) {
|
|
||||||
return (JmUavStatusDTO) obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("Redis 中未找到 IP={} 的 uav 状态", payloadIp);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,11 +65,10 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
|
String key = "sar:context:uav:" + payloadIp;
|
||||||
try {
|
try {
|
||||||
// 设置值 + 过期时间
|
uavCache.putShort(key, uavStatus);// 自动过期 10 分钟
|
||||||
redisUtil.set(key, uavStatus, CACHE_EXPIRE_SECONDS);
|
log.debug("已更新缓存:key={}, jobExecId={}", key, uavStatus.getJobExecId());
|
||||||
log.debug("已更新 Redis 缓存:key={}, jobExecId={}", key, uavStatus.getJobExecId());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新 SAR uav 状态缓存失败,ip={}", payloadIp, e);
|
log.error("更新 SAR uav 状态缓存失败,ip={}", payloadIp, e);
|
||||||
}
|
}
|
||||||
@ -97,8 +81,8 @@ public class SarTaskContextProviderImpl implements SarTaskContextProvider {
|
|||||||
if (payloadIp == null) {
|
if (payloadIp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String key = UAV_STATUS_KEY_PREFIX + payloadIp;
|
String key = "sar:context:uav:" + payloadIp;
|
||||||
redisUtil.del(key);
|
uavCache.evictShort(key);
|
||||||
log.debug("SAR uav 缓存已标记清理(或自然过期):{}", key);
|
log.debug("SAR uav 缓存已清理:{}", key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,20 @@
|
|||||||
package com.zhangy.skyeye.sar.control;
|
package com.zhangy.skyeye.sar.control;
|
||||||
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.publics.consts.CacheKey;
|
|
||||||
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 控制:连接
|
* 控制:建立连接
|
||||||
|
* 连接成功后,在回传状态时会将状态信息放入缓存,这里只记录连接标志
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Order(1)
|
@Order(1)
|
||||||
@ -23,12 +22,11 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SarControlConnectStrategy implements ISarControlStrategy {
|
public class SarControlConnectStrategy implements ISarControlStrategy {
|
||||||
|
|
||||||
private final RedisUtil redisUtil;
|
private final SarCache sarCache;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(SarControlParamDTO param) {
|
public boolean supports(SarControlParamDTO param) {
|
||||||
SarControlTypeEnum controlType = param.getControlType();
|
return param.getControlType() == SarControlTypeEnum.CONNECT;
|
||||||
return controlType == SarControlTypeEnum.CONNECT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -40,15 +38,13 @@ public class SarControlConnectStrategy implements ISarControlStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendPost(SarControlDTO sar) {
|
public void sendPost(SarControlDTO sar) {
|
||||||
// 建立连接后,回传状态时会将状态信息放入缓存
|
// 建立连接后,记录连接标志
|
||||||
redisUtil.hset(CacheKey.SAR_CONNECTED, sar.getIp(), new Date());
|
sarCache.markConnected(sar.getIp());
|
||||||
|
log.info("SAR [{}] 已标记为连接状态", sar.getIp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int retryType() {
|
public int retryType() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
package com.zhangy.skyeye.sar.control;
|
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.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import com.zhangy.skyeye.publics.consts.CacheKey;
|
import com.zhangy.skyeye.publics.consts.CacheKey;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlPackDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlPackDTO;
|
||||||
@ -14,7 +14,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@ -59,7 +58,7 @@ public class SarControlContext {
|
|||||||
private final int POLLING_INTERVAL = 100;
|
private final int POLLING_INTERVAL = 100;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private SarCache sarCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应答超时
|
* 应答超时
|
||||||
@ -74,6 +73,7 @@ public class SarControlContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行
|
* 执行
|
||||||
|
*
|
||||||
* @param param
|
* @param param
|
||||||
*/
|
*/
|
||||||
public void execute(SarControlParamDTO param) {
|
public void execute(SarControlParamDTO param) {
|
||||||
@ -89,12 +89,12 @@ public class SarControlContext {
|
|||||||
/**
|
/**
|
||||||
* 执行发送:失败则递归重试
|
* 执行发送:失败则递归重试
|
||||||
*
|
*
|
||||||
* @param param 控制参数
|
* @param param 控制参数
|
||||||
* @param matchedStrategy 控制策略
|
* @param matchedStrategy 控制策略
|
||||||
*/
|
*/
|
||||||
private void sendControl(SarControlParamDTO param, ISarControlStrategy matchedStrategy) {
|
private void sendControl(SarControlParamDTO param, ISarControlStrategy matchedStrategy) {
|
||||||
List<SarControlDTO> controls = matchedStrategy.handle(param);
|
List<SarControlDTO> controls = matchedStrategy.handle(param);
|
||||||
for(SarControlDTO control: controls) {
|
for (SarControlDTO control : controls) {
|
||||||
control.setIp(param.getIp());
|
control.setIp(param.getIp());
|
||||||
sendUdp(control);
|
sendUdp(control);
|
||||||
matchedStrategy.sendPost(control);
|
matchedStrategy.sendPost(control);
|
||||||
@ -103,60 +103,92 @@ public class SarControlContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送,未收到回执则重发
|
* 发送,未收到回执则重发
|
||||||
|
*
|
||||||
* @param control
|
* @param control
|
||||||
*/
|
*/
|
||||||
private void sendUdp(SarControlDTO control) {
|
private void sendUdp(SarControlDTO control) {
|
||||||
SarControlTypeEnum controlType = control.getControlType();
|
SarControlTypeEnum controlType = control.getControlType();
|
||||||
String ip = control.getIp();
|
String targetIp = control.getIp();
|
||||||
log.debug("开始发送雷达控制指令[" + controlType + "]----------------------");
|
log.info("准备发送雷达控制指令 | 类型:{} | 目标IP:{}", controlType, targetIp);
|
||||||
//System.out.println(control);
|
String cacheKey = CacheKey.getSarControlBack(targetIp);
|
||||||
|
byte[] payload;
|
||||||
|
try {
|
||||||
|
payload = pack(control);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("打包控制指令失败 | 类型:{} | ip:{} | {}", controlType, targetIp, e.getMessage(), e);
|
||||||
|
throw new ServiceException("控制指令打包失败:" + e.getMessage());
|
||||||
|
}
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
byte[] content = pack(control);
|
InetSocketAddress address = new InetSocketAddress(targetIp, PORT);
|
||||||
socket.connect(new InetSocketAddress(ip, PORT));
|
socket.connect(address);
|
||||||
DatagramPacket packet = new DatagramPacket(content, content.length);
|
DatagramPacket packet = new DatagramPacket(payload, payload.length);
|
||||||
|
SarErrorDTO response = null;
|
||||||
int failCount = 0;
|
int retryCount = 0;
|
||||||
SarErrorDTO info = null;
|
while (response == null && retryCount < RETRY_MAX) {
|
||||||
while (info == null && failCount < RETRY_MAX) { // 失败重试
|
// 发送
|
||||||
socket.send(packet);
|
try {
|
||||||
// 每0.1秒取回执,1秒后超时
|
socket.send(packet);
|
||||||
long startTime = System.currentTimeMillis();
|
log.info("UDP指令已发送 | 第{}次尝试 | 类型:{} | ip:{}",
|
||||||
String cacheKey = CacheKey.getSarControlBack(ip);
|
retryCount + 1, controlType, targetIp);
|
||||||
|
} catch (IOException e) {
|
||||||
while (System.currentTimeMillis() - startTime < ANSWER_TIMEOUT) {
|
log.warn("发送UDP失败 | 第{}次尝试 | {}", retryCount + 1, e.getMessage());
|
||||||
if (redisUtil.hasKey(cacheKey)) {
|
retryCount++;
|
||||||
info = (SarErrorDTO) redisUtil.get(cacheKey);
|
continue;
|
||||||
redisUtil.del(cacheKey);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Thread.sleep(POLLING_INTERVAL);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (info == null) { // 超时未收到回执
|
// 等待回执(轮询方式)
|
||||||
failCount++;
|
response = waitForResponse(cacheKey, targetIp, controlType);
|
||||||
} else if (info.getExecStatus() == 1) {
|
if (response == null) {
|
||||||
// 只有发送的数据结构错误时才会返回错误状态,并不会因为业务不允许返回错误
|
retryCount++;
|
||||||
throw new ServiceException("控制指令[" + controlType + "]执行状态错误,请重试");
|
log.warn("第{}次发送未收到回执 | 类型:{} | ip:{}", retryCount, controlType, targetIp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info == null) {
|
// 最终判断
|
||||||
throw new SarConnectException("控制指令[" + controlType + "]发送失败,雷达[" + ip + "]无应答,请重试");
|
if (response == null) {
|
||||||
} else {
|
String msg = String.format("雷达[%s]控制指令[%s]发送失败:超时无应答", targetIp, controlType);
|
||||||
log.info("雷达控制指令[" + controlType + "]发送完毕----------------------");
|
log.error(msg);
|
||||||
|
throw new SarConnectException(msg + ",请重试");
|
||||||
}
|
}
|
||||||
} catch (RedisConnectionFailureException ex) {
|
// 业务状态判断
|
||||||
throw ServiceException.errorLog("无法连接到Redis服务!");
|
if (response.getExecStatus() == 1) {
|
||||||
} catch (IOException ex) {
|
String msg = String.format("雷达[%s]控制指令[%s]执行失败:状态码=1(数据结构错误?)",
|
||||||
throw ServiceException.errorLog("控制指令[" + controlType + "]发送失败 " + ex.getMessage());
|
targetIp, controlType);
|
||||||
|
log.error(msg);
|
||||||
|
throw new ServiceException(msg + ",请检查指令内容后重试");
|
||||||
|
}
|
||||||
|
log.info("雷达控制指令发送成功 | 类型:{} | ip:{} | 状态:{}", controlType, targetIp, response.getExecStatus());
|
||||||
|
} catch (IOException e) {
|
||||||
|
String msg = String.format("雷达控制指令[%s]网络异常 | ip:%s | %s", controlType, targetIp, e.getMessage());
|
||||||
|
log.error(msg, e);
|
||||||
|
throw ServiceException.errorLog(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮询等待缓存中的回执(带超时)
|
||||||
|
*/
|
||||||
|
private SarErrorDTO waitForResponse(String cacheKey, String ip, SarControlTypeEnum controlType) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
while (System.currentTimeMillis() - start < ANSWER_TIMEOUT) {
|
||||||
|
SarErrorDTO dto = sarCache.getControlBack(ip); // 如果 SarCache 封装了泛型方法可以直接用
|
||||||
|
if (dto != null) {
|
||||||
|
sarCache.saveStatus(cacheKey, null); // 清空 shortCache 对应的 key
|
||||||
|
log.info("收到雷达回执 | ip:{} | 控制类型:{} | 状态:{}", ip, controlType, dto.getExecStatus());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(POLLING_INTERVAL);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
log.warn("等待雷达回执时被中断 | ip:{}", ip);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null; // 超时
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 封包
|
* 封包
|
||||||
|
*
|
||||||
* @param sarControl
|
* @param sarControl
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
package com.zhangy.skyeye.sar.control;
|
package com.zhangy.skyeye.sar.control;
|
||||||
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlDTO;
|
||||||
import com.zhangy.skyeye.publics.consts.CacheKey;
|
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
@ -22,7 +21,7 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SarControlDisconnectStrategy implements ISarControlStrategy {
|
public class SarControlDisconnectStrategy implements ISarControlStrategy {
|
||||||
|
|
||||||
private final RedisUtil redisUtil;
|
private final SarCache sarCache;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(SarControlParamDTO param) {
|
public boolean supports(SarControlParamDTO param) {
|
||||||
@ -37,15 +36,12 @@ public class SarControlDisconnectStrategy implements ISarControlStrategy {
|
|||||||
return Collections.singletonList(connect);
|
return Collections.singletonList(connect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除缓存键
|
|
||||||
* @param sar
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void sendPost(SarControlDTO sar) {
|
public void sendPost(SarControlDTO sar) {
|
||||||
String connectKey = CacheKey.getSarConnect(sar.getIp());
|
// 清理短时状态
|
||||||
redisUtil.del(connectKey);
|
sarCache.removeConnection(sar.getIp()); // ← 从 permanentCache 移除连接标志
|
||||||
redisUtil.hdel(CacheKey.SAR_CONNECTED, sar.getIp());
|
sarCache.saveStatus(sar.getIp(), null); // ← 等同于清空 shortCache 中的状态
|
||||||
|
log.info("已断开 SAR IP: {},缓存清理完成", sar.getIp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -42,14 +42,12 @@ public class SarControlController {
|
|||||||
*/
|
*/
|
||||||
@RequestMapping("/turnon")
|
@RequestMapping("/turnon")
|
||||||
public Result turnOn(@RequestBody String ip) {
|
public Result turnOn(@RequestBody String ip) {
|
||||||
String ipVal = "";
|
String ipVal;
|
||||||
try {
|
try {
|
||||||
// 1. 使用 ObjectMapper 将 JSON 字符串解析成一个 JsonNode 树
|
// 1. 使用 ObjectMapper 将 JSON 字符串解析成一个 JsonNode 树
|
||||||
JsonNode rootNode = objectMapper.readTree(ip);
|
JsonNode rootNode = objectMapper.readTree(ip);
|
||||||
|
|
||||||
// 2. 从树中获取 "payloadId" 节点,并将其值转换为文本
|
// 2. 从树中获取 "payloadId" 节点,并将其值转换为文本
|
||||||
ipVal = rootNode.path("payloadId").asText();
|
ipVal = rootNode.path("payloadId").asText();
|
||||||
|
|
||||||
// 检查是否成功获取
|
// 检查是否成功获取
|
||||||
if (ipVal == null || ipVal.isEmpty()) {
|
if (ipVal == null || ipVal.isEmpty()) {
|
||||||
// 根据你的业务逻辑返回错误,例如
|
// 根据你的业务逻辑返回错误,例如
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
package com.zhangy.skyeye.sar.listen;
|
package com.zhangy.skyeye.sar.listen;
|
||||||
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.service.JmJobStatusService;
|
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.SarErrorDTO;
|
||||||
import com.zhangy.skyeye.sar.dto.SarStatusPackDTO;
|
import com.zhangy.skyeye.sar.dto.SarStatusPackDTO;
|
||||||
import com.zhangy.skyeye.sar.enums.SarErrorTypeEnum;
|
import com.zhangy.skyeye.sar.enums.SarErrorTypeEnum;
|
||||||
import com.zhangy.skyeye.sar.task.CircularBufferQueue;
|
import com.zhangy.skyeye.sar.task.CircularBufferQueue;
|
||||||
import com.zhangy.skyeye.sar.task.DiscardOldestPolicyWithLog;
|
import com.zhangy.skyeye.sar.task.DiscardOldestPolicyWithLog;
|
||||||
import com.zhangy.skyeye.sar.task.PriorityThreadFactory;
|
import com.zhangy.skyeye.sar.task.PriorityThreadFactory;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class SarStatusListener extends SarAbstractListener {
|
public class SarStatusListener extends SarAbstractListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,8 +50,7 @@ public class SarStatusListener extends SarAbstractListener {
|
|||||||
@Value("${skyeye.sar.udp.status.connect-timeout:15}")
|
@Value("${skyeye.sar.udp.status.connect-timeout:15}")
|
||||||
private int connectTimeout;
|
private int connectTimeout;
|
||||||
|
|
||||||
@Autowired
|
private final SarCache sarCache;
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JmJobStatusService sarJobStatusService;
|
private JmJobStatusService sarJobStatusService;
|
||||||
@ -70,38 +70,34 @@ public class SarStatusListener extends SarAbstractListener {
|
|||||||
@Override
|
@Override
|
||||||
protected void processData(DatagramPacket packet) throws IOException {
|
protected void processData(DatagramPacket packet) throws IOException {
|
||||||
socket.receive(packet);
|
socket.receive(packet);
|
||||||
// 过滤无效数据包
|
|
||||||
if (packet.getLength() != 100) {
|
if (packet.getLength() != 100) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.debug("接收到状态包----------------------");
|
log.debug("接收到状态包----------------------");
|
||||||
String ip = packet.getAddress().getHostAddress();
|
String ip = packet.getAddress().getHostAddress();
|
||||||
// 处理接收到的数据
|
|
||||||
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
|
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
|
||||||
if (packDTO == null) {
|
if (packDTO == null) {
|
||||||
if (running)
|
if (running)
|
||||||
log.warn("[" + packDTO.getPayloadIp() + "]状态包校验失败,已丢弃。错误包=" +
|
log.warn("状态包校验失败,已丢弃。");
|
||||||
packDTO.getErrorPacketStatus() + ",状态包=" + packDTO.getDevicePacketStatus());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 错误包,结果放入缓存,超时1秒
|
// 错误包 → 回执
|
||||||
int errorStatus = packDTO.getErrorPacketStatus();
|
int errorStatus = packDTO.getErrorPacketStatus();
|
||||||
if (errorStatus == 1) {
|
if (errorStatus == 1) {
|
||||||
SarErrorDTO info = packDTO.getDeviceErrorInfo();
|
SarErrorDTO info = packDTO.getDeviceErrorInfo();
|
||||||
if (info.getErrorPacketType() == SarErrorTypeEnum.RESULT) {
|
if (info.getErrorPacketType() == SarErrorTypeEnum.RESULT) {
|
||||||
log.debug("收到回执包:" + ip);
|
log.debug("收到回执包:{}", ip);
|
||||||
redisUtil.set(CacheKey.getSarControlBack(ip), info, answerTimeout, TimeUnit.SECONDS);
|
sarCache.saveControlBack(ip, info); // 使用 SarCache,类型安全,自动短期缓存,自动 2s 过期
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 状态包
|
// 状态包
|
||||||
int deviceStatus = packDTO.getDevicePacketStatus();
|
int deviceStatus = packDTO.getDevicePacketStatus();
|
||||||
if (deviceStatus == 1) {
|
if (deviceStatus == 1) {
|
||||||
JmSarStatusDTO info = packDTO.getDeviceStatusInfo();
|
JmSarStatusDTO info = packDTO.getDeviceStatusInfo();
|
||||||
log.debug("sar开机状态:" + info.getIsBoot());
|
log.debug("sar开机状态:{}", info.getIsBoot());
|
||||||
sarJobStatusService.update(ip, info);
|
sarJobStatusService.update(ip, info);
|
||||||
redisUtil.set(CacheKey.getSarConnect(ip), info, answerTimeout, TimeUnit.SECONDS);
|
sarCache.saveStatus(ip, info); // 使用 SarCache,类型安全,自动短期缓存,自动过期
|
||||||
//System.out.println(info);
|
|
||||||
}
|
}
|
||||||
log.debug("----------------------状态包解析完毕");
|
log.debug("状态包解析完毕");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,16 +5,13 @@ import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
|||||||
import com.zhangy.skyeye.jm.dto.JmJobDTO;
|
import com.zhangy.skyeye.jm.dto.JmJobDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmJobPayload;
|
import com.zhangy.skyeye.jm.entity.JmJobPayload;
|
||||||
import com.zhangy.skyeye.jm.entity.JmJobUav;
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.publics.consts.CacheKey;
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import com.zhangy.skyeye.sar.control.SarControlContext;
|
import com.zhangy.skyeye.sar.control.SarControlContext;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
import com.zhangy.skyeye.sar.service.ISarControlService;
|
import com.zhangy.skyeye.sar.service.ISarControlService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -30,9 +27,7 @@ import static com.zhangy.skyeye.jm.consts.JmJobModeEnum.CRUISE;
|
|||||||
public class SarControlServiceImpl implements ISarControlService {
|
public class SarControlServiceImpl implements ISarControlService {
|
||||||
|
|
||||||
private final SarControlContext udpSendContext;
|
private final SarControlContext udpSendContext;
|
||||||
|
private final SarCache sarCache;
|
||||||
@Autowired
|
|
||||||
private RedisUtil redisUtil;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendUdp(JmJobDTO job) {
|
public void sendUdp(JmJobDTO job) {
|
||||||
@ -67,7 +62,6 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendUdp(SarControlParamDTO param) {
|
public void sendUdp(SarControlParamDTO param) {
|
||||||
udpSendContext.execute(param);
|
udpSendContext.execute(param);
|
||||||
@ -87,28 +81,30 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void turnOn(String ip) {
|
public void turnOn(String ip) {
|
||||||
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, ip) || !redisUtil.hasKey(CacheKey.getSarConnect(ip))) {
|
// 用领域缓存判断连接和状态
|
||||||
|
boolean connected = sarCache.isConnected(ip);
|
||||||
|
JmSarStatusDTO status = sarCache.getLatestStatus(ip);
|
||||||
|
if (!connected || status == null) {
|
||||||
throw new ServiceException("请先加电并连接sar");
|
throw new ServiceException("请先加电并连接sar");
|
||||||
}
|
}
|
||||||
|
|
||||||
SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.TURNON);
|
SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.TURNON);
|
||||||
udpSendContext.execute(param);
|
udpSendContext.execute(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endAll(String ip) {
|
public void endAll(String ip) {
|
||||||
if (!redisUtil.hHasKey(CacheKey.SAR_CONNECTED, ip) || !redisUtil.hasKey(CacheKey.getSarConnect(ip))) {
|
// 用领域缓存判断连接和状态
|
||||||
|
boolean connected = sarCache.isConnected(ip);
|
||||||
|
JmSarStatusDTO status = sarCache.getLatestStatus(ip);
|
||||||
|
if (!connected || status == null) {
|
||||||
throw new ServiceException("请先加电并连接sar");
|
throw new ServiceException("请先加电并连接sar");
|
||||||
}
|
}
|
||||||
|
|
||||||
SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.ENDALL);
|
SarControlParamDTO param = new SarControlParamDTO(ip, SarControlTypeEnum.ENDALL);
|
||||||
udpSendContext.execute(param);
|
udpSendContext.execute(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JmSarStatusDTO getLatestStatus(String ip) {
|
public JmSarStatusDTO getLatestStatus(String ip) {
|
||||||
String connectKey = CacheKey.getSarConnect(ip);
|
return sarCache.getLatestStatus(ip);
|
||||||
// 从缓存取载荷状态信息
|
|
||||||
return (JmSarStatusDTO) redisUtil.get(connectKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package com.zhangy.skyeye.sar.service.impl;
|
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.MathUtil;
|
||||||
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
|
||||||
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
import com.zhangy.skyeye.common.extend.util.ObjectUtil;
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
import com.zhangy.skyeye.jm.dto.JmAirlineStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmImageRotateDTO;
|
import com.zhangy.skyeye.jm.dto.JmImageRotateDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmUavStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmImage;
|
import com.zhangy.skyeye.jm.entity.JmImage;
|
||||||
@ -16,7 +16,6 @@ import com.zhangy.skyeye.publics.utils.OpenCVUtil;
|
|||||||
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
import com.zhangy.skyeye.sar.dto.SarBackImageFrameDTO;
|
||||||
import com.zhangy.skyeye.sar.service.ISarImageService;
|
import com.zhangy.skyeye.sar.service.ISarImageService;
|
||||||
import com.zhangy.skyeye.sar.service.SarWsAsyncService;
|
import com.zhangy.skyeye.sar.service.SarWsAsyncService;
|
||||||
import com.zhangy.skyeye.sar.util.RadarDisplayOptions;
|
|
||||||
import com.zhangy.skyeye.sar.util.SarImageToneAdjuster;
|
import com.zhangy.skyeye.sar.util.SarImageToneAdjuster;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -46,7 +45,7 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
private SarWsAsyncService sarWsAsyncService;
|
private SarWsAsyncService sarWsAsyncService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private ImageCache imageCache;
|
||||||
|
|
||||||
// 图片最大宽度,前端说和电脑有关,4096保险点,一般是4096 8192 16384
|
// 图片最大宽度,前端说和电脑有关,4096保险点,一般是4096 8192 16384
|
||||||
@Value("${skyeye.sar.image.max:4096}")
|
@Value("${skyeye.sar.image.max:4096}")
|
||||||
@ -56,43 +55,43 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
private final String CACHE_FIELD_START_FRAME_NO = "startFrameNo";
|
private final String CACHE_FIELD_START_FRAME_NO = "startFrameNo";
|
||||||
// 当前帧号
|
// 当前帧号
|
||||||
private final String CACHE_FIELD_CURR_FRAME_NO = "currFrameNo";
|
private final String CACHE_FIELD_CURR_FRAME_NO = "currFrameNo";
|
||||||
// 缓存超时(秒)
|
|
||||||
private final long CACHE_EXPIRE_SECOND = 24 * 3600;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取基准图像信息
|
* 获取基准图像信息
|
||||||
*
|
*
|
||||||
* @param airlineId 航线执行ID
|
* @param airlineId 航线执行ID
|
||||||
* @param singleWidth 单条图片宽度
|
* @param singleWidth 单条图片宽度
|
||||||
* @param frameNo 当前帧号
|
* @param frameNo 当前帧号
|
||||||
* @return 返回非空的图像信息,其字段 imageNo 一定有值
|
* @return 返回非空的图像信息,其字段 imageNo 一定有值
|
||||||
*/
|
*/
|
||||||
private JmImage getBaseImage(Long airlineId, int singleWidth, int frameNo) {IMG_MAX_WITH=1;
|
private JmImage getBaseImage(Long airlineId, int singleWidth, int frameNo) {
|
||||||
List<JmImage> imageList = imageService.selectLowByAirline(airlineId);
|
List<JmImage> imageList = imageService.selectLowByAirline(airlineId);
|
||||||
String cacheKey = "jmImgJoin-" + airlineId;
|
String cachePrefix = "jmImgJoin-" + airlineId;
|
||||||
JmImage base = null;
|
JmImage base;
|
||||||
// 情况1:航线第一张图
|
// 情况1:航线第一张图
|
||||||
if (ObjectUtil.isEmpty(imageList)) {
|
if (ObjectUtil.isEmpty(imageList)) {
|
||||||
base = new JmImage();
|
base = new JmImage();
|
||||||
base.setImageNo(1);
|
base.setImageNo(1);
|
||||||
redisUtil.hset(cacheKey, CACHE_FIELD_START_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);
|
// 存起始帧号
|
||||||
|
imageCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo);
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
// 情况2:如果最后一张还能拼图,则直接返回继续拼
|
// 情况2:如果最后一张还能拼图,则直接返回继续拼
|
||||||
JmImage last = imageList.get(imageList.size() - 1);
|
JmImage last = imageList.get(imageList.size() - 1);
|
||||||
Integer startFrameNo = (Integer) redisUtil.hget(cacheKey, CACHE_FIELD_START_FRAME_NO);
|
Integer startFrameNo = imageCache.get(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, Integer.class);
|
||||||
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1); // 图宽(当前图+基准图)
|
int currWidth = startFrameNo == null ? 0 : singleWidth * (frameNo - startFrameNo + 1);
|
||||||
int surplusNum = (IMG_MAX_WITH - currWidth) / singleWidth; // 还可以拼图片数
|
int surplusNum = (IMG_MAX_WITH - currWidth) / singleWidth; // 还可以拼图片数
|
||||||
Integer baseNo = (Integer) redisUtil.hget("jmImgJoin-" + airlineId, CACHE_FIELD_CURR_FRAME_NO);
|
Integer baseNo = imageCache.get(cachePrefix + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class);
|
||||||
|
|
||||||
if (startFrameNo == null || currWidth < IMG_MAX_WITH ||
|
if (startFrameNo == null || currWidth < IMG_MAX_WITH ||
|
||||||
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) { // 当前图+填充 不能超过允许拼接数
|
baseNo == null || (frameNo - baseNo + 1 <= surplusNum)) {
|
||||||
log.info("当前宽度:" + currWidth + " < " + IMG_MAX_WITH + " 可以继续拼接");
|
log.info("当前宽度:{} < {} 可以继续拼接", currWidth, IMG_MAX_WITH);
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
// 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件
|
// 情况3:已经拼接到最大数量,或者当前图+填充数量超过允许拼接数量,创建新图像文件
|
||||||
log.info("当前宽度:" + currWidth + " > " + IMG_MAX_WITH + " 重新拼接,当前帧号" + frameNo + "作为首帧");
|
log.info("当前宽度:{} > {} 重新拼接,当前帧号{}作为首帧", currWidth, IMG_MAX_WITH, frameNo);
|
||||||
base = new JmImage();
|
base = new JmImage();
|
||||||
redisUtil.hset(cacheKey, CACHE_FIELD_START_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);
|
imageCache.put(cachePrefix + ":" + CACHE_FIELD_START_FRAME_NO, frameNo);
|
||||||
base.setImageNo(last.getImageNo() + 1);
|
base.setImageNo(last.getImageNo() + 1);
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
@ -107,47 +106,12 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
}
|
}
|
||||||
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
||||||
if (!isFirst && !lostImage) {
|
if (!isFirst && !lostImage) {
|
||||||
/*imageFrame.setLon1(before[0]);
|
// 注释部分保持原样
|
||||||
imageFrame.setLat1(before[1]);
|
|
||||||
imageFrame.setLon4(before[2]);
|
|
||||||
imageFrame.setLat4(before[3]);*/
|
|
||||||
}
|
}
|
||||||
before[0] = imageFrame.getLon5();
|
before[0] = imageFrame.getLon5();
|
||||||
before[1] = imageFrame.getLat5();
|
before[1] = imageFrame.getLat5();
|
||||||
before[2] = imageFrame.getLon8();
|
before[2] = imageFrame.getLon8();
|
||||||
before[3] = imageFrame.getLat8();
|
before[3] = imageFrame.getLat8();
|
||||||
/*switch (rotateDTO.getType()) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
// 使用前一张图的右侧坐标作为后一张图的左侧,前提是没丢图
|
|
||||||
if (!isFirst && !lostImage) {
|
|
||||||
imageFrame.setLon1(before[0]);
|
|
||||||
imageFrame.setLat1(before[1]);
|
|
||||||
imageFrame.setLon4(before[2]);
|
|
||||||
imageFrame.setLat4(before[3]);
|
|
||||||
} else if (before == null) {
|
|
||||||
before = new Double[4];
|
|
||||||
currAirline.setBeforeRight(before);
|
|
||||||
}
|
|
||||||
before[0] = imageFrame.getLon5();
|
|
||||||
before[1] = imageFrame.getLat5();
|
|
||||||
before[2] = imageFrame.getLon8();
|
|
||||||
before[3] = imageFrame.getLat8();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
if (!isFirst && !lostImage) {
|
|
||||||
imageFrame.setLon5(before[0]);
|
|
||||||
imageFrame.setLat5(before[1]);
|
|
||||||
imageFrame.setLon8(before[2]);
|
|
||||||
imageFrame.setLat8(before[3]);
|
|
||||||
}
|
|
||||||
before[0] = imageFrame.getLon1();
|
|
||||||
before[1] = imageFrame.getLat1();
|
|
||||||
before[2] = imageFrame.getLon4();
|
|
||||||
before[3] = imageFrame.getLat4();
|
|
||||||
break;
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,8 +119,8 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
*
|
*
|
||||||
* @param sourceIp
|
* @param sourceIp
|
||||||
* @param airlineExecId
|
* @param airlineExecId
|
||||||
* @param frameData 图像帧数据
|
* @param frameData 图像帧数据
|
||||||
* @param imageFrame 图像帧
|
* @param imageFrame 图像帧
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public JmImage parseImage(String sourceIp, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
public JmImage parseImage(String sourceIp, Long airlineExecId, byte[] frameData, SarBackImageFrameDTO imageFrame) {
|
||||||
@ -187,28 +151,28 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3.保存图像png,用航线ID+序号命名
|
// 3.保存图像png,用航线ID+序号命名
|
||||||
JmImage base = getBaseImage(airlineExecId, currImage.width(), imageFrame.getFrameNo());
|
JmImage base = getBaseImage(airlineExecId, currImage.width(), frameNo);
|
||||||
String imageName = airlineExecId + "-" + base.getImageNo() +".png";
|
String imageName = airlineExecId + "-" + base.getImageNo() + ".png";
|
||||||
String[] imagePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, imageName);
|
String[] imagePath = sysFileTypeService.getFilePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId, imageName);
|
||||||
String currPath = imagePath[0];
|
String currPath = imagePath[0];
|
||||||
|
|
||||||
System.out.println("帧:" + frameNo);
|
System.out.println("帧:" + frameNo);
|
||||||
|
|
||||||
// 4.保存基准图(同步),用于下次拼接
|
// 4.保存基准图(同步),用于下次拼接
|
||||||
Integer baseNo = (Integer) redisUtil.hget("jmImgJoin-" + airlineExecId, CACHE_FIELD_CURR_FRAME_NO);
|
Integer baseNo = imageCache.get("jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, Integer.class);
|
||||||
boolean lostImage = baseNo != null && (frameNo - baseNo > 1); // 判断是否丢图
|
boolean lostImage = baseNo != null && (frameNo - baseNo > 1); // 判断是否丢图
|
||||||
String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId,
|
String basePath = sysFileTypeService.getAbsolutePath(FileTypeEnum.SAR_IMAGE_LOW, jobExecId,
|
||||||
airlineExecId + "-" + base.getImageNo() +"-base.png");
|
airlineExecId + "-" + base.getImageNo() + "-base.png");
|
||||||
Mat baseMat = generateBaseMat(base, currImage, frameNo, imageFrame.getMax(), basePath, baseNo);
|
Mat baseMat = generateBaseMat(base, currImage, frameNo, imageFrame.getMax(), basePath, baseNo);
|
||||||
if (baseMat == null) { // 拼接失败 或 基准图生成失败则跳过,按丢图处理
|
if (baseMat == null) { // 拼接失败 或 基准图生成失败则跳过,按丢图处理
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (lostImage) {
|
if (lostImage) {
|
||||||
log.warn("丢图"+(frameNo - baseNo)+"张!当前帧" + frameNo + ",前帧" + baseNo);
|
log.warn("丢图{}张!当前帧{},前帧{}", (frameNo - baseNo), frameNo, baseNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//modCoord(imageFrame, lostImage, currAirline);
|
//modCoord(imageFrame, lostImage, currAirline);
|
||||||
redisUtil.hset("jmImgJoin-" + airlineExecId, CACHE_FIELD_CURR_FRAME_NO, frameNo, CACHE_EXPIRE_SECOND);// 更新帧号
|
imageCache.put("jmImgJoin-" + airlineExecId + ":" + CACHE_FIELD_CURR_FRAME_NO, frameNo);
|
||||||
// ### 亮度调整,用于可靠udp版本图像,固定使用系数0.5
|
// ### 亮度调整,用于可靠udp版本图像,固定使用系数0.5
|
||||||
// 拆分多张图片,去掉自适应调整
|
// 拆分多张图片,去掉自适应调整
|
||||||
if (IMG_MAX_WITH > 20000) {
|
if (IMG_MAX_WITH > 20000) {
|
||||||
@ -224,15 +188,15 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
// 6.更新基准图坐标和帧号
|
// 6.更新基准图坐标和帧号
|
||||||
JmImage imageInfo = saveImage(uav, airlineExecId, base, imagePath, imageFrame, frameNo, lostImage);
|
JmImage imageInfo = saveImage(uav, airlineExecId, base, imagePath, imageFrame, frameNo, lostImage);
|
||||||
long end = System.currentTimeMillis();
|
long end = System.currentTimeMillis();
|
||||||
log.info("生成" + imageFrame.getImageBitDeep()+"位雷达回传图像:帧序号" + frameNo + "," +
|
log.info("生成{}位雷达回传图像:帧序号{},{},耗时{}秒",
|
||||||
imageInfo.getRelativePath() + ",耗时" + (end - start)/1000 + "秒");
|
imageFrame.getImageBitDeep(), frameNo, imageInfo.getRelativePath(), (end - start) / 1000);
|
||||||
return imageInfo;
|
return imageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载回传图像,先转置再转为Mat
|
* 加载回传图像,先转置再转为Mat
|
||||||
*
|
*
|
||||||
* @param frameData 图像帧数据,包含参数信息和未转置的8位灰度图像数据
|
* @param frameData 图像帧数据,包含参数信息和未转置的8位灰度图像数据
|
||||||
* @param imageFrame 图像帧对象
|
* @param imageFrame 图像帧对象
|
||||||
* @return 转置后的4通道图像Mat
|
* @return 转置后的4通道图像Mat
|
||||||
*/
|
*/
|
||||||
@ -265,10 +229,10 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
/**
|
/**
|
||||||
* 生成基准图,将原有基准图与新图拼接
|
* 生成基准图,将原有基准图与新图拼接
|
||||||
*
|
*
|
||||||
* @param base 基准图信息
|
* @param base 基准图信息
|
||||||
* @param currImage 当前图
|
* @param currImage 当前图
|
||||||
* @param currNo 当前图的帧号
|
* @param currNo 当前图的帧号
|
||||||
* @param currMax 当前图最大值
|
* @param currMax 当前图最大值
|
||||||
* @param imagePath 基准图路径
|
* @param imagePath 基准图路径
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -281,7 +245,7 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
if (baseMax < currMax) {
|
if (baseMax < currMax) {
|
||||||
OpenCVUtil.multiply(baseImage, baseMax / currMax);
|
OpenCVUtil.multiply(baseImage, baseMax / currMax);
|
||||||
} else if (baseMax > currMax) {
|
} else if (baseMax > currMax) {
|
||||||
OpenCVUtil.multiply(currImage , currMax / baseMax);
|
OpenCVUtil.multiply(currImage, currMax / baseMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo); // 会释放 currImage baseImage 资源
|
Mat baseMat = ImageUtil.join(baseImage, baseNo, currImage, currNo); // 会释放 currImage baseImage 资源
|
||||||
@ -295,9 +259,9 @@ public class SarImageServiceImpl implements ISarImageService {
|
|||||||
* 生成后处理图,将拼接好的基准图转置并调整对比度
|
* 生成后处理图,将拼接好的基准图转置并调整对比度
|
||||||
*
|
*
|
||||||
* @param currAirline 当前航线信息
|
* @param currAirline 当前航线信息
|
||||||
* @param baseMat 原基准图,处理后释放资源
|
* @param baseMat 原基准图,处理后释放资源
|
||||||
* @param imagePath 后处理图路径,每条航线对应一张图,每次生成会覆盖
|
* @param imagePath 后处理图路径,每条航线对应一张图,每次生成会覆盖
|
||||||
* @param imageLight 图像亮度倍数,0是不调整
|
* @param imageLight 图像亮度倍数,0是不调整
|
||||||
*/
|
*/
|
||||||
private void generateAfterMat(JmAirlineStatusDTO currAirline, Mat baseMat, String imagePath, int imageLight) {
|
private void generateAfterMat(JmAirlineStatusDTO currAirline, Mat baseMat, String imagePath, int imageLight) {
|
||||||
// 后处理参数
|
// 后处理参数
|
||||||
|
|||||||
@ -49,11 +49,11 @@ spring:
|
|||||||
date-format: yyyy-MM-dd'T'HH:mm:ss'Z'
|
date-format: yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||||
|
|
||||||
#redis 配置
|
#redis 配置
|
||||||
redis:
|
#redis:
|
||||||
database: 0
|
#database: 0
|
||||||
host: 127.0.0.1
|
#host: 127.0.0.1
|
||||||
port: 6379
|
#port: 6379
|
||||||
password: 'P@ssw0rd'
|
#password: 'P@ssw0rd'
|
||||||
|
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
mapper-locations: classpath*:mapping/**/*Mapping.xml
|
mapper-locations: classpath*:mapping/**/*Mapping.xml
|
||||||
|
|||||||
@ -22,10 +22,10 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- redis -->
|
<!-- redis -->
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>org.springframework.boot</groupId>
|
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
package com.zhangy.skyeye.redis.config;
|
|
||||||
|
|
||||||
import com.zhangy.skyeye.redis.utils.RedisUtil;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
|
||||||
import org.springframework.cache.CacheManager;
|
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
|
||||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
|
||||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
|
||||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
|
||||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis 配置
|
|
||||||
*/
|
|
||||||
@EnableCaching
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@AutoConfigureBefore(RedisAutoConfiguration.class)
|
|
||||||
public class DTRedisTemplateConfiguration {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* value 值 序列化
|
|
||||||
* @return RedisSerializer
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(RedisSerializer.class)
|
|
||||||
public RedisSerializer<Object> redisSerializer() {
|
|
||||||
return new JdkSerializationRedisSerializer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "redisTemplate")
|
|
||||||
@ConditionalOnMissingBean(RedisTemplate.class)
|
|
||||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory, RedisSerializer<Object> redisSerializer) {
|
|
||||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
|
||||||
RedisKeySerializer redisKeySerializer = new RedisKeySerializer();
|
|
||||||
// key 序列化
|
|
||||||
redisTemplate.setKeySerializer(redisKeySerializer);
|
|
||||||
redisTemplate.setHashKeySerializer(redisKeySerializer);
|
|
||||||
// value 序列化
|
|
||||||
redisTemplate.setValueSerializer(redisSerializer);
|
|
||||||
redisTemplate.setHashValueSerializer(redisSerializer);
|
|
||||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
|
||||||
return redisTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
|
|
||||||
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
|
|
||||||
.entryTtl(Duration.ofHours(1));
|
|
||||||
return RedisCacheManager
|
|
||||||
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
|
|
||||||
.cacheDefaults(redisCacheConfiguration).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "redisUtil")
|
|
||||||
@ConditionalOnBean(RedisTemplate.class)
|
|
||||||
public RedisUtil redisUtils(RedisTemplate<String, Object> redisTemplate) {
|
|
||||||
return new RedisUtil(redisTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,55 +1,55 @@
|
|||||||
package com.zhangy.skyeye.redis.config;
|
//package com.zhangy.skyeye.redis.config;
|
||||||
|
//
|
||||||
import org.springframework.cache.interceptor.SimpleKey;
|
//import org.springframework.cache.interceptor.SimpleKey;
|
||||||
import org.springframework.core.convert.ConversionService;
|
//import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
//import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
//import org.springframework.data.redis.serializer.RedisSerializer;
|
||||||
import org.springframework.lang.Nullable;
|
//import org.springframework.lang.Nullable;
|
||||||
|
//
|
||||||
import java.nio.charset.Charset;
|
//import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
//import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Objects;
|
//import java.util.Objects;
|
||||||
|
//
|
||||||
/**
|
///**
|
||||||
* 将redis key序列化为字符串
|
// * 将redis key序列化为字符串
|
||||||
* spring cache中的简单基本类型直接使用 StringRedisSerializer 会有问题
|
// * spring cache中的简单基本类型直接使用 StringRedisSerializer 会有问题
|
||||||
*/
|
// */
|
||||||
public class RedisKeySerializer implements RedisSerializer<Object> {
|
//public class RedisKeySerializer implements RedisSerializer<Object> {
|
||||||
private final Charset charset;
|
// private final Charset charset;
|
||||||
private final ConversionService converter;
|
// private final ConversionService converter;
|
||||||
|
//
|
||||||
public RedisKeySerializer() {
|
// public RedisKeySerializer() {
|
||||||
this(StandardCharsets.UTF_8);
|
// this(StandardCharsets.UTF_8);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public RedisKeySerializer(Charset charset) {
|
// public RedisKeySerializer(Charset charset) {
|
||||||
Objects.requireNonNull(charset, "Charset must not be null");
|
// Objects.requireNonNull(charset, "Charset must not be null");
|
||||||
this.charset = charset;
|
// this.charset = charset;
|
||||||
this.converter = DefaultConversionService.getSharedInstance();
|
// this.converter = DefaultConversionService.getSharedInstance();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
public Object deserialize(byte[] bytes) {
|
// public Object deserialize(byte[] bytes) {
|
||||||
// redis keys 会用到反序列化
|
// // redis keys 会用到反序列化
|
||||||
if (bytes == null) {
|
// if (bytes == null) {
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
return new String(bytes, charset);
|
// return new String(bytes, charset);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Override
|
// @Override
|
||||||
@Nullable
|
// @Nullable
|
||||||
public byte[] serialize(Object object) {
|
// public byte[] serialize(Object object) {
|
||||||
Objects.requireNonNull(object, "redis key is null");
|
// Objects.requireNonNull(object, "redis key is null");
|
||||||
String key;
|
// String key;
|
||||||
if (object instanceof SimpleKey) {
|
// if (object instanceof SimpleKey) {
|
||||||
key = "";
|
// key = "";
|
||||||
} else if (object instanceof String) {
|
// } else if (object instanceof String) {
|
||||||
key = (String) object;
|
// key = (String) object;
|
||||||
} else {
|
// } else {
|
||||||
key = converter.convert(object, String.class);
|
// key = converter.convert(object, String.class);
|
||||||
}
|
// }
|
||||||
return key.getBytes(this.charset);
|
// return key.getBytes(this.charset);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,2 +1 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
|
||||||
com.zhangy.skyeye.redis.config.DTRedisTemplateConfiguration
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user