From fdca179d453a9afb7c009ded35c4a90d833b50fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Tue, 14 Apr 2026 18:46:44 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=8E=A5=E5=8F=A3=E5=91=BD=E5=90=8D):?= =?UTF-8?q?=20=E7=BB=9F=E4=B8=80=E6=8E=A5=E5=8F=A3=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E5=B9=B6=E9=87=8D=E6=9E=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将接口命名统一调整为以I开头,并重构相关实现类和服务调用 重构审计日志和网关路由服务接口,优化代码结构 删除旧接口文件,更新依赖接口的类 --- .../handler/GlobalExceptionHandler.java | 4 +- ...Service.java => IExceptionLogService.java} | 6 +- .../discovery/ServiceDiscoveryService.java | 223 ------------------ .../service/IConfigRefreshService.java | 25 ++ .../gateway/service/IDynamicRouteService.java | 44 ++++ .../gateway/service/IRequestCacheService.java | 27 +++ .../service/IServiceDiscoveryService.java | 41 ++++ .../impl}/DynamicRouteService.java | 117 ++++----- .../service/impl/ServiceDiscoveryService.java | 182 ++++++++++++++ .../audit/controller/AuditLogController.java | 6 +- .../scheduler/AuditLogArchiveScheduler.java | 6 +- .../audit/service/AuditLogArchiveService.java | 95 -------- .../sys/audit/service/AuditLogService.java | 93 -------- .../service/IAuditLogArchiveService.java | 41 ++++ .../sys/audit/service/IAuditLogService.java | 57 ++++- .../service/impl/AuditLogArchiveService.java | 142 +++++++++++ .../audit/service/impl/AuditLogService.java | 150 +++++++++++- .../manage/sys/config/ExceptionLogConfig.java | 21 -- .../sys/handler/ExceptionLogServiceImpl.java | 42 ---- 19 files changed, 757 insertions(+), 565 deletions(-) rename novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/{ExceptionLogService.java => IExceptionLogService.java} (88%) delete mode 100644 novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/discovery/ServiceDiscoveryService.java create mode 100644 novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IConfigRefreshService.java create mode 100644 novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IDynamicRouteService.java create mode 100644 novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IRequestCacheService.java create mode 100644 novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IServiceDiscoveryService.java rename novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/{route => service/impl}/DynamicRouteService.java (66%) create mode 100644 novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/ServiceDiscoveryService.java delete mode 100644 novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogArchiveService.java delete mode 100644 novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogService.java create mode 100644 novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogArchiveService.java create mode 100644 novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogArchiveService.java delete mode 100644 novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/ExceptionLogConfig.java delete mode 100644 novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/ExceptionLogServiceImpl.java diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java index cffc305..8c2267f 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java @@ -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; } diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/IExceptionLogService.java similarity index 88% rename from novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java rename to novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/IExceptionLogService.java index 63e99a0..1513065 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/IExceptionLogService.java @@ -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 logException(String title, String exceptionName, String exceptionMsg, String methodName, String ip, String stackTrace); -} +} \ No newline at end of file diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/discovery/ServiceDiscoveryService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/discovery/ServiceDiscoveryService.java deleted file mode 100644 index 9c4ce79..0000000 --- a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/discovery/ServiceDiscoveryService.java +++ /dev/null @@ -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> serviceCache = new ConcurrentHashMap<>(); - private final Map 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 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 getInstances(String serviceId) { - if (serviceId == null || serviceId.isEmpty()) { - logger.warn("Service ID is null or empty"); - return Flux.empty(); - } - - if (isCacheValid(serviceId)) { - List 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 getServices() { - return reactiveDiscoveryClient.getServices() - .doOnNext(serviceId -> logger.debug("Found service: {}", serviceId)); - } - - public Mono getFirstInstance(String serviceId) { - return getInstances(serviceId) - .next() - .doOnNext(instance -> logger.debug("Returning first instance for service: {}", serviceId)); - } - - public Mono 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 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>> getAllServicesWithInstances() { - return getServices() - .flatMap(serviceId -> - getInstances(serviceId) - .collectList() - .map(instances -> Map.entry(serviceId, instances)) - ) - .collectMap(Map.Entry::getKey, Map.Entry::getValue); - } - - public Mono getInstanceCount(String serviceId) { - return getInstances(serviceId) - .count() - .map(Long::intValue); - } - - public Mono 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 instances = serviceCache.get(serviceId); - return instances != null ? instances.size() : 0; - } -} diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IConfigRefreshService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IConfigRefreshService.java new file mode 100644 index 0000000..f720f80 --- /dev/null +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IConfigRefreshService.java @@ -0,0 +1,25 @@ +package cn.novalon.manage.gateway.service; + +import reactor.core.publisher.Mono; + +/** + * 配置刷新服务接口 + * + * 文件定义:定义网关配置动态刷新接口 + * 涉及业务:配置热更新、配置版本管理 + * + * @author 张翔 + * @date 2026-04-14 + */ +public interface IConfigRefreshService { + + Mono refreshGatewayConfig(); + + Mono refreshRouteConfig(); + + Mono refreshFilterConfig(); + + Mono getCurrentConfigVersion(); + + Mono isConfigChanged(); +} \ No newline at end of file diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IDynamicRouteService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IDynamicRouteService.java new file mode 100644 index 0000000..0bc01cb --- /dev/null +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IDynamicRouteService.java @@ -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 addRoute(RouteDefinition routeDefinition); + + Mono updateRoute(RouteDefinition routeDefinition); + + Mono deleteRoute(String routeId); + + Flux getRoutes(); + + Mono getRoute(String routeId); + + Mono refreshRoutes(); + + Mono getRouteCount(); + + Mono routeExists(String routeId); + + Mono clearRouteCache(); +} \ No newline at end of file diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IRequestCacheService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IRequestCacheService.java new file mode 100644 index 0000000..a7f84d4 --- /dev/null +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IRequestCacheService.java @@ -0,0 +1,27 @@ +package cn.novalon.manage.gateway.service; + +import reactor.core.publisher.Mono; + +/** + * 请求缓存服务接口 + * + * 文件定义:定义请求缓存管理接口 + * 涉及业务:请求缓存、缓存清理、缓存统计 + * + * @author 张翔 + * @date 2026-04-14 + */ +public interface IRequestCacheService { + + Mono cacheRequest(String requestId, Object requestData); + + Mono getCachedRequest(String requestId); + + Mono removeCachedRequest(String requestId); + + Mono clearExpiredCache(); + + Mono getCacheSize(); + + Mono isRequestCached(String requestId); +} \ No newline at end of file diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IServiceDiscoveryService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IServiceDiscoveryService.java new file mode 100644 index 0000000..1de6c83 --- /dev/null +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/IServiceDiscoveryService.java @@ -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 getInstances(String serviceId); + + Flux getServices(); + + Mono isServiceHealthy(String serviceId); + + Mono getInstanceCount(String serviceId); + + Mono refreshServiceCache(String serviceId); + + Mono refreshAllServiceCache(); + + Mono getServiceCount(); + + Mono serviceExists(String serviceId); + + Mono clearServiceCache(); +} \ No newline at end of file diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/route/DynamicRouteService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java similarity index 66% rename from novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/route/DynamicRouteService.java rename to novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java index 07b5718..e04c67c 100644 --- a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/route/DynamicRouteService.java +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java @@ -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 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 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 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 getAllRoutes() { + @Override + public Flux getRoutes() { return Flux.fromIterable(routeCache.values()); } + @Override public Mono 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 refreshRoutes() { + return Mono.fromRunnable(() -> { + publisher.publishEvent(new RefreshRoutesEvent(this)); + logger.info("Routes refreshed"); + }); } - public Mono batchAddRoutes(List routeDefinitions) { - if (routeDefinitions == null || routeDefinitions.isEmpty()) { - logger.warn("No routes to add"); + @Override + public Mono getRouteCount() { + return Mono.just((long) routeCache.size()); + } + + @Override + public Mono 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 batchDeleteRoutes(List 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 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(); - } -} +} \ No newline at end of file diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/ServiceDiscoveryService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/ServiceDiscoveryService.java new file mode 100644 index 0000000..8be03f5 --- /dev/null +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/ServiceDiscoveryService.java @@ -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> serviceCache = new ConcurrentHashMap<>(); + private final Map 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 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 getInstances(String serviceId) { + if (serviceId == null || serviceId.isEmpty()) { + logger.warn("Service ID is null or empty"); + return Flux.empty(); + } + + if (isCacheValid(serviceId)) { + List 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 getServices() { + return reactiveDiscoveryClient.getServices() + .doOnNext(serviceId -> logger.debug("Found service: {}", serviceId)); + } + + @Override + public Mono 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 getInstanceCount(String serviceId) { + return getInstances(serviceId) + .count() + .doOnNext(count -> logger.debug("Service {} has {} instances", serviceId, count)); + } + + @Override + public Mono 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 refreshAllServiceCache() { + return Mono.fromRunnable(() -> { + serviceCache.clear(); + lastUpdateTime.clear(); + initializeServiceCache(); + logger.info("Refreshed all service cache"); + }); + } + + @Override + public Mono getServiceCount() { + return getServices() + .count() + .doOnNext(count -> logger.debug("Found {} services", count)); + } + + @Override + public Mono 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 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; + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java index 7cec887..52c9964 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java @@ -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; } diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/scheduler/AuditLogArchiveScheduler.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/scheduler/AuditLogArchiveScheduler.java index 6c8e94b..6afad8e 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/scheduler/AuditLogArchiveScheduler.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/scheduler/AuditLogArchiveScheduler.java @@ -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; } diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogArchiveService.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogArchiveService.java deleted file mode 100644 index 1a8373c..0000000 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogArchiveService.java +++ /dev/null @@ -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 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 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 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 countArchivedLogs(String entityType) { - if (entityType != null) { - return auditLogArchiveRepository.countByEntityType(entityType); - } - return auditLogArchiveRepository.count(); - } -} diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogService.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogService.java deleted file mode 100644 index 94ac301..0000000 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/AuditLogService.java +++ /dev/null @@ -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 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 findById(Long id) { - return auditLogRepository.findById(id); - } - - public Flux findByEntityType(String entityType) { - return auditLogRepository.findByEntityType(entityType); - } - - public Flux findByEntityId(Long entityId) { - return auditLogRepository.findByEntityId(entityId); - } - - public Flux findByOperator(String operator) { - return auditLogRepository.findByOperator(operator); - } - - public Flux findByOperationType(String operationType) { - return auditLogRepository.findByOperationType(operationType); - } - - public Flux findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) { - return auditLogRepository.findByOperationTimeBetween(startTime, endTime); - } - - public Flux findByEntityTypeAndEntityId(String entityType, Long entityId) { - return auditLogRepository.findByEntityTypeAndEntityId(entityType, entityId); - } - - public Mono countByEntityType(String entityType) { - return auditLogRepository.countByEntityType(entityType); - } - - public Mono countByOperationType(String operationType) { - return auditLogRepository.countByOperationType(operationType); - } - - public Mono countByOperator(String operator) { - return auditLogRepository.countByOperator(operator); - } - - public Mono countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) { - return auditLogRepository.countByOperationTimeBetween(startTime, endTime); - } -} diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogArchiveService.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogArchiveService.java new file mode 100644 index 0000000..b199ab3 --- /dev/null +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogArchiveService.java @@ -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 archiveOldLogs(int daysToKeep); + + Mono archiveLog(AuditLog auditLog); + + Flux findArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate); + + Flux findArchivedLogsByEntityType(String entityType); + + Mono findArchivedLogById(Long id); + + Mono countArchivedLogs(); + + Mono countArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate); + + Mono deleteArchivedLogsOlderThan(LocalDateTime date); + + Mono getArchiveStatistics(); + + Mono isLogArchived(Long auditLogId); +} \ No newline at end of file diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogService.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogService.java index 142895c..621a1c9 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogService.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/IAuditLogService.java @@ -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 save(AuditLog auditLog); - + Mono findById(Long id); - + Flux findAll(); - + + Flux findAll(boolean includeDeleted); + + Mono> findAuditLogsByPage(PageRequest pageRequest); + + Mono count(); + Flux findByEntityType(String entityType); - + Flux findByEntityId(Long entityId); - + Flux findByEntityTypeAndEntityId(String entityType, Long entityId); - + Flux findByOperator(String operator); - + Flux findByOperationType(String operationType); + + Flux findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime); + + Flux findByEntityTypeAndOperationTimeBetween(String entityType, LocalDateTime startTime, + LocalDateTime endTime); + + Flux findByOperatorAndOperationTimeBetween(String operator, LocalDateTime startTime, + LocalDateTime endTime); + + Mono countByEntityType(String entityType); + + Mono countByOperationType(String operationType); + + Mono countByOperator(String operator); + + Mono countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime); + + Mono save(AuditLog auditLog); + + Mono saveAsync(AuditLog auditLog); + + Mono deleteById(Long id); + + Mono logicalDeleteById(Long id); + + Mono logicalDeleteByIds(List ids); + + Mono restoreById(Long id); + + Mono restoreByIds(List ids); } diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogArchiveService.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogArchiveService.java new file mode 100644 index 0000000..7e748bc --- /dev/null +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogArchiveService.java @@ -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 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 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 findArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate) { + return auditLogArchiveRepository.findByOperationTimeBetween(startDate, endDate); + } + + @Override + public Flux findArchivedLogsByEntityType(String entityType) { + return auditLogArchiveRepository.findByEntityType(entityType); + } + + @Override + public Mono findArchivedLogById(Long id) { + return auditLogArchiveRepository.findById(id); + } + + @Override + public Mono countArchivedLogs() { + return auditLogArchiveRepository.count(); + } + + @Override + public Mono countArchivedLogsByDateRange(LocalDateTime startDate, LocalDateTime endDate) { + return auditLogArchiveRepository.findByOperationTimeBetween(startDate, endDate) + .count(); + } + + @Override + @Transactional + public Mono 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 getArchiveStatistics() { + return auditLogArchiveRepository.count() + .doOnNext(count -> logger.info("归档日志统计: {} 条记录", count)); + } + + @Override + public Mono 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; + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogService.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogService.java index a66a968..6d1ab6e 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogService.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/service/impl/AuditLogService.java @@ -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 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 findAll(boolean includeDeleted) { + if (includeDeleted) { + return auditLogRepository.findAll(); + } else { + return auditLogRepository.findAll(); + } + } + + @Override + public Mono> 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 pageContent = auditLogs.subList(fromIndex, toIndex); + int totalPages = (int) Math.ceil((double) total / pageSize); + return new PageResponse<>(pageContent, totalPages, total, pageNumber, pageSize); + }); + } + + @Override + public Mono count() { + return auditLogRepository.findAll() + .count(); + } + @Override public Flux findByEntityType(String entityType) { return auditLogRepository.findByEntityType(entityType); @@ -65,4 +108,99 @@ public class AuditLogService implements IAuditLogService { public Flux findByOperationType(String operationType) { return auditLogRepository.findByOperationType(operationType); } + + @Override + public Flux findByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) { + return auditLogRepository.findByOperationTimeBetween(startTime, endTime); + } + + @Override + public Flux findByEntityTypeAndOperationTimeBetween(String entityType, LocalDateTime startTime, LocalDateTime endTime) { + return auditLogRepository.findByEntityTypeAndOperationTimeBetween(entityType, startTime, endTime); + } + + @Override + public Flux findByOperatorAndOperationTimeBetween(String operator, LocalDateTime startTime, LocalDateTime endTime) { + return auditLogRepository.findByOperatorAndOperationTimeBetween(operator, startTime, endTime); + } + + @Override + public Mono countByEntityType(String entityType) { + return auditLogRepository.countByEntityType(entityType); + } + + @Override + public Mono countByOperationType(String operationType) { + return auditLogRepository.countByOperationType(operationType); + } + + @Override + public Mono countByOperator(String operator) { + return auditLogRepository.countByOperator(operator); + } + + @Override + public Mono countByOperationTimeBetween(LocalDateTime startTime, LocalDateTime endTime) { + return auditLogRepository.countByOperationTimeBetween(startTime, endTime); + } + + @Override + public Mono save(AuditLog auditLog) { + return auditLogRepository.save(auditLog); + } + + @Override + @Async("auditLogExecutor") + public Mono 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 deleteById(Long id) { + return auditLogRepository.deleteById(id); + } + + @Override + @Transactional + public Mono logicalDeleteById(Long id) { + return auditLogRepository.findById(id) + .flatMap(auditLog -> { + auditLog.setDeletedAt(LocalDateTime.now()); + return auditLogRepository.save(auditLog); + }) + .then(); + } + + @Override + @Transactional + public Mono logicalDeleteByIds(List ids) { + return Flux.fromIterable(ids) + .flatMap(this::logicalDeleteById) + .then(); + } + + @Override + @Transactional + public Mono restoreById(Long id) { + return auditLogRepository.findById(id) + .flatMap(auditLog -> { + auditLog.setDeletedAt(null); + return auditLogRepository.save(auditLog); + }) + .then(); + } + + @Override + @Transactional + public Mono restoreByIds(List ids) { + return Flux.fromIterable(ids) + .flatMap(this::restoreById) + .then(); + } } diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/ExceptionLogConfig.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/ExceptionLogConfig.java deleted file mode 100644 index ddbfe15..0000000 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/ExceptionLogConfig.java +++ /dev/null @@ -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; - } -} diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/ExceptionLogServiceImpl.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/ExceptionLogServiceImpl.java deleted file mode 100644 index 7586700..0000000 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/ExceptionLogServiceImpl.java +++ /dev/null @@ -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 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(); - } -}