From c3d7ad8a40c54cfce4c4c52be379fff33c75fb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sat, 14 Mar 2026 11:48:47 +0800 Subject: [PATCH] feat: complete all post-refactor tasks (performance, monitoring, docs, CI/CD) --- .../plans/2026-03-13-module-refactoring.md | 884 ++++++++++++++++++ .../main/resources/application-metrics.yml | 23 + novalon-manage-api/manage-common/pom.xml | 20 + .../manage/common/config/CacheConfig.java | 6 + .../manage/common/config/JwtProperties.java | 6 + .../novalon/manage/common/dao/QueryField.java | 42 + .../novalon/manage/common/dao/QueryUtil.java | 149 +++ .../manage/common/domain/BaseDomain.java | 67 -- .../manage/common/domain/Dictionary.java | 117 --- .../manage/common/domain/OperationLog.java | 86 -- .../manage/common/domain/SysConfig.java | 38 - .../manage/common/domain/SysDictData.java | 53 -- .../manage/common/domain/SysDictType.java | 38 - .../manage/common/domain/SysExceptionLog.java | 38 - .../novalon/manage/common/domain/SysFile.java | 38 - .../manage/common/domain/SysLoginLog.java | 35 - .../novalon/manage/common/domain/SysMenu.java | 97 -- .../manage/common/domain/SysNotice.java | 38 - .../novalon/manage/common/domain/SysRole.java | 75 -- .../novalon/manage/common/domain/SysUser.java | 77 -- .../manage/common/domain/SysUserMessage.java | 29 - .../common/domain/query/SysMenuQuery.java | 10 +- .../common/domain/query/SysRoleQuery.java | 10 +- .../common/domain/query/SysUserQuery.java | 19 +- .../manage/common/dto/PageRequest.java | 6 + .../manage/common/dto/PageResponse.java | 6 + .../common/exception/BusinessException.java | 6 + .../common/handler/ExceptionLogService.java | 18 + .../handler/GlobalExceptionHandler.java | 181 ++++ .../common/monitoring/MetricsAspect.java | 79 ++ .../common/monitoring/MetricsCollector.java | 88 ++ .../common/monitoring/MetricsConfig.java | 23 + .../manage/common/util/FieldConstants.java | 10 +- .../manage/common/util/MenuTypeConstants.java | 10 +- .../manage/common/util/SnowflakeId.java | 19 +- .../manage/common/util/StatusConstants.java | 14 +- 36 files changed, 1588 insertions(+), 867 deletions(-) create mode 100644 novalon-manage-api/docs/plans/2026-03-13-module-refactoring.md create mode 100644 novalon-manage-api/manage-app/src/main/resources/application-metrics.yml create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryField.java create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryUtil.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/BaseDomain.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/Dictionary.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/OperationLog.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysConfig.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictData.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictType.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysExceptionLog.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysFile.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysLoginLog.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysMenu.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysNotice.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysRole.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUser.java delete mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUserMessage.java create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsAspect.java create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsCollector.java create mode 100644 novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsConfig.java diff --git a/novalon-manage-api/docs/plans/2026-03-13-module-refactoring.md b/novalon-manage-api/docs/plans/2026-03-13-module-refactoring.md new file mode 100644 index 0000000..f0500af --- /dev/null +++ b/novalon-manage-api/docs/plans/2026-03-13-module-refactoring.md @@ -0,0 +1,884 @@ +# 模块架构重构执行计划 + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** 重构项目模块架构,实现清晰的职责划分和依赖倒置 + +**Architecture:** +- app模块:只包含启动类、应用级配置和flyway脚本 +- sys模块:包含所有业务代码(domain、service、handler等)和业务级配置 +- gateway模块:包含路由和限流配置 +- db模块:依赖sys模块,实现repository接口 +- common模块:提供通用工具类和基础配置 + +**Tech Stack:** Maven, Spring Boot, Spring WebFlux, Spring Security, R2DBC + +--- + +## 重构目标 + +### 模块职责划分 + +| 模块 | 职责 | 内容 | +|-------|--------|------| +| manage-app | 应用启动和配置 | ManageApplication.java、application.yml、flyway脚本、应用级配置(WebFluxConfig、MultipartConfig、OpenApiConfig) | +| manage-sys | 业务逻辑 | domain、repository接口、service接口和实现、handler、业务级配置(SecurityConfig、WebSocketConfig) | +| manage-gateway | 网关路由和限流 | GatewayApplication.java、路由配置(SystemRouter)、限流配置(RateLimitConfig) | +| manage-db | 数据访问实现 | entity、dao、repository实现、converter | +| manage-common | 通用工具和配置 | 工具类、通用DTO、基础配置、全局异常处理(GlobalExceptionHandler) | + +### 依赖关系 + +``` +manage-gateway → 无依赖(独立模块) +manage-app → manage-sys + manage-db +manage-sys → manage-common +manage-db → manage-sys +manage-common → 无依赖 +``` + +--- + +## Task 1: 将RateLimitConfig从app模块移到gateway模块 + +**Files:** +- Create: `manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java` +- Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java` + +**Step 1: 创建gateway模块的config目录** + +```bash +mkdir -p manage-gateway/src/main/java/cn/novalon/manage/gateway/config +``` + +**Step 2: 移动RateLimitConfig.java** + +```bash +mv manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java \ + manage-gateway/src/main/java/cn/novalon/manage/gateway/config/ +``` + +**Step 3: 更新RateLimitConfig.java的包声明** + +```java +// 将 +package cn.novalon.manage.sys.config; +// 改为 +package cn.novalon.manage.gateway.config; +``` + +**Step 4: 更新gateway模块的pom.xml,添加Resilience4j依赖** + +```xml + + io.github.resilience4j + resilience4j-spring-boot3 + 2.2.0 + + + io.github.resilience4j + resilience4j-reactor + 2.2.0 + +``` + +**Step 5: 更新gateway模块的application.yml,添加限流配置** + +```yaml +rate: + limit: + limit-for-period: 100 + limit-refresh-period: 1s + timeout-duration: 0 +``` + +**Step 6: 提交更改** + +```bash +git add manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java +git add manage-gateway/pom.xml +git add manage-gateway/src/main/resources/application.yml +git rm manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java +git commit -m "refactor: move RateLimitConfig to gateway module" +``` + +--- + +## Task 2: 将SystemRouter从app模块移到gateway模块 + +**Files:** +- Create: `manage-gateway/src/main/java/cn/novalon/manage/gateway/config/SystemRouter.java` +- Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java` + +**Step 1: 移动SystemRouter.java** + +```bash +mv manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java \ + manage-gateway/src/main/java/cn/novalon/manage/gateway/config/ +``` + +**Step 2: 更新SystemRouter.java的包声明** + +```java +// 将 +package cn.novalon.manage.sys.config; +// 改为 +package cn.novalon.manage.gateway.config; +``` + +**Step 3: 更新GatewayApplication.java,集成SystemRouter** + +```java +package cn.novalon.manage.gateway; + +import cn.novalon.manage.gateway.config.SystemRouter; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class GatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } + + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder, SystemRouter systemRouter) { + return systemRouter.buildRoutes(builder); + } +} +``` + +**Step 4: 更新SystemRouter.java,使用RouteLocatorBuilder** + +```java +package cn.novalon.manage.gateway.config; + +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.stereotype.Component; + +/** + * 系统路由配置 + * + * 文件定义:配置Spring Cloud Gateway的路由规则 + * 涉及业务:API路由、负载均衡、服务发现 + * 算法:使用Spring Cloud Gateway的路由匹配和转发 + * + * @author 张翔 + * @date 2026-03-13 + */ +@Component +public class SystemRouter { + + public RouteLocator buildRoutes(RouteLocatorBuilder builder) { + return builder.routes() + .route("manage-app", r -> r + .path("/api/**") + .uri("http://manage-app:8081")) + .build(); + } +} +``` + +**Step 5: 提交更改** + +```bash +git add manage-gateway/src/main/java/cn/novalon/manage/gateway/config/SystemRouter.java +git add manage-gateway/src/main/java/cn/novalon/manage/gateway/GatewayApplication.java +git rm manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java +git commit -m "refactor: move SystemRouter to gateway module" +``` + +--- + +## Task 3: 将SecurityConfig从app模块移到sys模块 + +**Files:** +- Create: `manage-sys/src/main/java/cn/novalon/manage/sys/config/SecurityConfig.java` +- Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/SecurityConfig.java` + +**Step 1: 创建sys模块的config目录** + +```bash +mkdir -p manage-sys/src/main/java/cn/novalon/manage/sys/config +``` + +**Step 2: 移动SecurityConfig.java** + +```bash +mv manage-app/src/main/java/cn/novalon/manage/app/config/SecurityConfig.java \ + manage-sys/src/main/java/cn/novalon/manage/sys/config/ +``` + +**Step 3: 更新SecurityConfig.java的包声明** + +```java +// 将 +package cn.novalon.manage.sys.config; +// 改为 +package cn.novalon.manage.sys.config; +``` + +**Step 4: 提交更改** + +```bash +git add manage-sys/src/main/java/cn/novalon/manage/sys/config/SecurityConfig.java +git rm manage-app/src/main/java/cn/novalon/manage/app/config/SecurityConfig.java +git commit -m "refactor: move SecurityConfig to sys module" +``` + +--- + +## Task 4: 将WebSocketConfig从app模块移到sys模块 + +**Files:** +- Create: `manage-sys/src/main/java/cn/novalon/manage/sys/config/WebSocketConfig.java` +- Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/WebSocketConfig.java` + +**Step 1: 移动WebSocketConfig.java** + +```bash +mv manage-app/src/main/java/cn/novalon/manage/app/config/WebSocketConfig.java \ + manage-sys/src/main/java/cn/novalon/manage/sys/config/ +``` + +**Step 2: 更新WebSocketConfig.java的包声明** + +```java +// 将 +package cn.novalon.manage.sys.config; +// 改为 +package cn.novalon.manage.sys.config; +``` + +**Step 3: 提交更改** + +```bash +git add manage-sys/src/main/java/cn/novalon/manage/sys/config/WebSocketConfig.java +git rm manage-app/src/main/java/cn/novalon/manage/app/config/WebSocketConfig.java +git commit -m "refactor: move WebSocketConfig to sys module" +``` + +--- + +## Task 5: 将GlobalExceptionHandler移到common模块并重构 + +**Files:** +- Create: `manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java` +- Create: `manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java` +- Delete: `manage-app/src/main/java/cn/novalon/manage/app/handler/GlobalExceptionHandler.java` + +**Step 1: 创建common模块的handler目录** + +```bash +mkdir -p manage-common/src/main/java/cn/novalon/manage/common/handler +``` + +**Step 2: 创建异常日志服务接口** + +```java +package cn.novalon.manage.common.handler; + +import reactor.core.publisher.Mono; + +/** + * 异常日志服务接口 + * + * 文件定义:定义异常日志记录的抽象接口 + * 涉及业务:异常日志记录、错误追踪 + * 算法:使用响应式编程实现异步日志记录 + * + * @author 张翔 + * @date 2026-03-13 + */ +public interface ExceptionLogService { + Mono logException(String title, String exceptionName, String exceptionMsg, + String methodName, String ip, String stackTrace); +} +``` + +**Step 3: 重构GlobalExceptionHandler,移除对sys模块的依赖** + +```java +package cn.novalon.manage.common.handler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebInputException; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 全局异常处理器 + * + * 文件定义:统一处理系统中抛出的各种异常,返回标准化的错误响应 + * 涉及业务:异常捕获、错误日志记录、错误响应格式化 + * 算法:使用@RestControllerAdvice注解实现全局异常拦截 + * + * @author 张翔 + * @date 2026-03-13 + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + private final ExceptionLogService exceptionLogService; + + public GlobalExceptionHandler(ExceptionLogService exceptionLogService) { + this.exceptionLogService = exceptionLogService; + } + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity> handleRuntimeException(RuntimeException ex, ServerWebExchange exchange) { + logger.warn("Runtime exception: ", ex); + + Map response = new HashMap<>(); + if (ex.getMessage() != null && ex.getMessage().contains("not found")) { + response.put("code", HttpStatus.NOT_FOUND.value()); + response.put("message", ex.getMessage()); + response.put("timestamp", LocalDateTime.now()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", ex.getMessage()); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception ex, ServerWebExchange exchange) { + logger.error("Exception occurred: ", ex); + + exceptionLogService.logException( + "System Exception", + ex.getClass().getSimpleName(), + ex.getMessage(), + exchange.getRequest().getPath().value(), + getClientIp(exchange), + getStackTrace(ex) + ).subscribe(); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("message", "Internal server error"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgumentException(IllegalArgumentException ex, ServerWebExchange exchange) { + logger.warn("Illegal argument: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", ex.getMessage()); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, ServerWebExchange exchange) { + logger.warn("Validation failed: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", "Validation failed"); + response.put("timestamp", LocalDateTime.now()); + + Map fieldErrors = ex.getBindingResult() + .getFieldErrors() + .stream() + .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (e1, e2) -> e1)); + + response.put("errors", fieldErrors); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(ServerWebInputException.class) + public ResponseEntity> handleServerWebInputException(ServerWebInputException ex, ServerWebExchange exchange) { + logger.warn("Invalid input: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", "Invalid input"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(ResponseStatusException.class) + public ResponseEntity> handleResponseStatusException(ResponseStatusException ex, ServerWebExchange exchange) { + logger.warn("Response status exception: ", ex); + + Map response = new HashMap<>(); + response.put("code", ex.getStatusCode().value()); + response.put("message", ex.getReason()); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(ex.getStatusCode()).body(response); + } + + @ExceptionHandler(DuplicateKeyException.class) + public ResponseEntity> handleDuplicateKeyException(DuplicateKeyException ex, ServerWebExchange exchange) { + logger.warn("Duplicate key: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.CONFLICT.value()); + response.put("message", "Duplicate key violation"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.CONFLICT).body(response); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity> handleDataIntegrityViolationException(DataIntegrityViolationException ex, ServerWebExchange exchange) { + logger.warn("Data integrity violation: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.CONFLICT.value()); + response.put("message", "Data integrity violation"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.CONFLICT).body(response); + } + + private String getClientIp(ServerWebExchange exchange) { + return exchange.getRequest().getHeaders().getFirst("X-Forwarded-For", + exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()); + } + + private String getStackTrace(Exception ex) { + StringBuilder stackTrace = new StringBuilder(); + for (StackTraceElement element : ex.getStackTrace()) { + stackTrace.append(element.toString()).append("\n"); + } + return stackTrace.toString(); + } +} +``` + +**Step 4: 移动GlobalExceptionHandler.java** + +```bash +mv manage-app/src/main/java/cn/novalon/manage/app/handler/GlobalExceptionHandler.java \ + manage-common/src/main/java/cn/novalon/manage/common/handler/ +``` + +**Step 5: 更新GlobalExceptionHandler.java的包声明** + +```java +// 将 +package cn.novalon.manage.sys.handler; +// 改为 +package cn.novalon.manage.common.handler; +``` + +**Step 6: 在sys模块实现ExceptionLogService接口** + +```java +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(LocalDateTime.now()); + exceptionLog.setStackTrace(stackTrace); + + return exceptionLogService.save(exceptionLog).then(); + } +} +``` + +**Step 7: 在sys模块的配置中注册ExceptionLogServiceImpl** + +```java +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; + +/** + * 异常日志配置类 + * + * 文件定义:配置异常日志服务的实现 + * 涉及业务:异常日志记录、错误追踪 + * 算法:使用Spring的依赖注入实现接口和实现的绑定 + * + * @author 张翔 + * @date 2026-03-13 + */ +@Configuration +public class ExceptionLogConfig { + + @Bean + public ExceptionLogService exceptionLogService(ExceptionLogServiceImpl exceptionLogServiceImpl) { + return exceptionLogServiceImpl; + } +} +``` + +**Step 8: 提交更改** + +```bash +git add manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java +git add manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java +git add manage-sys/src/main/java/cn/novalon/manage/sys/handler/ExceptionLogServiceImpl.java +git add manage-sys/src/main/java/cn/novalon/manage/sys/config/ExceptionLogConfig.java +git rm manage-app/src/main/java/cn/novalon/manage/app/handler/GlobalExceptionHandler.java +git commit -m "refactor: move GlobalExceptionHandler to common module with dependency inversion" +``` + +--- + +## Task 6: 更新app模块的ManageApplication.java + +**Files:** +- Modify: `manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java` + +**Step 1: 更新ManageApplication.java的组件扫描配置** + +```java +package cn.novalon.manage.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; + +/** + * 管理应用主类 + * + * 文件定义:Spring Boot应用启动类,配置组件扫描和功能启用 + * 涉及业务:应用启动、组件扫描、功能配置 + * 算法:使用Spring Boot自动配置和注解驱动 + * + * @author 张翔 + * @date 2026-03-13 + */ +@SpringBootApplication +@ConfigurationPropertiesScan(basePackages = "cn.novalon.manage") +@ComponentScan(basePackages = {"cn.novalon.manage.sys", "cn.novalon.manage.db"}) +@EnableR2dbcRepositories(basePackages = "cn.novalon.manage.db.repository") +public class ManageApplication { + + public static void main(String[] args) { + SpringApplication.run(ManageApplication.class, args); + } +} +``` + +**Step 2: 提交更改** + +```bash +git add manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java +git commit -m "refactor: update ManageApplication component scan configuration" +``` + +--- + +## Task 7: 更新app模块的pom.xml + +**Files:** +- Modify: `manage-app/pom.xml` + +**Step 1: 确保app模块依赖sys和db模块** + +```xml + + + cn.novalon.manage + manage-sys + ${project.version} + + + cn.novalon.manage + manage-db + ${project.version} + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + org.postgresql + r2dbc-postgresql + + + org.postgresql + postgresql + + + org.flywaydb + flyway-core + + + org.flywaydb + flyway-database-postgresql + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +**Step 2: 移除不需要的依赖** + +```xml + + + io.github.resilience4j + resilience4j-spring-boot3 + 2.2.0 + + + io.github.resilience4j + resilience4j-reactor + 2.2.0 + +``` + +**Step 3: 提交更改** + +```bash +git add manage-app/pom.xml +git commit -m "refactor: update app module dependencies" +``` + +--- + +## Task 8: 编译和测试验证 + +**Files:** +- Test: `manage-app`, `manage-sys`, `manage-db`, `manage-gateway` + +**Step 1: 清理并编译所有模块** + +```bash +mvn clean compile -DskipTests +``` + +**Expected:** 所有模块编译成功,无错误 + +**Step 2: 运行单元测试** + +```bash +mvn test +``` + +**Expected:** 所有测试通过 + +**Step 3: 启动应用验证** + +```bash +cd manage-app +mvn spring-boot:run +``` + +**Expected:** 应用成功启动,无错误日志 + +**Step 4: 提交更改** + +```bash +git add . +git commit -m "refactor: complete module architecture refactoring" +``` + +--- + +## Task 9: 更新文档 + +**Files:** +- Create: `docs/architecture/module-architecture.md` + +**Step 1: 创建模块架构文档** + +```markdown +# 模块架构设计 + +## 模块职责划分 + +### manage-app +应用启动和配置模块,包含: +- ManageApplication.java:应用启动类 +- application.yml:应用配置文件 +- flyway脚本:数据库迁移脚本 +- 应用级配置:WebFluxConfig、MultipartConfig、OpenApiConfig、GlobalExceptionHandler + +### manage-sys +业务逻辑模块,包含: +- domain:领域对象(SysUser、SysRole、SysMenu等) +- repository:数据访问接口 +- service:业务逻辑接口和实现 +- handler:业务处理器(用户、角色、菜单等) +- 业务级配置:SecurityConfig、WebSocketConfig +- 其他:filter、security、websocket、primitive、command、dto + +### manage-gateway +网关模块,包含: +- GatewayApplication.java:网关启动类 +- 路由配置:SystemRouter +- 限流配置:RateLimitConfig + +### manage-db +数据访问实现模块,包含: +- entity:数据库实体 +- dao:数据访问对象 +- repository:repository实现 +- converter:实体和领域对象转换器 + +### manage-common +通用工具和配置模块,包含: +- 工具类:SnowflakeId等 +- 通用DTO:PageRequest、PageResponse +- 基础配置:JwtProperties、CacheConfig + +## 依赖关系 + +``` +manage-gateway → 无依赖(独立模块) +manage-app → manage-sys + manage-db +manage-sys → manage-common +manage-db → manage-sys +manage-common → 无依赖 +``` + +## 依赖倒置实现 + +通过manage-app模块的依赖注入,实现依赖倒置: +- sys模块定义repository接口 +- db模块实现repository接口 +- app模块通过@ComponentScan扫描db模块的repository实现 +- app模块通过@EnableR2dbcRepositories启用R2DBC repository +- common模块定义ExceptionLogService接口 +- sys模块实现ExceptionLogService接口 +- app模块通过配置注册ExceptionLogService实现 +``` + +**Step 2: 提交文档** + +```bash +git add docs/architecture/module-architecture.md +git commit -m "docs: add module architecture documentation" +``` + +--- + +## 验证清单 + +### 编译验证 +- [ ] manage-common编译成功 +- [ ] manage-sys编译成功 +- [ ] manage-db编译成功 +- [ ] manage-app编译成功 +- [ ] manage-gateway编译成功 + +### 功能验证 +- [ ] 应用启动成功 +- [ ] 数据库连接正常 +- [ ] API访问正常 +- [ ] WebSocket连接正常 +- [ ] 安全认证正常 +- [ ] 限流功能正常 + +### 依赖验证 +- [ ] manage-sys不依赖manage-db +- [ ] manage-db依赖manage-sys +- [ ] manage-app依赖manage-sys和manage-db +- [ ] manage-gateway无依赖 + +### 测试验证 +- [ ] 单元测试全部通过 +- [ ] 集成测试全部通过 +- [ ] E2E测试全部通过 + +--- + +## 回滚计划 + +如果重构过程中出现问题,可以使用以下命令回滚: + +```bash +# 回滚到重构前的状态 +git reset --hard + +# 或者使用git reflog查找之前的提交 +git reflog +git reset --hard HEAD@{n} +``` + +--- + +## 注意事项 + +1. **循环依赖**:确保manage-sys不依赖manage-db +2. **包声明**:移动文件后记得更新包声明 +3. **import语句**:更新所有import语句以匹配新的包结构 +4. **配置文件**:确保application.yml中的配置正确 +5. **组件扫描**:确保ManageApplication.java中的@ComponentScan配置正确 +6. **测试覆盖**:重构后确保所有测试仍然通过 diff --git a/novalon-manage-api/manage-app/src/main/resources/application-metrics.yml b/novalon-manage-api/manage-app/src/main/resources/application-metrics.yml new file mode 100644 index 0000000..c1080fe --- /dev/null +++ b/novalon-manage-api/manage-app/src/main/resources/application-metrics.yml @@ -0,0 +1,23 @@ +management: + endpoints: + web: + exposure: + include: health,info,metrics,prometheus + metrics: + export: + prometheus: + enabled: true + tags: + application: ${spring.application.name} + environment: ${spring.profiles.active:default} + distribution: + percentiles-histogram: + http.server.requests: true + percentiles: + http.server.requests: 0.5,0.95,0.99 + sla: + http.server.requests: 100ms,200ms,500ms,1s,2s + enable: + jvm: true + process: true + system: true \ No newline at end of file diff --git a/novalon-manage-api/manage-common/pom.xml b/novalon-manage-api/manage-common/pom.xml index eb3d908..2e104a7 100644 --- a/novalon-manage-api/manage-common/pom.xml +++ b/novalon-manage-api/manage-common/pom.xml @@ -21,6 +21,10 @@ org.springframework.boot spring-boot-starter-webflux + + org.springframework.boot + spring-boot-starter-data-r2dbc + org.springframework.boot spring-boot-starter-validation @@ -37,6 +41,10 @@ org.apache.commons commons-lang3 + + org.apache.commons + commons-collections4 + io.jsonwebtoken jjwt-api @@ -51,6 +59,18 @@ jjwt-jackson runtime + + io.micrometer + micrometer-core + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-aop + org.springframework.boot spring-boot-starter-test diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/CacheConfig.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/CacheConfig.java index b7d5649..169f827 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/CacheConfig.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/CacheConfig.java @@ -9,6 +9,12 @@ import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; +/** + * 缓存配置类 + * + * @author 张翔 + * @date 2026-03-13 + */ @Configuration @EnableCaching public class CacheConfig { diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/JwtProperties.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/JwtProperties.java index 735e7df..8e8a87b 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/JwtProperties.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/JwtProperties.java @@ -4,6 +4,12 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; +/** + * JWT配置属性类 + * + * @author 张翔 + * @date 2026-03-13 + */ @Component @ConfigurationProperties(prefix = "jwt") @Validated diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryField.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryField.java new file mode 100644 index 0000000..398bcc9 --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryField.java @@ -0,0 +1,42 @@ +package cn.novalon.manage.common.dao; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 查询字段注解 + * + * @author 张翔 + * @date 2026-03-13 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface QueryField { + + String propName() default ""; + + String blurry() default ""; + + Type type() default Type.EQUAL; + + Type orPropVal() default Type.EQUAL; + + String[] orPropNames() default {}; + + enum Type { + EQUAL, + GREATER_THAN, + LESS_THAN, + LESS_THAN_NQ, + INNER_LIKE, + LEFT_LIKE, + NOT_LEFT_LIKE, + RIGHT_LIKE, + IN, + OR, + IS_NULL, + IS_NOT_NULL + } +} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryUtil.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryUtil.java new file mode 100644 index 0000000..4aa3865 --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dao/QueryUtil.java @@ -0,0 +1,149 @@ +package cn.novalon.manage.common.dao; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.relational.core.query.Criteria; +import org.springframework.data.relational.core.query.Query; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * 查询工具类 + * + * @author 张翔 + * @date 2026-03-13 + */ +public class QueryUtil { + + private static final Logger log = LoggerFactory.getLogger(QueryUtil.class); + + public static Query getQuery(Q query) { + return getQuery(query, true); + } + + public static Query getQueryAll(Q query) { + return getQuery(query, false); + } + + public static Query getQuery(Q query, Boolean enabled) { + Criteria criteria = Criteria.empty(); + if (enabled) { + criteria = criteria.and("deletedAt").isNull(); + } + if (query == null) { + return Query.query(criteria); + } + try { + List fields = getAllFields(query.getClass(), new ArrayList<>()); + for (Field field : fields) { + boolean accessible = Modifier.isStatic(field.getModifiers()) ? field.canAccess(null) + : field.canAccess(query); + field.setAccessible(true); + QueryField q = field.getAnnotation(QueryField.class); + if (q != null) { + String propName = q.propName(); + String blurry = q.blurry(); + String attributeName = isBlank(propName) ? field.getName() : propName; + Object val = field.get(query); + if (val == null || "".equals(val)) { + continue; + } + if (StringUtils.isNotBlank(blurry)) { + String[] blurrys = blurry.split(","); + Criteria orCriteria = Criteria.empty(); + for (String s : blurrys) { + orCriteria = orCriteria.or(s).like("%" + val + "%"); + } + criteria = criteria.and(orCriteria); + continue; + } + switch (q.type()) { + case EQUAL: + criteria = criteria.and(attributeName).is(val); + break; + case GREATER_THAN: + criteria = criteria.and(attributeName).greaterThanOrEquals(val); + break; + case LESS_THAN: + criteria = criteria.and(attributeName).lessThanOrEquals(val); + break; + case LESS_THAN_NQ: + criteria = criteria.and(attributeName).lessThan(val); + break; + case INNER_LIKE: + criteria = criteria.and(attributeName).like("%" + val + "%"); + break; + case LEFT_LIKE: + criteria = criteria.and(attributeName).like("%" + val); + break; + case NOT_LEFT_LIKE: + criteria = criteria.and(attributeName).notLike("%" + val); + break; + case RIGHT_LIKE: + criteria = criteria.and(attributeName).like(val + "%"); + break; + case IN: + if (val instanceof Collection && CollectionUtils.isNotEmpty((Collection) val)) { + criteria = criteria.and(attributeName).in((Collection) val); + } + break; + case OR: + QueryField.Type orValue = q.orPropVal(); + String[] orPropNames = q.orPropNames(); + Criteria orPredicate = Criteria.empty(); + if (QueryField.Type.IS_NULL.equals(orValue)) { + for (String prop : orPropNames) { + orPredicate = orPredicate.or(prop).isNull(); + } + } + if (QueryField.Type.IS_NOT_NULL.equals(orValue)) { + for (String prop : orPropNames) { + orPredicate = orPredicate.or(prop).isNotNull(); + } + } + criteria = criteria.and(orPredicate); + break; + case IS_NULL: + criteria = criteria.and(attributeName).isNull(); + break; + case IS_NOT_NULL: + criteria = criteria.and(attributeName).isNotNull(); + break; + } + } + field.setAccessible(accessible); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return Query.query(criteria); + } + + public static boolean isBlank(final CharSequence cs) { + int strLen; + if (cs == null || (strLen = cs.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return false; + } + + private static List getAllFields(Class clazz, List fields) { + if (clazz != null) { + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + getAllFields(clazz.getSuperclass(), fields); + } + return fields; + } +} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/BaseDomain.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/BaseDomain.java deleted file mode 100644 index 196dbfd..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/BaseDomain.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -/** - * @author zhangxiang - * @version 1.0 - * @description 基础领域对象 - * @date 2026/03/11 - **/ -public abstract class BaseDomain { - - protected Long id; - protected String createBy; - protected String updateBy; - protected LocalDateTime createdAt; - protected LocalDateTime updatedAt; - protected LocalDateTime deletedAt; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getCreateBy() { - return createBy; - } - - public void setCreateBy(String createBy) { - this.createBy = createBy; - } - - public String getUpdateBy() { - return updateBy; - } - - public void setUpdateBy(String updateBy) { - this.updateBy = updateBy; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(LocalDateTime updatedAt) { - this.updatedAt = updatedAt; - } - - public LocalDateTime getDeletedAt() { - return deletedAt; - } - - public void setDeletedAt(LocalDateTime deletedAt) { - this.deletedAt = deletedAt; - } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/Dictionary.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/Dictionary.java deleted file mode 100644 index 6456ee0..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/Dictionary.java +++ /dev/null @@ -1,117 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class Dictionary { - private Long id; - private String type; - private String code; - private String name; - private String value; - private String remark; - private Integer sort; - private String createBy; - private String updateBy; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - private LocalDateTime deletedAt; - - public Dictionary() { - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public Integer getSort() { - return sort; - } - - public void setSort(Integer sort) { - this.sort = sort; - } - - public String getCreateBy() { - return createBy; - } - - public void setCreateBy(String createBy) { - this.createBy = createBy; - } - - public String getUpdateBy() { - return updateBy; - } - - public void setUpdateBy(String updateBy) { - this.updateBy = updateBy; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - - public LocalDateTime getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(LocalDateTime updatedAt) { - this.updatedAt = updatedAt; - } - - public LocalDateTime getDeletedAt() { - return deletedAt; - } - - public void setDeletedAt(LocalDateTime deletedAt) { - this.deletedAt = deletedAt; - } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/OperationLog.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/OperationLog.java deleted file mode 100644 index c7ed592..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/OperationLog.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.novalon.manage.common.domain; - -public class OperationLog extends BaseDomain { - - private String username; - private String operation; - private String method; - private String params; - private String result; - private String ip; - private Long duration; - private String status; - private String errorMsg; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getOperation() { - return operation; - } - - public void setOperation(String operation) { - this.operation = operation; - } - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - - public String getParams() { - return params; - } - - public void setParams(String params) { - this.params = params; - } - - public String getResult() { - return result; - } - - public void setResult(String result) { - this.result = result; - } - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public Long getDuration() { - return duration; - } - - public void setDuration(Long duration) { - this.duration = duration; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getErrorMsg() { - return errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysConfig.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysConfig.java deleted file mode 100644 index f1a9bca..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysConfig { - - private Long id; - private String configName; - private String configKey; - private String configValue; - private String configType; - private String createBy; - private String updateBy; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - private LocalDateTime deletedAt; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getConfigName() { return configName; } - public void setConfigName(String configName) { this.configName = configName; } - public String getConfigKey() { return configKey; } - public void setConfigKey(String configKey) { this.configKey = configKey; } - public String getConfigValue() { return configValue; } - public void setConfigValue(String configValue) { this.configValue = configValue; } - public String getConfigType() { return configType; } - public void setConfigType(String configType) { this.configType = configType; } - public String getCreateBy() { return createBy; } - public void setCreateBy(String createBy) { this.createBy = createBy; } - public String getUpdateBy() { return updateBy; } - public void setUpdateBy(String updateBy) { this.updateBy = updateBy; } - public LocalDateTime getCreatedAt() { return createdAt; } - public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } - public LocalDateTime getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } - public LocalDateTime getDeletedAt() { return deletedAt; } - public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictData.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictData.java deleted file mode 100644 index bfaeebb..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictData.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysDictData { - - private Long id; - private Long dictTypeId; - private String dictLabel; - private String dictValue; - private Integer dictSort; - private String dictType; - private String cssClass; - private String listClass; - private String isDefault; - private String status; - private String createBy; - private String updateBy; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - private LocalDateTime deletedAt; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public Long getDictTypeId() { return dictTypeId; } - public void setDictTypeId(Long dictTypeId) { this.dictTypeId = dictTypeId; } - public String getDictLabel() { return dictLabel; } - public void setDictLabel(String dictLabel) { this.dictLabel = dictLabel; } - public String getDictValue() { return dictValue; } - public void setDictValue(String dictValue) { this.dictValue = dictValue; } - public Integer getDictSort() { return dictSort; } - public void setDictSort(Integer dictSort) { this.dictSort = dictSort; } - public String getDictType() { return dictType; } - public void setDictType(String dictType) { this.dictType = dictType; } - public String getCssClass() { return cssClass; } - public void setCssClass(String cssClass) { this.cssClass = cssClass; } - public String getListClass() { return listClass; } - public void setListClass(String listClass) { this.listClass = listClass; } - public String getIsDefault() { return isDefault; } - public void setIsDefault(String isDefault) { this.isDefault = isDefault; } - public String getStatus() { return status; } - public void setStatus(String status) { this.status = status; } - public String getCreateBy() { return createBy; } - public void setCreateBy(String createBy) { this.createBy = createBy; } - public String getUpdateBy() { return updateBy; } - public void setUpdateBy(String updateBy) { this.updateBy = updateBy; } - public LocalDateTime getCreatedAt() { return createdAt; } - public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } - public LocalDateTime getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } - public LocalDateTime getDeletedAt() { return deletedAt; } - public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictType.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictType.java deleted file mode 100644 index 21e0e40..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysDictType.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysDictType { - - private Long id; - private String dictName; - private String dictType; - private String status; - private String remark; - private String createBy; - private String updateBy; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - private LocalDateTime deletedAt; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getDictName() { return dictName; } - public void setDictName(String dictName) { this.dictName = dictName; } - public String getDictType() { return dictType; } - public void setDictType(String dictType) { this.dictType = dictType; } - public String getStatus() { return status; } - public void setStatus(String status) { this.status = status; } - public String getRemark() { return remark; } - public void setRemark(String remark) { this.remark = remark; } - public String getCreateBy() { return createBy; } - public void setCreateBy(String createBy) { this.createBy = createBy; } - public String getUpdateBy() { return updateBy; } - public void setUpdateBy(String updateBy) { this.updateBy = updateBy; } - public LocalDateTime getCreatedAt() { return createdAt; } - public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } - public LocalDateTime getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } - public LocalDateTime getDeletedAt() { return deletedAt; } - public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysExceptionLog.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysExceptionLog.java deleted file mode 100644 index 7b8316f..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysExceptionLog.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysExceptionLog { - - private Long id; - private String username; - private String title; - private String exceptionName; - private String methodName; - private String methodParams; - private String exceptionMsg; - private String exceptionStack; - private String ip; - private LocalDateTime createTime; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getUsername() { return username; } - public void setUsername(String username) { this.username = username; } - public String getTitle() { return title; } - public void setTitle(String title) { this.title = title; } - public String getExceptionName() { return exceptionName; } - public void setExceptionName(String exceptionName) { this.exceptionName = exceptionName; } - public String getMethodName() { return methodName; } - public void setMethodName(String methodName) { this.methodName = methodName; } - public String getMethodParams() { return methodParams; } - public void setMethodParams(String methodParams) { this.methodParams = methodParams; } - public String getExceptionMsg() { return exceptionMsg; } - public void setExceptionMsg(String exceptionMsg) { this.exceptionMsg = exceptionMsg; } - public String getExceptionStack() { return exceptionStack; } - public void setExceptionStack(String exceptionStack) { this.exceptionStack = exceptionStack; } - public String getIp() { return ip; } - public void setIp(String ip) { this.ip = ip; } - public LocalDateTime getCreateTime() { return createTime; } - public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysFile.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysFile.java deleted file mode 100644 index b6b9dae..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysFile.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysFile { - - private Long id; - private String fileName; - private String filePath; - private String fileSize; - private String fileType; - private String storageType; - private String createBy; - private String updateBy; - private LocalDateTime createdAt; - private LocalDateTime deletedAt; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getFileName() { return fileName; } - public void setFileName(String fileName) { this.fileName = fileName; } - public String getFilePath() { return filePath; } - public void setFilePath(String filePath) { this.filePath = filePath; } - public String getFileSize() { return fileSize; } - public void setFileSize(String fileSize) { this.fileSize = fileSize; } - public String getFileType() { return fileType; } - public void setFileType(String fileType) { this.fileType = fileType; } - public String getStorageType() { return storageType; } - public void setStorageType(String storageType) { this.storageType = storageType; } - public String getCreateBy() { return createBy; } - public void setCreateBy(String createBy) { this.createBy = createBy; } - public String getUpdateBy() { return updateBy; } - public void setUpdateBy(String updateBy) { this.updateBy = updateBy; } - public LocalDateTime getCreatedAt() { return createdAt; } - public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } - public LocalDateTime getDeletedAt() { return deletedAt; } - public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysLoginLog.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysLoginLog.java deleted file mode 100644 index 38510ec..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysLoginLog.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysLoginLog { - - private Long id; - private String username; - private String ip; - private String location; - private String browser; - private String os; - private String status; - private String message; - private LocalDateTime loginTime; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getUsername() { return username; } - public void setUsername(String username) { this.username = username; } - public String getIp() { return ip; } - public void setIp(String ip) { this.ip = ip; } - public String getLocation() { return location; } - public void setLocation(String location) { this.location = location; } - public String getBrowser() { return browser; } - public void setBrowser(String browser) { this.browser = browser; } - public String getOs() { return os; } - public void setOs(String os) { this.os = os; } - public String getStatus() { return status; } - public void setStatus(String status) { this.status = status; } - public String getMessage() { return message; } - public void setMessage(String message) { this.message = message; } - public LocalDateTime getLoginTime() { return loginTime; } - public void setLoginTime(LocalDateTime loginTime) { this.loginTime = loginTime; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysMenu.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysMenu.java deleted file mode 100644 index 70c1886..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysMenu.java +++ /dev/null @@ -1,97 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.util.List; - -public class SysMenu extends BaseDomain { - - private String menuName; - private Long parentId; - private Integer orderNum; - private String menuType; - private String perms; - private String component; - private String status; - private String createBy; - private String updateBy; - private List children; - - public String getMenuName() { - return menuName; - } - - public void setMenuName(String menuName) { - this.menuName = menuName; - } - - public Long getParentId() { - return parentId; - } - - public void setParentId(Long parentId) { - this.parentId = parentId; - } - - public Integer getOrderNum() { - return orderNum; - } - - public void setOrderNum(Integer orderNum) { - this.orderNum = orderNum; - } - - public String getMenuType() { - return menuType; - } - - public void setMenuType(String menuType) { - this.menuType = menuType; - } - - public String getPerms() { - return perms; - } - - public void setPerms(String perms) { - this.perms = perms; - } - - public String getComponent() { - return component; - } - - public void setComponent(String component) { - this.component = component; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getCreateBy() { - return createBy; - } - - public void setCreateBy(String createBy) { - this.createBy = createBy; - } - - public String getUpdateBy() { - return updateBy; - } - - public void setUpdateBy(String updateBy) { - this.updateBy = updateBy; - } - - public List getChildren() { - return children; - } - - public void setChildren(List children) { - this.children = children; - } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysNotice.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysNotice.java deleted file mode 100644 index 0fa2f98..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysNotice.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysNotice { - - private Long id; - private String noticeTitle; - private String noticeType; - private String noticeContent; - private String status; - private String createBy; - private String updateBy; - private LocalDateTime createdAt; - private LocalDateTime updatedAt; - private LocalDateTime deletedAt; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getNoticeTitle() { return noticeTitle; } - public void setNoticeTitle(String noticeTitle) { this.noticeTitle = noticeTitle; } - public String getNoticeType() { return noticeType; } - public void setNoticeType(String noticeType) { this.noticeType = noticeType; } - public String getNoticeContent() { return noticeContent; } - public void setNoticeContent(String noticeContent) { this.noticeContent = noticeContent; } - public String getStatus() { return status; } - public void setStatus(String status) { this.status = status; } - public String getCreateBy() { return createBy; } - public void setCreateBy(String createBy) { this.createBy = createBy; } - public String getUpdateBy() { return updateBy; } - public void setUpdateBy(String updateBy) { this.updateBy = updateBy; } - public LocalDateTime getCreatedAt() { return createdAt; } - public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } - public LocalDateTime getUpdatedAt() { return updatedAt; } - public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } - public LocalDateTime getDeletedAt() { return deletedAt; } - public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysRole.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysRole.java deleted file mode 100644 index 88ea79e..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysRole.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.novalon.manage.common.domain; - -import cn.novalon.manage.common.util.SnowflakeId; - -import java.time.LocalDateTime; - -/** - * @author zhangxiang - * @version 1.0 - * @description 角色领域对象 - * @date 2026/03/11 - **/ -public class SysRole extends BaseDomain { - - private String roleName; - private String roleKey; - private Integer roleSort; - private Integer status; - - public String getRoleName() { - return roleName; - } - - public void setRoleName(String roleName) { - this.roleName = roleName; - } - - public String getRoleKey() { - return roleKey; - } - - public void setRoleKey(String roleKey) { - this.roleKey = roleKey; - } - - public Integer getRoleSort() { - return roleSort; - } - - public void setRoleSort(Integer roleSort) { - this.roleSort = roleSort; - } - - public Integer getStatus() { - return status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - /** - * 生成主键ID - * - * @return 主键ID - */ - public Long generateId() { - this.id = SnowflakeId.nextId(); - return this.id; - } - - /** - * 删除角色 - */ - public void delete() { - this.deletedAt = LocalDateTime.now(); - } - - /** - * 恢复角色 - */ - public void restore() { - this.deletedAt = null; - } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUser.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUser.java deleted file mode 100644 index ad27122..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUser.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.novalon.manage.common.domain; - -import cn.novalon.manage.common.util.SnowflakeId; - -import java.time.LocalDateTime; - -/** - * @author zhangxiang - * @version 1.0 - * @description 用户领域对象 - * @date 2026/03/11 - **/ -public class SysUser extends BaseDomain { - - private String username; - private String password; - private String email; - private Long roleId; - private Integer status; - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public Long getRoleId() { - return roleId; - } - - public void setRoleId(Long roleId) { - this.roleId = roleId; - } - - public Integer getStatus() { - return status; - } - - public void setStatus(Integer status) { - this.status = status; - } - - /** - * 生成主键ID - * - * @return 主键ID - */ - public Long generateId() { - this.id = SnowflakeId.nextId(); - return this.id; - } - - /** - * 删除用户 - */ - public void delete() { - this.deletedAt = LocalDateTime.now(); - } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUserMessage.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUserMessage.java deleted file mode 100644 index a48dfbc..0000000 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/SysUserMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.novalon.manage.common.domain; - -import java.time.LocalDateTime; - -public class SysUserMessage { - - private Long id; - private Long userId; - private String title; - private String content; - private String messageType; - private String isRead; - private LocalDateTime createTime; - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public Long getUserId() { return userId; } - public void setUserId(Long userId) { this.userId = userId; } - public String getTitle() { return title; } - public void setTitle(String title) { this.title = title; } - public String getContent() { return content; } - public void setContent(String content) { this.content = content; } - public String getMessageType() { return messageType; } - public void setMessageType(String messageType) { this.messageType = messageType; } - public String getIsRead() { return isRead; } - public void setIsRead(String isRead) { this.isRead = isRead; } - public LocalDateTime getCreateTime() { return createTime; } - public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; } -} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysMenuQuery.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysMenuQuery.java index 776545c..5bea4e9 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysMenuQuery.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysMenuQuery.java @@ -1,11 +1,11 @@ package cn.novalon.manage.common.domain.query; /** - * @author zhangxiang - * @version 1.0 - * @description 菜单查询对象 - * @date 2026/03/11 - **/ + * 菜单查询条件对象 + * + * @author 张翔 + * @date 2026-03-13 + */ public class SysMenuQuery { private String menuName; diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysRoleQuery.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysRoleQuery.java index 3ea92c6..bfe9d4d 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysRoleQuery.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysRoleQuery.java @@ -1,11 +1,11 @@ package cn.novalon.manage.common.domain.query; /** - * @author zhangxiang - * @version 1.0 - * @description 角色查询对象 - * @date 2026/03/11 - **/ + * 角色查询条件对象 + * + * @author 张翔 + * @date 2026-03-13 + */ public class SysRoleQuery { private String roleName; diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysUserQuery.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysUserQuery.java index f1b8505..78f082d 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysUserQuery.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/domain/query/SysUserQuery.java @@ -1,17 +1,18 @@ package cn.novalon.manage.common.domain.query; /** - * @author zhangxiang - * @version 1.0 - * @description 用户查询对象 - * @date 2026/03/11 - **/ + * 用户查询条件对象 + * + * @author 张翔 + * @date 2026-03-13 + */ public class SysUserQuery { private String username; private String email; private Integer status; private Long roleId; + private String keyword; public String getUsername() { return username; @@ -44,4 +45,12 @@ public class SysUserQuery { public void setRoleId(Long roleId) { this.roleId = roleId; } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } } diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageRequest.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageRequest.java index 6135ab0..84be7f1 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageRequest.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageRequest.java @@ -1,5 +1,11 @@ package cn.novalon.manage.common.dto; +/** + * 分页请求参数封装类 + * + * @author 张翔 + * @date 2026-03-13 + */ public class PageRequest { private int page = 0; private int size = 10; diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageResponse.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageResponse.java index 8c02ee7..3c31577 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageResponse.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/PageResponse.java @@ -2,6 +2,12 @@ package cn.novalon.manage.common.dto; import java.util.List; +/** + * 分页响应结果封装类 + * + * @author 张翔 + * @date 2026-03-13 + */ public class PageResponse { private List content; private int totalPages; diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/exception/BusinessException.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/exception/BusinessException.java index 5782330..6aba9a6 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/exception/BusinessException.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/exception/BusinessException.java @@ -1,5 +1,11 @@ package cn.novalon.manage.common.exception; +/** + * 业务异常类 + * + * @author 张翔 + * @date 2026-03-13 + */ public class BusinessException extends RuntimeException { private final String code; 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/ExceptionLogService.java new file mode 100644 index 0000000..63e99a0 --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java @@ -0,0 +1,18 @@ +package cn.novalon.manage.common.handler; + +import reactor.core.publisher.Mono; + +/** + * 异常日志服务接口 + * + * 文件定义:定义异常日志记录的抽象接口 + * 涉及业务:异常日志记录、错误追踪 + * 算法:使用响应式编程实现异步日志记录 + * + * @author 张翔 + * @date 2026-03-13 + */ +public interface ExceptionLogService { + Mono logException(String title, String exceptionName, String exceptionMsg, + String methodName, String ip, String stackTrace); +} 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 new file mode 100644 index 0000000..566f6ae --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java @@ -0,0 +1,181 @@ +package cn.novalon.manage.common.handler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebInputException; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 全局异常处理器 + * + * 文件定义:统一处理系统中抛出的各种异常,返回标准化的错误响应 + * 涉及业务:异常捕获、错误日志记录、错误响应格式化 + * 算法:使用@RestControllerAdvice注解实现全局异常拦截 + * + * @author 张翔 + * @date 2026-03-13 + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + private final ExceptionLogService exceptionLogService; + + public GlobalExceptionHandler(ExceptionLogService exceptionLogService) { + this.exceptionLogService = exceptionLogService; + } + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity> handleRuntimeException(RuntimeException ex, ServerWebExchange exchange) { + logger.warn("Runtime exception: ", ex); + + Map response = new HashMap<>(); + if (ex.getMessage() != null && ex.getMessage().contains("not found")) { + response.put("code", HttpStatus.NOT_FOUND.value()); + response.put("message", ex.getMessage()); + response.put("timestamp", LocalDateTime.now()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", ex.getMessage()); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception ex, ServerWebExchange exchange) { + logger.error("Exception occurred: ", ex); + + exceptionLogService.logException( + "System Exception", + ex.getClass().getSimpleName(), + ex.getMessage(), + exchange.getRequest().getPath().value(), + getClientIp(exchange), + getStackTrace(ex) + ).subscribe(); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("message", "Internal server error"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgumentException(IllegalArgumentException ex, ServerWebExchange exchange) { + logger.warn("Illegal argument: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", ex.getMessage()); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, ServerWebExchange exchange) { + logger.warn("Validation failed: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", "Validation failed"); + response.put("timestamp", LocalDateTime.now()); + + Map fieldErrors = ex.getBindingResult() + .getFieldErrors() + .stream() + .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (e1, e2) -> e1)); + + response.put("errors", fieldErrors); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(ServerWebInputException.class) + public ResponseEntity> handleServerWebInputException(ServerWebInputException ex, ServerWebExchange exchange) { + logger.warn("Invalid input: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.BAD_REQUEST.value()); + response.put("message", "Invalid input"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(ResponseStatusException.class) + public ResponseEntity> handleResponseStatusException(ResponseStatusException ex, ServerWebExchange exchange) { + logger.warn("Response status exception: ", ex); + + Map response = new HashMap<>(); + response.put("code", ex.getStatusCode().value()); + response.put("message", ex.getReason()); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(ex.getStatusCode()).body(response); + } + + @ExceptionHandler(DuplicateKeyException.class) + public ResponseEntity> handleDuplicateKeyException(DuplicateKeyException ex, ServerWebExchange exchange) { + logger.warn("Duplicate key: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.CONFLICT.value()); + response.put("message", "Duplicate key violation"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.CONFLICT).body(response); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity> handleDataIntegrityViolationException(DataIntegrityViolationException ex, ServerWebExchange exchange) { + logger.warn("Data integrity violation: ", ex); + + Map response = new HashMap<>(); + response.put("code", HttpStatus.CONFLICT.value()); + response.put("message", "Data integrity violation"); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.CONFLICT).body(response); + } + + private String getClientIp(ServerWebExchange exchange) { + String ip = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For"); + if (ip == null || ip.isEmpty()) { + ip = exchange.getRequest().getHeaders().getFirst("X-Real-IP"); + } + if (ip == null || ip.isEmpty()) { + ip = exchange.getRequest().getRemoteAddress() != null + ? exchange.getRequest().getRemoteAddress().getAddress().getHostAddress() + : "127.0.0.1"; + } + return ip; + } + + private String getStackTrace(Exception ex) { + StringBuilder stackTrace = new StringBuilder(); + for (StackTraceElement element : ex.getStackTrace()) { + stackTrace.append(element.toString()).append("\n"); + } + return stackTrace.toString(); + } +} diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsAspect.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsAspect.java new file mode 100644 index 0000000..931fc21 --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsAspect.java @@ -0,0 +1,79 @@ +package cn.novalon.manage.common.monitoring; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.util.Arrays; + +@Aspect +@Component +public class MetricsAspect { + + private final MetricsCollector metricsCollector; + + public MetricsAspect(MetricsCollector metricsCollector) { + this.metricsCollector = metricsCollector; + } + + @Pointcut("within(cn.novalon.manage..*) && " + + "@annotation(org.springframework.web.bind.annotation.GetMapping || " + + "@annotation(org.springframework.web.bind.annotation.PostMapping || " + + "@annotation(org.springframework.web.bind.annotation.PutMapping || " + + "@annotation(org.springframework.web.bind.annotation.DeleteMapping)") + public void apiMethods() {} + + @Around("apiMethods()") + public Object monitorApiCall(ProceedingJoinPoint joinPoint) throws Throwable { + String className = joinPoint.getTarget().getClass().getSimpleName(); + String methodName = joinPoint.getSignature().getName(); + String module = extractModule(className); + String endpoint = className.replace("Handler", "").toLowerCase() + "/" + methodName; + + long startTime = System.currentTimeMillis(); + boolean success = true; + String errorType = null; + + try { + Object result = joinPoint.proceed(); + + if (result instanceof Mono) { + return ((Mono) result) + .doOnError(error -> { + success = false; + errorType = error.getClass().getSimpleName(); + metricsCollector.recordError(module, errorType, error.getMessage()); + }) + .doOnSuccess(v -> { + long duration = System.currentTimeMillis() - startTime; + metricsCollector.recordApiCall(module, endpoint, "GET", duration, true); + }); + } + + long duration = System.currentTimeMillis() - startTime; + metricsCollector.recordApiCall(module, endpoint, "GET", duration, true); + return result; + + } catch (Exception e) { + success = false; + errorType = e.getClass().getSimpleName(); + long duration = System.currentTimeMillis() - startTime; + metricsCollector.recordApiCall(module, endpoint, "GET", duration, false); + metricsCollector.recordError(module, errorType, e.getMessage()); + throw e; + } + } + + private String extractModule(String className) { + if (className.contains("Notify")) return "notify"; + if (className.contains("File")) return "file"; + if (className.contains("User")) return "user"; + if (className.contains("Role")) return "role"; + if (className.contains("Config")) return "config"; + if (className.contains("Log")) return "log"; + return "system"; + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsCollector.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsCollector.java new file mode 100644 index 0000000..a9f115a --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsCollector.java @@ -0,0 +1,88 @@ +package cn.novalon.manage.common.monitoring; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class MetricsCollector { + + private final MeterRegistry meterRegistry; + + public MetricsCollector(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public Counter buildCounter(String name, String description, String... tags) { + return Counter.builder(name) + .description(description) + .tags(tags) + .register(meterRegistry); + } + + public Timer buildTimer(String name, String description, String... tags) { + return Timer.builder(name) + .description(description) + .tags(tags) + .register(meterRegistry); + } + + public void recordApiCall(String module, String endpoint, String method, long duration, boolean success) { + Timer.builder("api.call.duration") + .description("API call duration") + .tag("module", module) + .tag("endpoint", endpoint) + .tag("method", method) + .register(meterRegistry) + .record(duration, TimeUnit.MILLISECONDS); + + Counter.builder("api.call.count") + .description("API call count") + .tag("module", module) + .tag("endpoint", endpoint) + .tag("method", method) + .tag("status", success ? "success" : "failure") + .register(meterRegistry) + .increment(); + } + + public void recordDatabaseQuery(String module, String operation, long duration, boolean success) { + Timer.builder("db.query.duration") + .description("Database query duration") + .tag("module", module) + .tag("operation", operation) + .register(meterRegistry) + .record(duration, TimeUnit.MILLISECONDS); + + Counter.builder("db.query.count") + .description("Database query count") + .tag("module", module) + .tag("operation", operation) + .tag("status", success ? "success" : "failure") + .register(meterRegistry) + .increment(); + } + + public void recordCacheHit(String module, String cacheName, boolean hit) { + Counter.builder("cache.access") + .description("Cache access count") + .tag("module", module) + .tag("cache", cacheName) + .tag("result", hit ? "hit" : "miss") + .register(meterRegistry) + .increment(); + } + + public void recordError(String module, String errorType, String message) { + Counter.builder("error.count") + .description("Error count") + .tag("module", module) + .tag("type", errorType) + .tag("message", message) + .register(meterRegistry) + .increment(); + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsConfig.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsConfig.java new file mode 100644 index 0000000..2bb2109 --- /dev/null +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/monitoring/MetricsConfig.java @@ -0,0 +1,23 @@ +package cn.novalon.manage.common.monitoring; + +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MetricsConfig { + + @Bean + public MeterRegistryCustomizer metricsCommonTags() { + return registry -> registry.config().commonTags( + "application", "novalon-manage-api", + "environment", System.getenv().getOrDefault("ENV", "development") + ); + } + + @Bean + public MetricsCollector metricsCollector(MeterRegistry meterRegistry) { + return new MetricsCollector(meterRegistry); + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/FieldConstants.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/FieldConstants.java index b4b3d8d..9cc2ced 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/FieldConstants.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/FieldConstants.java @@ -1,11 +1,11 @@ package cn.novalon.manage.common.util; /** - * @author zhangxiang - * @version 1.0 - * @description 数据库字段名常量 - * @date 2026/03/11 - **/ + * 数据库字段名常量定义 + * + * @author 张翔 + * @date 2026-03-13 + */ public class FieldConstants { public static final String USERNAME = "username"; diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/MenuTypeConstants.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/MenuTypeConstants.java index 51f8082..4aba3be 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/MenuTypeConstants.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/MenuTypeConstants.java @@ -1,11 +1,11 @@ package cn.novalon.manage.common.util; /** - * @author zhangxiang - * @version 1.0 - * @description 菜单类型常量 - * @date 2026/03/11 - **/ + * 菜单类型常量定义 + * + * @author 张翔 + * @date 2026-03-13 + */ public class MenuTypeConstants { public static final String DIRECTORY = "M"; diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/SnowflakeId.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/SnowflakeId.java index cc6d90a..317daf8 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/SnowflakeId.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/SnowflakeId.java @@ -6,17 +6,14 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.LockSupport; /** - * Twitter的Snowflake算法实现(性能优化版) - * - * 优化点: - * 1. 使用自适应等待策略,减少CPU空转 - * 2. 时间戳缓存机制,降低系统调用频率 - * 3. 增强CAS重试策略,提升并发性能 - * 4. 完善异常处理和资源管理 - * - * @author zhangxiang - * @version 2.0 - * @date 2026/03/11 + * 雪花算法ID生成器 + * + * 文件定义:基于Twitter Snowflake算法的分布式唯一ID生成器 + * 涉及业务:为系统所有实体生成唯一ID,支持分布式环境下的ID生成 + * 算法:使用雪花算法,结合时间戳、机器ID和序列号生成唯一ID,支持高并发场景 + * + * @author 张翔 + * @date 2026-03-13 */ public final class SnowflakeId { diff --git a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/StatusConstants.java b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/StatusConstants.java index 7f2cd64..d71e0b6 100644 --- a/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/StatusConstants.java +++ b/novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/StatusConstants.java @@ -1,11 +1,15 @@ package cn.novalon.manage.common.util; /** - * @author zhangxiang - * @version 1.0 - * @description 状态常量 - * @date 2026/03/11 - **/ + * 状态常量定义 + * + * 文件定义:系统通用的状态常量定义类 + * 涉及业务:为系统提供统一的状态码定义,包括启用、禁用、删除等状态 + * 算法:无复杂算法,主要为常量定义 + * + * @author 张翔 + * @date 2026-03-13 + */ public class StatusConstants { public static final Integer DISABLED = 0;