refactor(接口命名): 统一接口命名规范并重构相关实现
将接口命名统一调整为以I开头,并重构相关实现类和服务调用 重构审计日志和网关路由服务接口,优化代码结构 删除旧接口文件,更新依赖接口的类
This commit is contained in:
+2
-2
@@ -35,9 +35,9 @@ public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
private final ExceptionLogService exceptionLogService;
|
||||
private final IExceptionLogService exceptionLogService;
|
||||
|
||||
public GlobalExceptionHandler(ExceptionLogService exceptionLogService) {
|
||||
public GlobalExceptionHandler(IExceptionLogService exceptionLogService) {
|
||||
this.exceptionLogService = exceptionLogService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -10,9 +10,9 @@ import reactor.core.publisher.Mono;
|
||||
* 算法:使用响应式编程实现异步日志记录
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
public interface ExceptionLogService {
|
||||
public interface IExceptionLogService {
|
||||
Mono<Void> logException(String title, String exceptionName, String exceptionMsg,
|
||||
String methodName, String ip, String stackTrace);
|
||||
}
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
package cn.novalon.manage.gateway.discovery;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 服务发现服务
|
||||
*
|
||||
* 文件定义:实现服务实例的发现、监控和管理
|
||||
* 涉及业务:服务实例查询、健康检查、服务状态监控
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 服务实例查询
|
||||
* 2. 服务健康检查
|
||||
* 3. 服务状态监控
|
||||
* 4. 服务实例缓存
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
*/
|
||||
@Service
|
||||
public class ServiceDiscoveryService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryService.class);
|
||||
|
||||
private final ReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
private final DiscoveryClient discoveryClient;
|
||||
|
||||
private final Map<String, List<ServiceInstance>> serviceCache = new ConcurrentHashMap<>();
|
||||
private final Map<String, Long> lastUpdateTime = new ConcurrentHashMap<>();
|
||||
|
||||
private static final long CACHE_TTL_MS = 30000;
|
||||
|
||||
public ServiceDiscoveryService(
|
||||
ReactiveDiscoveryClient reactiveDiscoveryClient,
|
||||
DiscoveryClient discoveryClient) {
|
||||
this.reactiveDiscoveryClient = reactiveDiscoveryClient;
|
||||
this.discoveryClient = discoveryClient;
|
||||
|
||||
initializeServiceCache();
|
||||
}
|
||||
|
||||
private void initializeServiceCache() {
|
||||
logger.info("Initializing service cache");
|
||||
|
||||
discoveryClient.getServices().forEach(serviceId -> {
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
|
||||
if (!instances.isEmpty()) {
|
||||
serviceCache.put(serviceId, instances);
|
||||
lastUpdateTime.put(serviceId, System.currentTimeMillis());
|
||||
logger.debug("Cached {} instances for service: {}", instances.size(), serviceId);
|
||||
}
|
||||
});
|
||||
|
||||
logger.info("Service cache initialized with {} services", serviceCache.size());
|
||||
}
|
||||
|
||||
public Flux<ServiceInstance> getInstances(String serviceId) {
|
||||
if (serviceId == null || serviceId.isEmpty()) {
|
||||
logger.warn("Service ID is null or empty");
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
if (isCacheValid(serviceId)) {
|
||||
List<ServiceInstance> cachedInstances = serviceCache.get(serviceId);
|
||||
if (cachedInstances != null && !cachedInstances.isEmpty()) {
|
||||
logger.debug("Returning {} cached instances for service: {}",
|
||||
cachedInstances.size(), serviceId);
|
||||
return Flux.fromIterable(cachedInstances);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Fetching instances for service: {}", serviceId);
|
||||
|
||||
return reactiveDiscoveryClient.getInstances(serviceId)
|
||||
.doOnNext(instance -> logger.debug("Found instance: {}:{} for service: {}",
|
||||
instance.getHost(), instance.getPort(), serviceId))
|
||||
.collectList()
|
||||
.doOnNext(instances -> {
|
||||
serviceCache.put(serviceId, instances);
|
||||
lastUpdateTime.put(serviceId, System.currentTimeMillis());
|
||||
logger.info("Updated cache with {} instances for service: {}",
|
||||
instances.size(), serviceId);
|
||||
})
|
||||
.flatMapMany(Flux::fromIterable);
|
||||
}
|
||||
|
||||
public Flux<String> getServices() {
|
||||
return reactiveDiscoveryClient.getServices()
|
||||
.doOnNext(serviceId -> logger.debug("Found service: {}", serviceId));
|
||||
}
|
||||
|
||||
public Mono<ServiceInstance> getFirstInstance(String serviceId) {
|
||||
return getInstances(serviceId)
|
||||
.next()
|
||||
.doOnNext(instance -> logger.debug("Returning first instance for service: {}", serviceId));
|
||||
}
|
||||
|
||||
public Mono<ServiceInstance> getInstanceByHost(String serviceId, String host) {
|
||||
if (host == null || host.isEmpty()) {
|
||||
logger.warn("Host is null or empty");
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return getInstances(serviceId)
|
||||
.filter(instance -> host.equals(instance.getHost()))
|
||||
.next()
|
||||
.doOnNext(instance -> logger.debug("Found instance with host {} for service: {}",
|
||||
host, serviceId));
|
||||
}
|
||||
|
||||
public Mono<ServiceInstance> getInstanceByPort(String serviceId, int port) {
|
||||
if (port <= 0) {
|
||||
logger.warn("Invalid port: {}", port);
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return getInstances(serviceId)
|
||||
.filter(instance -> port == instance.getPort())
|
||||
.next()
|
||||
.doOnNext(instance -> logger.debug("Found instance with port {} for service: {}",
|
||||
port, serviceId));
|
||||
}
|
||||
|
||||
public Mono<Map<String, List<ServiceInstance>>> getAllServicesWithInstances() {
|
||||
return getServices()
|
||||
.flatMap(serviceId ->
|
||||
getInstances(serviceId)
|
||||
.collectList()
|
||||
.map(instances -> Map.entry(serviceId, instances))
|
||||
)
|
||||
.collectMap(Map.Entry::getKey, Map.Entry::getValue);
|
||||
}
|
||||
|
||||
public Mono<Integer> getInstanceCount(String serviceId) {
|
||||
return getInstances(serviceId)
|
||||
.count()
|
||||
.map(Long::intValue);
|
||||
}
|
||||
|
||||
public Mono<Boolean> isServiceAvailable(String serviceId) {
|
||||
return getInstanceCount(serviceId)
|
||||
.map(count -> count > 0)
|
||||
.doOnNext(available -> logger.debug("Service {} availability: {}",
|
||||
serviceId, available));
|
||||
}
|
||||
|
||||
public void refreshServiceCache(String serviceId) {
|
||||
if (serviceId == null || serviceId.isEmpty()) {
|
||||
logger.warn("Service ID is null or empty");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Refreshing cache for service: {}", serviceId);
|
||||
|
||||
reactiveDiscoveryClient.getInstances(serviceId)
|
||||
.collectList()
|
||||
.subscribe(
|
||||
instances -> {
|
||||
serviceCache.put(serviceId, instances);
|
||||
lastUpdateTime.put(serviceId, System.currentTimeMillis());
|
||||
logger.info("Refreshed cache with {} instances for service: {}",
|
||||
instances.size(), serviceId);
|
||||
},
|
||||
error -> logger.error("Failed to refresh cache for service: {}",
|
||||
serviceId, error)
|
||||
);
|
||||
}
|
||||
|
||||
public void refreshAllServices() {
|
||||
logger.info("Refreshing cache for all services");
|
||||
|
||||
reactiveDiscoveryClient.getServices()
|
||||
.flatMap(serviceId ->
|
||||
reactiveDiscoveryClient.getInstances(serviceId)
|
||||
.collectList()
|
||||
.doOnNext(instances -> {
|
||||
serviceCache.put(serviceId, instances);
|
||||
lastUpdateTime.put(serviceId, System.currentTimeMillis());
|
||||
})
|
||||
)
|
||||
.subscribe(
|
||||
instances -> logger.debug("Refreshed {} instances", instances.size()),
|
||||
error -> logger.error("Failed to refresh all services", error),
|
||||
() -> logger.info("All services cache refreshed")
|
||||
);
|
||||
}
|
||||
|
||||
public void clearServiceCache() {
|
||||
logger.info("Clearing service cache");
|
||||
serviceCache.clear();
|
||||
lastUpdateTime.clear();
|
||||
initializeServiceCache();
|
||||
}
|
||||
|
||||
private boolean isCacheValid(String serviceId) {
|
||||
Long lastUpdate = lastUpdateTime.get(serviceId);
|
||||
if (lastUpdate == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
return (currentTime - lastUpdate) < CACHE_TTL_MS;
|
||||
}
|
||||
|
||||
public int getCachedServiceCount() {
|
||||
return serviceCache.size();
|
||||
}
|
||||
|
||||
public int getCachedInstanceCount(String serviceId) {
|
||||
List<ServiceInstance> instances = serviceCache.get(serviceId);
|
||||
return instances != null ? instances.size() : 0;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.novalon.manage.gateway.service;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 配置刷新服务接口
|
||||
*
|
||||
* 文件定义:定义网关配置动态刷新接口
|
||||
* 涉及业务:配置热更新、配置版本管理
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
public interface IConfigRefreshService {
|
||||
|
||||
Mono<Void> refreshGatewayConfig();
|
||||
|
||||
Mono<Void> refreshRouteConfig();
|
||||
|
||||
Mono<Void> refreshFilterConfig();
|
||||
|
||||
Mono<String> getCurrentConfigVersion();
|
||||
|
||||
Mono<Boolean> isConfigChanged();
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package cn.novalon.manage.gateway.service;
|
||||
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 动态路由服务接口
|
||||
*
|
||||
* 文件定义:定义网关路由的动态配置和管理接口
|
||||
* 涉及业务:路由增删改查、路由刷新、路由缓存管理
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 动态添加路由
|
||||
* 2. 动态删除路由
|
||||
* 3. 动态更新路由
|
||||
* 4. 路由列表查询
|
||||
* 5. 路由刷新
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
public interface IDynamicRouteService {
|
||||
|
||||
Mono<Boolean> addRoute(RouteDefinition routeDefinition);
|
||||
|
||||
Mono<Boolean> updateRoute(RouteDefinition routeDefinition);
|
||||
|
||||
Mono<Boolean> deleteRoute(String routeId);
|
||||
|
||||
Flux<RouteDefinition> getRoutes();
|
||||
|
||||
Mono<RouteDefinition> getRoute(String routeId);
|
||||
|
||||
Mono<Void> refreshRoutes();
|
||||
|
||||
Mono<Long> getRouteCount();
|
||||
|
||||
Mono<Boolean> routeExists(String routeId);
|
||||
|
||||
Mono<Void> clearRouteCache();
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.novalon.manage.gateway.service;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 请求缓存服务接口
|
||||
*
|
||||
* 文件定义:定义请求缓存管理接口
|
||||
* 涉及业务:请求缓存、缓存清理、缓存统计
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
public interface IRequestCacheService {
|
||||
|
||||
Mono<Void> cacheRequest(String requestId, Object requestData);
|
||||
|
||||
Mono<Object> getCachedRequest(String requestId);
|
||||
|
||||
Mono<Boolean> removeCachedRequest(String requestId);
|
||||
|
||||
Mono<Void> clearExpiredCache();
|
||||
|
||||
Mono<Long> getCacheSize();
|
||||
|
||||
Mono<Boolean> isRequestCached(String requestId);
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cn.novalon.manage.gateway.service;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 服务发现服务接口
|
||||
*
|
||||
* 文件定义:定义服务实例的发现、监控和管理接口
|
||||
* 涉及业务:服务实例查询、健康检查、服务状态监控
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 服务实例查询
|
||||
* 2. 服务健康检查
|
||||
* 3. 服务状态监控
|
||||
* 4. 服务实例缓存
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
public interface IServiceDiscoveryService {
|
||||
|
||||
Flux<ServiceInstance> getInstances(String serviceId);
|
||||
|
||||
Flux<String> getServices();
|
||||
|
||||
Mono<Boolean> isServiceHealthy(String serviceId);
|
||||
|
||||
Mono<Long> getInstanceCount(String serviceId);
|
||||
|
||||
Mono<Void> refreshServiceCache(String serviceId);
|
||||
|
||||
Mono<Void> refreshAllServiceCache();
|
||||
|
||||
Mono<Long> getServiceCount();
|
||||
|
||||
Mono<Boolean> serviceExists(String serviceId);
|
||||
|
||||
Mono<Void> clearServiceCache();
|
||||
}
|
||||
+52
-65
@@ -1,5 +1,6 @@
|
||||
package cn.novalon.manage.gateway.route;
|
||||
package cn.novalon.manage.gateway.service.impl;
|
||||
|
||||
import cn.novalon.manage.gateway.service.IDynamicRouteService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
@@ -11,12 +12,11 @@ import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 动态路由服务
|
||||
* 动态路由服务实现类
|
||||
*
|
||||
* 文件定义:实现网关路由的动态配置和管理
|
||||
* 涉及业务:路由增删改查、路由刷新、路由缓存管理
|
||||
@@ -29,10 +29,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
* 5. 路由刷新
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-26
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
@Service
|
||||
public class DynamicRouteService {
|
||||
public class DynamicRouteService implements IDynamicRouteService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DynamicRouteService.class);
|
||||
|
||||
@@ -63,6 +63,7 @@ public class DynamicRouteService {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> addRoute(RouteDefinition routeDefinition) {
|
||||
if (routeDefinition == null || routeDefinition.getId() == null) {
|
||||
logger.error("Invalid route definition: route or route ID is null");
|
||||
@@ -85,6 +86,7 @@ public class DynamicRouteService {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> updateRoute(RouteDefinition routeDefinition) {
|
||||
if (routeDefinition == null || routeDefinition.getId() == null) {
|
||||
logger.error("Invalid route definition: route or route ID is null");
|
||||
@@ -100,18 +102,29 @@ public class DynamicRouteService {
|
||||
|
||||
logger.info("Updating route: {}", routeId);
|
||||
|
||||
return deleteRoute(routeId)
|
||||
.flatMap(success -> {
|
||||
if (success) {
|
||||
return addRoute(routeDefinition);
|
||||
}
|
||||
return routeDefinitionWriter.delete(Mono.just(routeId))
|
||||
.then(routeDefinitionWriter.save(Mono.just(routeDefinition)))
|
||||
.then(Mono.fromRunnable(() -> {
|
||||
routeCache.put(routeId, routeDefinition);
|
||||
refreshRoutes();
|
||||
logger.info("Route updated successfully: {}", routeId);
|
||||
}))
|
||||
.thenReturn(true)
|
||||
.onErrorResume(error -> {
|
||||
logger.error("Failed to update route: {}", routeId, error);
|
||||
return Mono.just(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> deleteRoute(String routeId) {
|
||||
if (routeId == null || routeId.isEmpty()) {
|
||||
logger.error("Invalid route ID: route ID is null or empty");
|
||||
if (routeId == null) {
|
||||
logger.error("Invalid route ID: null");
|
||||
return Mono.just(false);
|
||||
}
|
||||
|
||||
if (!routeCache.containsKey(routeId)) {
|
||||
logger.warn("Route not found for deletion: {}", routeId);
|
||||
return Mono.just(false);
|
||||
}
|
||||
|
||||
@@ -130,71 +143,45 @@ public class DynamicRouteService {
|
||||
});
|
||||
}
|
||||
|
||||
public Flux<RouteDefinition> getAllRoutes() {
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRoutes() {
|
||||
return Flux.fromIterable(routeCache.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<RouteDefinition> getRoute(String routeId) {
|
||||
if (routeId == null || routeId.isEmpty()) {
|
||||
if (routeId == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
RouteDefinition route = routeCache.get(routeId);
|
||||
return route != null ? Mono.just(route) : Mono.empty();
|
||||
return Mono.justOrEmpty(routeCache.get(routeId));
|
||||
}
|
||||
|
||||
public void refreshRoutes() {
|
||||
logger.info("Refreshing routes");
|
||||
publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||
@Override
|
||||
public Mono<Void> refreshRoutes() {
|
||||
return Mono.fromRunnable(() -> {
|
||||
publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||
logger.info("Routes refreshed");
|
||||
});
|
||||
}
|
||||
|
||||
public Mono<Boolean> batchAddRoutes(List<RouteDefinition> routeDefinitions) {
|
||||
if (routeDefinitions == null || routeDefinitions.isEmpty()) {
|
||||
logger.warn("No routes to add");
|
||||
@Override
|
||||
public Mono<Long> getRouteCount() {
|
||||
return Mono.just((long) routeCache.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> routeExists(String routeId) {
|
||||
if (routeId == null) {
|
||||
return Mono.just(false);
|
||||
}
|
||||
|
||||
logger.info("Batch adding {} routes", routeDefinitions.size());
|
||||
|
||||
return Flux.fromIterable(routeDefinitions)
|
||||
.flatMap(this::addRoute)
|
||||
.all(success -> success)
|
||||
.doOnSuccess(allSuccess -> {
|
||||
if (allSuccess) {
|
||||
logger.info("All routes added successfully");
|
||||
} else {
|
||||
logger.warn("Some routes failed to add");
|
||||
}
|
||||
});
|
||||
return Mono.just(routeCache.containsKey(routeId));
|
||||
}
|
||||
|
||||
public Mono<Boolean> batchDeleteRoutes(List<String> routeIds) {
|
||||
if (routeIds == null || routeIds.isEmpty()) {
|
||||
logger.warn("No routes to delete");
|
||||
return Mono.just(false);
|
||||
}
|
||||
|
||||
logger.info("Batch deleting {} routes", routeIds.size());
|
||||
|
||||
return Flux.fromIterable(routeIds)
|
||||
.flatMap(this::deleteRoute)
|
||||
.all(success -> success)
|
||||
.doOnSuccess(allSuccess -> {
|
||||
if (allSuccess) {
|
||||
logger.info("All routes deleted successfully");
|
||||
} else {
|
||||
logger.warn("Some routes failed to delete");
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public Mono<Void> clearRouteCache() {
|
||||
return Mono.fromRunnable(() -> {
|
||||
routeCache.clear();
|
||||
logger.info("Route cache cleared");
|
||||
});
|
||||
}
|
||||
|
||||
public int getRouteCount() {
|
||||
return routeCache.size();
|
||||
}
|
||||
|
||||
public void clearRouteCache() {
|
||||
logger.info("Clearing route cache");
|
||||
routeCache.clear();
|
||||
initializeRouteCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package cn.novalon.manage.gateway.service.impl;
|
||||
|
||||
import cn.novalon.manage.gateway.service.IServiceDiscoveryService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 服务发现服务实现类
|
||||
*
|
||||
* 文件定义:实现服务实例的发现、监控和管理
|
||||
* 涉及业务:服务实例查询、健康检查、服务状态监控
|
||||
*
|
||||
* 核心功能:
|
||||
* 1. 服务实例查询
|
||||
* 2. 服务健康检查
|
||||
* 3. 服务状态监控
|
||||
* 4. 服务实例缓存
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
@Service
|
||||
public class ServiceDiscoveryService implements IServiceDiscoveryService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryService.class);
|
||||
|
||||
private final ReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
private final DiscoveryClient discoveryClient;
|
||||
|
||||
private final Map<String, List<ServiceInstance>> serviceCache = new ConcurrentHashMap<>();
|
||||
private final Map<String, Long> lastUpdateTime = new ConcurrentHashMap<>();
|
||||
|
||||
private static final long CACHE_TTL_MS = 30000;
|
||||
|
||||
public ServiceDiscoveryService(
|
||||
ReactiveDiscoveryClient reactiveDiscoveryClient,
|
||||
DiscoveryClient discoveryClient) {
|
||||
this.reactiveDiscoveryClient = reactiveDiscoveryClient;
|
||||
this.discoveryClient = discoveryClient;
|
||||
|
||||
initializeServiceCache();
|
||||
}
|
||||
|
||||
private void initializeServiceCache() {
|
||||
logger.info("Initializing service cache");
|
||||
|
||||
discoveryClient.getServices().forEach(serviceId -> {
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
|
||||
if (!instances.isEmpty()) {
|
||||
serviceCache.put(serviceId, instances);
|
||||
lastUpdateTime.put(serviceId, System.currentTimeMillis());
|
||||
logger.debug("Cached {} instances for service: {}", instances.size(), serviceId);
|
||||
}
|
||||
});
|
||||
|
||||
logger.info("Service cache initialized with {} services", serviceCache.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ServiceInstance> getInstances(String serviceId) {
|
||||
if (serviceId == null || serviceId.isEmpty()) {
|
||||
logger.warn("Service ID is null or empty");
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
if (isCacheValid(serviceId)) {
|
||||
List<ServiceInstance> cachedInstances = serviceCache.get(serviceId);
|
||||
if (cachedInstances != null && !cachedInstances.isEmpty()) {
|
||||
logger.debug("Returning {} cached instances for service: {}",
|
||||
cachedInstances.size(), serviceId);
|
||||
return Flux.fromIterable(cachedInstances);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Fetching instances for service: {}", serviceId);
|
||||
|
||||
return reactiveDiscoveryClient.getInstances(serviceId)
|
||||
.doOnNext(instance -> logger.debug("Found instance: {}:{} for service: {}",
|
||||
instance.getHost(), instance.getPort(), serviceId))
|
||||
.collectList()
|
||||
.doOnNext(instances -> {
|
||||
serviceCache.put(serviceId, instances);
|
||||
lastUpdateTime.put(serviceId, System.currentTimeMillis());
|
||||
logger.info("Updated cache with {} instances for service: {}",
|
||||
instances.size(), serviceId);
|
||||
})
|
||||
.flatMapMany(Flux::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> getServices() {
|
||||
return reactiveDiscoveryClient.getServices()
|
||||
.doOnNext(serviceId -> logger.debug("Found service: {}", serviceId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> isServiceHealthy(String serviceId) {
|
||||
return getInstances(serviceId)
|
||||
.hasElements()
|
||||
.map(hasInstances -> {
|
||||
if (hasInstances) {
|
||||
logger.debug("Service {} is healthy - has instances", serviceId);
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("Service {} is unhealthy - no instances found", serviceId);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> getInstanceCount(String serviceId) {
|
||||
return getInstances(serviceId)
|
||||
.count()
|
||||
.doOnNext(count -> logger.debug("Service {} has {} instances", serviceId, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> refreshServiceCache(String serviceId) {
|
||||
return Mono.fromRunnable(() -> {
|
||||
if (serviceId != null) {
|
||||
serviceCache.remove(serviceId);
|
||||
lastUpdateTime.remove(serviceId);
|
||||
logger.info("Refreshed cache for service: {}", serviceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> refreshAllServiceCache() {
|
||||
return Mono.fromRunnable(() -> {
|
||||
serviceCache.clear();
|
||||
lastUpdateTime.clear();
|
||||
initializeServiceCache();
|
||||
logger.info("Refreshed all service cache");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> getServiceCount() {
|
||||
return getServices()
|
||||
.count()
|
||||
.doOnNext(count -> logger.debug("Found {} services", count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> serviceExists(String serviceId) {
|
||||
if (serviceId == null || serviceId.isEmpty()) {
|
||||
return Mono.just(false);
|
||||
}
|
||||
return getServices()
|
||||
.any(s -> s.equals(serviceId))
|
||||
.doOnNext(exists -> logger.debug("Service {} exists: {}", serviceId, exists));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> clearServiceCache() {
|
||||
return Mono.fromRunnable(() -> {
|
||||
serviceCache.clear();
|
||||
lastUpdateTime.clear();
|
||||
logger.info("Cleared service cache");
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isCacheValid(String serviceId) {
|
||||
Long lastUpdate = lastUpdateTime.get(serviceId);
|
||||
if (lastUpdate == null) {
|
||||
return false;
|
||||
}
|
||||
return System.currentTimeMillis() - lastUpdate < CACHE_TTL_MS;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -3,7 +3,7 @@ package cn.novalon.manage.sys.audit.controller;
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.dto.AuditLogQueryRequest;
|
||||
import cn.novalon.manage.sys.audit.dto.AuditLogStatistics;
|
||||
import cn.novalon.manage.sys.audit.service.AuditLogService;
|
||||
import cn.novalon.manage.sys.audit.service.IAuditLogService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -29,9 +29,9 @@ import java.time.LocalDateTime;
|
||||
@Tag(name = "审计日志", description = "审计日志查询和统计接口")
|
||||
public class AuditLogController {
|
||||
|
||||
private final AuditLogService auditLogService;
|
||||
private final IAuditLogService auditLogService;
|
||||
|
||||
public AuditLogController(AuditLogService auditLogService) {
|
||||
public AuditLogController(IAuditLogService auditLogService) {
|
||||
this.auditLogService = auditLogService;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
package cn.novalon.manage.sys.audit.scheduler;
|
||||
|
||||
import cn.novalon.manage.sys.audit.service.AuditLogArchiveService;
|
||||
import cn.novalon.manage.sys.audit.service.IAuditLogArchiveService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -21,9 +21,9 @@ public class AuditLogArchiveScheduler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuditLogArchiveScheduler.class);
|
||||
|
||||
private final AuditLogArchiveService auditLogArchiveService;
|
||||
private final IAuditLogArchiveService auditLogArchiveService;
|
||||
|
||||
public AuditLogArchiveScheduler(AuditLogArchiveService auditLogArchiveService) {
|
||||
public AuditLogArchiveScheduler(IAuditLogArchiveService auditLogArchiveService) {
|
||||
this.auditLogArchiveService = auditLogArchiveService;
|
||||
}
|
||||
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
package cn.novalon.manage.sys.audit.service;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLogArchive;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogArchiveRepository;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审计日志归档服务
|
||||
*
|
||||
* 文件定义:封装审计日志归档的业务逻辑
|
||||
* 涉及业务:审计日志的归档、查询、清理等操作
|
||||
* 算法:定期将历史审计日志移动到归档表
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-01
|
||||
*/
|
||||
@Service
|
||||
public class AuditLogArchiveService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuditLogArchiveService.class);
|
||||
|
||||
private final IAuditLogRepository auditLogRepository;
|
||||
private final IAuditLogArchiveRepository auditLogArchiveRepository;
|
||||
|
||||
public AuditLogArchiveService(IAuditLogRepository auditLogRepository,
|
||||
IAuditLogArchiveRepository auditLogArchiveRepository) {
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.auditLogArchiveRepository = auditLogArchiveRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Mono<Long> archiveOldLogs(int daysToKeep) {
|
||||
LocalDateTime archiveBefore = LocalDateTime.now().minusDays(daysToKeep);
|
||||
|
||||
logger.info("开始归档审计日志,归档时间点: {}", archiveBefore);
|
||||
|
||||
return auditLogRepository.findByOperationTimeBetween(
|
||||
LocalDateTime.MIN,
|
||||
archiveBefore
|
||||
)
|
||||
.flatMap(this::archiveLog)
|
||||
.count()
|
||||
.doOnSuccess(count -> logger.info("审计日志归档完成,共归档 {} 条记录", count))
|
||||
.doOnError(error -> logger.error("审计日志归档失败: {}", error.getMessage()));
|
||||
}
|
||||
|
||||
private Mono<Void> archiveLog(AuditLog auditLog) {
|
||||
AuditLogArchive archive = new AuditLogArchive();
|
||||
archive.setEntityType(auditLog.getEntityType());
|
||||
archive.setEntityId(auditLog.getEntityId());
|
||||
archive.setOperationType(auditLog.getOperationType());
|
||||
archive.setOperator(auditLog.getOperator());
|
||||
archive.setOperationTime(auditLog.getOperationTime());
|
||||
archive.setBeforeData(auditLog.getBeforeData());
|
||||
archive.setAfterData(auditLog.getAfterData());
|
||||
archive.setChangedFields(auditLog.getChangedFields());
|
||||
archive.setIpAddress(auditLog.getIpAddress());
|
||||
archive.setUserAgent(auditLog.getUserAgent());
|
||||
archive.setDescription(auditLog.getDescription());
|
||||
archive.setCreatedAt(auditLog.getCreatedAt());
|
||||
archive.setArchivedAt(LocalDateTime.now());
|
||||
|
||||
return auditLogArchiveRepository.save(archive)
|
||||
.flatMap(savedArchive -> auditLogRepository.deleteById(auditLog.getId()))
|
||||
.doOnSuccess(v -> logger.debug("归档审计日志成功: ID={}", auditLog.getId()))
|
||||
.doOnError(error -> logger.error("归档审计日志失败: ID={}, 错误: {}",
|
||||
auditLog.getId(), error.getMessage()))
|
||||
.then();
|
||||
}
|
||||
|
||||
public Flux<AuditLogArchive> findArchivedLogs(String entityType, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
if (entityType != null) {
|
||||
return auditLogArchiveRepository.findByEntityType(entityType);
|
||||
} else if (startTime != null && endTime != null) {
|
||||
return auditLogArchiveRepository.findByArchivedAtBetween(startTime, endTime);
|
||||
}
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
public Mono<Long> countArchivedLogs(String entityType) {
|
||||
if (entityType != null) {
|
||||
return auditLogArchiveRepository.countByEntityType(entityType);
|
||||
}
|
||||
return auditLogArchiveRepository.count();
|
||||
}
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package cn.novalon.manage.sys.audit.service;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 审计日志服务
|
||||
*
|
||||
* 文件定义:封装审计日志的业务逻辑
|
||||
* 涉及业务:审计日志的保存、查询、统计等操作
|
||||
* 算法:使用异步线程池处理审计日志,不阻塞主流程
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-01
|
||||
*/
|
||||
@Service
|
||||
public class AuditLogService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuditLogService.class);
|
||||
|
||||
private final IAuditLogRepository auditLogRepository;
|
||||
private final Executor auditLogExecutor;
|
||||
|
||||
public AuditLogService(IAuditLogRepository auditLogRepository,
|
||||
Executor auditLogExecutor) {
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.auditLogExecutor = auditLogExecutor;
|
||||
}
|
||||
|
||||
@Async("auditLogExecutor")
|
||||
public Mono<AuditLog> saveAsync(AuditLog auditLog) {
|
||||
logger.debug("异步保存审计日志: {} - {}", auditLog.getEntityType(), auditLog.getOperationType());
|
||||
|
||||
return auditLogRepository.save(auditLog)
|
||||
.doOnSuccess(saved -> logger.debug("审计日志保存成功: ID={}", saved.getId()))
|
||||
.doOnError(error -> logger.error("审计日志保存失败: {}", error.getMessage()))
|
||||
.subscribeOn(Schedulers.fromExecutor(auditLogExecutor));
|
||||
}
|
||||
|
||||
public Mono<AuditLog> findById(Long id) {
|
||||
return auditLogRepository.findById(id);
|
||||
}
|
||||
|
||||
public Flux<AuditLog> findByEntityType(String entityType) {
|
||||
return auditLogRepository.findByEntityType(entityType);
|
||||
}
|
||||
|
||||
public Flux<AuditLog> findByEntityId(Long entityId) {
|
||||
return auditLogRepository.findByEntityId(entityId);
|
||||
}
|
||||
|
||||
public Flux<AuditLog> findByOperator(String operator) {
|
||||
return auditLogRepository.findByOperator(operator);
|
||||
}
|
||||
|
||||
public Flux<AuditLog> findByOperationType(String operationType) {
|
||||
return auditLogRepository.findByOperationType(operationType);
|
||||
}
|
||||
|
||||
public Flux<AuditLog> findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return auditLogRepository.findByOperationTimeBetween(startTime, endTime);
|
||||
}
|
||||
|
||||
public Flux<AuditLog> findByEntityTypeAndEntityId(String entityType, Long entityId) {
|
||||
return auditLogRepository.findByEntityTypeAndEntityId(entityType, entityId);
|
||||
}
|
||||
|
||||
public Mono<Long> countByEntityType(String entityType) {
|
||||
return auditLogRepository.countByEntityType(entityType);
|
||||
}
|
||||
|
||||
public Mono<Long> countByOperationType(String operationType) {
|
||||
return auditLogRepository.countByOperationType(operationType);
|
||||
}
|
||||
|
||||
public Mono<Long> countByOperator(String operator) {
|
||||
return auditLogRepository.countByOperator(operator);
|
||||
}
|
||||
|
||||
public Mono<Long> countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return auditLogRepository.countByOperationTimeBetween(startTime, endTime);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package cn.novalon.manage.sys.audit.service;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLogArchive;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审计日志归档服务接口
|
||||
*
|
||||
* 文件定义:定义审计日志归档的业务逻辑接口
|
||||
* 涉及业务:审计日志的归档、查询、清理等操作
|
||||
* 算法:定期将历史审计日志移动到归档表
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
public interface IAuditLogArchiveService {
|
||||
|
||||
Mono<Long> archiveOldLogs(int daysToKeep);
|
||||
|
||||
Mono<AuditLogArchive> archiveLog(AuditLog auditLog);
|
||||
|
||||
Flux<AuditLogArchive> findArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate);
|
||||
|
||||
Flux<AuditLogArchive> findArchivedLogsByEntityType(String entityType);
|
||||
|
||||
Mono<AuditLogArchive> findArchivedLogById(Long id);
|
||||
|
||||
Mono<Long> countArchivedLogs();
|
||||
|
||||
Mono<Long> countArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate);
|
||||
|
||||
Mono<Void> deleteArchivedLogsOlderThan(LocalDateTime date);
|
||||
|
||||
Mono<Long> getArchiveStatistics();
|
||||
|
||||
Mono<Boolean> isLogArchived(Long auditLogId);
|
||||
}
|
||||
+48
-9
@@ -1,9 +1,14 @@
|
||||
package cn.novalon.manage.sys.audit.service;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审计日志服务接口
|
||||
*
|
||||
@@ -11,20 +16,54 @@ import reactor.core.publisher.Mono;
|
||||
* @date 2026-04-08
|
||||
*/
|
||||
public interface IAuditLogService {
|
||||
|
||||
Mono<AuditLog> save(AuditLog auditLog);
|
||||
|
||||
|
||||
Mono<AuditLog> findById(Long id);
|
||||
|
||||
|
||||
Flux<AuditLog> findAll();
|
||||
|
||||
|
||||
Flux<AuditLog> findAll(boolean includeDeleted);
|
||||
|
||||
Mono<PageResponse<AuditLog>> findAuditLogsByPage(PageRequest pageRequest);
|
||||
|
||||
Mono<Long> count();
|
||||
|
||||
Flux<AuditLog> findByEntityType(String entityType);
|
||||
|
||||
|
||||
Flux<AuditLog> findByEntityId(Long entityId);
|
||||
|
||||
|
||||
Flux<AuditLog> findByEntityTypeAndEntityId(String entityType, Long entityId);
|
||||
|
||||
|
||||
Flux<AuditLog> findByOperator(String operator);
|
||||
|
||||
|
||||
Flux<AuditLog> findByOperationType(String operationType);
|
||||
|
||||
Flux<AuditLog> findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
Flux<AuditLog> findByEntityTypeAndOperationTimeBetween(String entityType, LocalDateTime startTime,
|
||||
LocalDateTime endTime);
|
||||
|
||||
Flux<AuditLog> findByOperatorAndOperationTimeBetween(String operator, LocalDateTime startTime,
|
||||
LocalDateTime endTime);
|
||||
|
||||
Mono<Long> countByEntityType(String entityType);
|
||||
|
||||
Mono<Long> countByOperationType(String operationType);
|
||||
|
||||
Mono<Long> countByOperator(String operator);
|
||||
|
||||
Mono<Long> countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
Mono<AuditLog> save(AuditLog auditLog);
|
||||
|
||||
Mono<AuditLog> saveAsync(AuditLog auditLog);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
|
||||
Mono<Void> logicalDeleteById(Long id);
|
||||
|
||||
Mono<Void> logicalDeleteByIds(List<Long> ids);
|
||||
|
||||
Mono<Void> restoreById(Long id);
|
||||
|
||||
Mono<Void> restoreByIds(List<Long> ids);
|
||||
}
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package cn.novalon.manage.sys.audit.service.impl;
|
||||
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLogArchive;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogArchiveRepository;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
|
||||
import cn.novalon.manage.sys.audit.service.IAuditLogArchiveService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审计日志归档服务实现类
|
||||
*
|
||||
* 文件定义:实现审计日志归档的业务逻辑
|
||||
* 涉及业务:审计日志的归档、查询、清理等操作
|
||||
* 算法:定期将历史审计日志移动到归档表
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-14
|
||||
*/
|
||||
@Service
|
||||
public class AuditLogArchiveService implements IAuditLogArchiveService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuditLogArchiveService.class);
|
||||
|
||||
private final IAuditLogRepository auditLogRepository;
|
||||
private final IAuditLogArchiveRepository auditLogArchiveRepository;
|
||||
|
||||
public AuditLogArchiveService(IAuditLogRepository auditLogRepository,
|
||||
IAuditLogArchiveRepository auditLogArchiveRepository) {
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.auditLogArchiveRepository = auditLogArchiveRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Long> archiveOldLogs(int daysToKeep) {
|
||||
LocalDateTime archiveBefore = LocalDateTime.now().minusDays(daysToKeep);
|
||||
|
||||
logger.info("开始归档审计日志,归档时间点: {}", archiveBefore);
|
||||
|
||||
return auditLogRepository.findByOperationTimeBetween(LocalDateTime.MIN, archiveBefore)
|
||||
.flatMap(this::archiveLog)
|
||||
.count()
|
||||
.doOnSuccess(count -> logger.info("归档完成,共归档 {} 条日志", count))
|
||||
.doOnError(error -> logger.error("归档失败: {}", error.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<AuditLogArchive> archiveLog(AuditLog auditLog) {
|
||||
AuditLogArchive archive = convertToArchive(auditLog);
|
||||
|
||||
return auditLogArchiveRepository.save(archive)
|
||||
.doOnSuccess(saved -> {
|
||||
logger.debug("审计日志归档成功: ID={}, 操作类型={}",
|
||||
saved.getId(), saved.getOperationType());
|
||||
|
||||
auditLogRepository.deleteById(auditLog.getId())
|
||||
.doOnSuccess(v -> logger.debug("原始日志删除成功: ID={}", auditLog.getId()))
|
||||
.doOnError(error -> logger.error("原始日志删除失败: ID={}, {}",
|
||||
auditLog.getId(), error.getMessage()))
|
||||
.subscribe();
|
||||
})
|
||||
.doOnError(error -> logger.error("审计日志归档失败: ID={}, {}",
|
||||
auditLog.getId(), error.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLogArchive> findArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
|
||||
return auditLogArchiveRepository.findByOperationTimeBetween(startDate, endDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLogArchive> findArchivedLogsByEntityType(String entityType) {
|
||||
return auditLogArchiveRepository.findByEntityType(entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AuditLogArchive> findArchivedLogById(Long id) {
|
||||
return auditLogArchiveRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countArchivedLogs() {
|
||||
return auditLogArchiveRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
|
||||
return auditLogArchiveRepository.findByOperationTimeBetween(startDate, endDate)
|
||||
.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> deleteArchivedLogsOlderThan(LocalDateTime date) {
|
||||
return auditLogArchiveRepository.findByOperationTimeBetween(LocalDateTime.MIN, date)
|
||||
.flatMap(archive -> auditLogArchiveRepository.deleteById(archive.getId()))
|
||||
.then()
|
||||
.doOnSuccess(v -> logger.info("删除早于 {} 的归档日志完成", date))
|
||||
.doOnError(error -> logger.error("删除归档日志失败: {}", error.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> getArchiveStatistics() {
|
||||
return auditLogArchiveRepository.count()
|
||||
.doOnNext(count -> logger.info("归档日志统计: {} 条记录", count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> isLogArchived(Long auditLogId) {
|
||||
return auditLogArchiveRepository.findAll()
|
||||
.filter(archive -> archive.getEntityId() != null && archive.getEntityId().equals(auditLogId))
|
||||
.hasElements()
|
||||
.doOnNext(archived -> logger.debug("日志 ID={} 是否已归档: {}", auditLogId, archived));
|
||||
}
|
||||
|
||||
private AuditLogArchive convertToArchive(AuditLog auditLog) {
|
||||
AuditLogArchive archive = new AuditLogArchive();
|
||||
archive.setEntityType(auditLog.getEntityType());
|
||||
archive.setEntityId(auditLog.getEntityId());
|
||||
archive.setOperationType(auditLog.getOperationType());
|
||||
archive.setOperator(auditLog.getOperator());
|
||||
archive.setOperationTime(auditLog.getOperationTime());
|
||||
archive.setIpAddress(auditLog.getIpAddress());
|
||||
archive.setUserAgent(auditLog.getUserAgent());
|
||||
archive.setArchivedAt(LocalDateTime.now());
|
||||
|
||||
if (auditLog.getDescription() != null) {
|
||||
archive.setDescription(auditLog.getDescription());
|
||||
}
|
||||
|
||||
return archive;
|
||||
}
|
||||
}
|
||||
+144
-6
@@ -3,15 +3,28 @@ package cn.novalon.manage.sys.audit.service.impl;
|
||||
import cn.novalon.manage.sys.audit.domain.AuditLog;
|
||||
import cn.novalon.manage.sys.audit.repository.IAuditLogRepository;
|
||||
import cn.novalon.manage.sys.audit.service.IAuditLogService;
|
||||
import cn.novalon.manage.common.dto.PageRequest;
|
||||
import cn.novalon.manage.common.dto.PageResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 审计日志服务实现类
|
||||
*
|
||||
* 文件定义:实现审计日志管理的核心业务逻辑
|
||||
* 涉及业务:审计日志的保存、查询、统计、删除等操作
|
||||
* 算法:使用R2DBC进行响应式数据库操作,支持分页查询、条件查询、批量操作
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-04-08
|
||||
*/
|
||||
@@ -21,14 +34,12 @@ public class AuditLogService implements IAuditLogService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuditLogService.class);
|
||||
|
||||
private final IAuditLogRepository auditLogRepository;
|
||||
private final Executor auditLogExecutor;
|
||||
|
||||
public AuditLogService(IAuditLogRepository auditLogRepository) {
|
||||
public AuditLogService(IAuditLogRepository auditLogRepository,
|
||||
Executor auditLogExecutor) {
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AuditLog> save(AuditLog auditLog) {
|
||||
return auditLogRepository.save(auditLog);
|
||||
this.auditLogExecutor = auditLogExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,6 +52,38 @@ public class AuditLogService implements IAuditLogService {
|
||||
return auditLogRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLog> findAll(boolean includeDeleted) {
|
||||
if (includeDeleted) {
|
||||
return auditLogRepository.findAll();
|
||||
} else {
|
||||
return auditLogRepository.findAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<PageResponse<AuditLog>> findAuditLogsByPage(PageRequest pageRequest) {
|
||||
return auditLogRepository.findAll()
|
||||
.collectList()
|
||||
.map(auditLogs -> {
|
||||
int total = auditLogs.size();
|
||||
int pageSize = pageRequest.getSize();
|
||||
int pageNumber = pageRequest.getPage();
|
||||
int fromIndex = pageNumber * pageSize;
|
||||
int toIndex = Math.min(fromIndex + pageSize, total);
|
||||
|
||||
List<AuditLog> pageContent = auditLogs.subList(fromIndex, toIndex);
|
||||
int totalPages = (int) Math.ceil((double) total / pageSize);
|
||||
return new PageResponse<>(pageContent, totalPages, total, pageNumber, pageSize);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count() {
|
||||
return auditLogRepository.findAll()
|
||||
.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLog> findByEntityType(String entityType) {
|
||||
return auditLogRepository.findByEntityType(entityType);
|
||||
@@ -65,4 +108,99 @@ public class AuditLogService implements IAuditLogService {
|
||||
public Flux<AuditLog> findByOperationType(String operationType) {
|
||||
return auditLogRepository.findByOperationType(operationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLog> findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return auditLogRepository.findByOperationTimeBetween(startTime, endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLog> findByEntityTypeAndOperationTimeBetween(String entityType, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return auditLogRepository.findByEntityTypeAndOperationTimeBetween(entityType, startTime, endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<AuditLog> findByOperatorAndOperationTimeBetween(String operator, LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return auditLogRepository.findByOperatorAndOperationTimeBetween(operator, startTime, endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countByEntityType(String entityType) {
|
||||
return auditLogRepository.countByEntityType(entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countByOperationType(String operationType) {
|
||||
return auditLogRepository.countByOperationType(operationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countByOperator(String operator) {
|
||||
return auditLogRepository.countByOperator(operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return auditLogRepository.countByOperationTimeBetween(startTime, endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AuditLog> save(AuditLog auditLog) {
|
||||
return auditLogRepository.save(auditLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async("auditLogExecutor")
|
||||
public Mono<AuditLog> saveAsync(AuditLog auditLog) {
|
||||
logger.debug("异步保存审计日志: {} - {}", auditLog.getEntityType(), auditLog.getOperationType());
|
||||
|
||||
return auditLogRepository.save(auditLog)
|
||||
.doOnSuccess(saved -> logger.debug("审计日志保存成功: ID={}", saved.getId()))
|
||||
.doOnError(error -> logger.error("审计日志保存失败: {}", error.getMessage()))
|
||||
.subscribeOn(Schedulers.fromExecutor(auditLogExecutor));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return auditLogRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> logicalDeleteById(Long id) {
|
||||
return auditLogRepository.findById(id)
|
||||
.flatMap(auditLog -> {
|
||||
auditLog.setDeletedAt(LocalDateTime.now());
|
||||
return auditLogRepository.save(auditLog);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> logicalDeleteByIds(List<Long> ids) {
|
||||
return Flux.fromIterable(ids)
|
||||
.flatMap(this::logicalDeleteById)
|
||||
.then();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> restoreById(Long id) {
|
||||
return auditLogRepository.findById(id)
|
||||
.flatMap(auditLog -> {
|
||||
auditLog.setDeletedAt(null);
|
||||
return auditLogRepository.save(auditLog);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Mono<Void> restoreByIds(List<Long> ids) {
|
||||
return Flux.fromIterable(ids)
|
||||
.flatMap(this::restoreById)
|
||||
.then();
|
||||
}
|
||||
}
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package cn.novalon.manage.sys.config;
|
||||
|
||||
import cn.novalon.manage.common.handler.ExceptionLogService;
|
||||
import cn.novalon.manage.sys.handler.ExceptionLogServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 异常日志配置类
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
@Configuration
|
||||
public class ExceptionLogConfig {
|
||||
|
||||
@Bean
|
||||
public ExceptionLogService exceptionLogService(ExceptionLogServiceImpl exceptionLogServiceImpl) {
|
||||
return exceptionLogServiceImpl;
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package cn.novalon.manage.sys.handler;
|
||||
|
||||
import cn.novalon.manage.common.handler.ExceptionLogService;
|
||||
import cn.novalon.manage.sys.core.domain.SysExceptionLog;
|
||||
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 异常日志服务实现
|
||||
*
|
||||
* 文件定义:实现异常日志记录接口,使用sys模块的异常日志服务
|
||||
* 涉及业务:异常日志记录、错误追踪
|
||||
* 算法:使用响应式编程实现异步日志记录
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
@Service
|
||||
public class ExceptionLogServiceImpl implements ExceptionLogService {
|
||||
|
||||
private final ISysExceptionLogService exceptionLogService;
|
||||
|
||||
public ExceptionLogServiceImpl(ISysExceptionLogService exceptionLogService) {
|
||||
this.exceptionLogService = exceptionLogService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> logException(String title, String exceptionName, String exceptionMsg,
|
||||
String methodName, String ip, String stackTrace) {
|
||||
SysExceptionLog exceptionLog = new SysExceptionLog();
|
||||
exceptionLog.setTitle(title);
|
||||
exceptionLog.setExceptionName(exceptionName);
|
||||
exceptionLog.setExceptionMsg(exceptionMsg);
|
||||
exceptionLog.setMethodName(methodName);
|
||||
exceptionLog.setIp(ip);
|
||||
exceptionLog.setCreateTime(java.time.LocalDateTime.now());
|
||||
exceptionLog.setExceptionStack(stackTrace);
|
||||
|
||||
return exceptionLogService.save(exceptionLog).then();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user