feat: complete all post-refactor tasks (performance, monitoring, docs, CI/CD)
This commit is contained in:
@@ -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
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.resilience4j</groupId>
|
||||||
|
<artifactId>resilience4j-spring-boot3</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.resilience4j</groupId>
|
||||||
|
<artifactId>resilience4j-reactor</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
**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<Void> 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<Map<String, Object>> handleRuntimeException(RuntimeException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Runtime exception: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> 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<String, Object> 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<Map<String, Object>> handleIllegalArgumentException(IllegalArgumentException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Illegal argument: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Validation failed: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("code", HttpStatus.BAD_REQUEST.value());
|
||||||
|
response.put("message", "Validation failed");
|
||||||
|
response.put("timestamp", LocalDateTime.now());
|
||||||
|
|
||||||
|
Map<String, String> 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<Map<String, Object>> handleServerWebInputException(ServerWebInputException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Invalid input: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleResponseStatusException(ResponseStatusException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Response status exception: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleDuplicateKeyException(DuplicateKeyException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Duplicate key: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleDataIntegrityViolationException(DataIntegrityViolationException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Data integrity violation: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Void> logException(String title, String exceptionName, String exceptionMsg,
|
||||||
|
String methodName, String ip, String stackTrace) {
|
||||||
|
SysExceptionLog exceptionLog = new SysExceptionLog();
|
||||||
|
exceptionLog.setTitle(title);
|
||||||
|
exceptionLog.setExceptionName(exceptionName);
|
||||||
|
exceptionLog.setExceptionMsg(exceptionMsg);
|
||||||
|
exceptionLog.setMethodName(methodName);
|
||||||
|
exceptionLog.setIp(ip);
|
||||||
|
exceptionLog.setCreateTime(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
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.novalon.manage</groupId>
|
||||||
|
<artifactId>manage-sys</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.novalon.manage</groupId>
|
||||||
|
<artifactId>manage-db</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>r2dbc-postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-database-postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: 移除不需要的依赖**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 移除以下依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.resilience4j</groupId>
|
||||||
|
<artifactId>resilience4j-spring-boot3</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.resilience4j</groupId>
|
||||||
|
<artifactId>resilience4j-reactor</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
**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 <commit-hash-before-refactoring>
|
||||||
|
|
||||||
|
# 或者使用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. **测试覆盖**:重构后确保所有测试仍然通过
|
||||||
@@ -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
|
||||||
@@ -21,6 +21,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
@@ -37,6 +41,10 @@
|
|||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.jsonwebtoken</groupId>
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
<artifactId>jjwt-api</artifactId>
|
<artifactId>jjwt-api</artifactId>
|
||||||
@@ -51,6 +59,18 @@
|
|||||||
<artifactId>jjwt-jackson</artifactId>
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
|||||||
+6
@@ -9,6 +9,12 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存配置类
|
||||||
|
*
|
||||||
|
* @author 张翔
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
public class CacheConfig {
|
public class CacheConfig {
|
||||||
|
|||||||
+6
@@ -4,6 +4,12 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT配置属性类
|
||||||
|
*
|
||||||
|
* @author 张翔
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties(prefix = "jwt")
|
@ConfigurationProperties(prefix = "jwt")
|
||||||
@Validated
|
@Validated
|
||||||
|
|||||||
+42
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+149
@@ -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 <Q> Query getQuery(Q query) {
|
||||||
|
return getQuery(query, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <Q> Query getQueryAll(Q query) {
|
||||||
|
return getQuery(query, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <Q> 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<Field> 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<Field> getAllFields(Class<?> clazz, List<Field> fields) {
|
||||||
|
if (clazz != null) {
|
||||||
|
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
|
||||||
|
getAllFields(clazz.getSuperclass(), fields);
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
-67
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-117
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-86
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-38
@@ -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; }
|
|
||||||
}
|
|
||||||
-53
@@ -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; }
|
|
||||||
}
|
|
||||||
-38
@@ -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; }
|
|
||||||
}
|
|
||||||
-38
@@ -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; }
|
|
||||||
}
|
|
||||||
-38
@@ -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; }
|
|
||||||
}
|
|
||||||
-35
@@ -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; }
|
|
||||||
}
|
|
||||||
-97
@@ -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<SysMenu> 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<SysMenu> getChildren() {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChildren(List<SysMenu> children) {
|
|
||||||
this.children = children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-38
@@ -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; }
|
|
||||||
}
|
|
||||||
-75
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-77
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-29
@@ -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; }
|
|
||||||
}
|
|
||||||
+5
-5
@@ -1,11 +1,11 @@
|
|||||||
package cn.novalon.manage.common.domain.query;
|
package cn.novalon.manage.common.domain.query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author zhangxiang
|
* 菜单查询条件对象
|
||||||
* @version 1.0
|
*
|
||||||
* @description 菜单查询对象
|
* @author 张翔
|
||||||
* @date 2026/03/11
|
* @date 2026-03-13
|
||||||
**/
|
*/
|
||||||
public class SysMenuQuery {
|
public class SysMenuQuery {
|
||||||
|
|
||||||
private String menuName;
|
private String menuName;
|
||||||
|
|||||||
+5
-5
@@ -1,11 +1,11 @@
|
|||||||
package cn.novalon.manage.common.domain.query;
|
package cn.novalon.manage.common.domain.query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author zhangxiang
|
* 角色查询条件对象
|
||||||
* @version 1.0
|
*
|
||||||
* @description 角色查询对象
|
* @author 张翔
|
||||||
* @date 2026/03/11
|
* @date 2026-03-13
|
||||||
**/
|
*/
|
||||||
public class SysRoleQuery {
|
public class SysRoleQuery {
|
||||||
|
|
||||||
private String roleName;
|
private String roleName;
|
||||||
|
|||||||
+14
-5
@@ -1,17 +1,18 @@
|
|||||||
package cn.novalon.manage.common.domain.query;
|
package cn.novalon.manage.common.domain.query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author zhangxiang
|
* 用户查询条件对象
|
||||||
* @version 1.0
|
*
|
||||||
* @description 用户查询对象
|
* @author 张翔
|
||||||
* @date 2026/03/11
|
* @date 2026-03-13
|
||||||
**/
|
*/
|
||||||
public class SysUserQuery {
|
public class SysUserQuery {
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private String email;
|
private String email;
|
||||||
private Integer status;
|
private Integer status;
|
||||||
private Long roleId;
|
private Long roleId;
|
||||||
|
private String keyword;
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
@@ -44,4 +45,12 @@ public class SysUserQuery {
|
|||||||
public void setRoleId(Long roleId) {
|
public void setRoleId(Long roleId) {
|
||||||
this.roleId = roleId;
|
this.roleId = roleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getKeyword() {
|
||||||
|
return keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyword(String keyword) {
|
||||||
|
this.keyword = keyword;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -1,5 +1,11 @@
|
|||||||
package cn.novalon.manage.common.dto;
|
package cn.novalon.manage.common.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页请求参数封装类
|
||||||
|
*
|
||||||
|
* @author 张翔
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
public class PageRequest {
|
public class PageRequest {
|
||||||
private int page = 0;
|
private int page = 0;
|
||||||
private int size = 10;
|
private int size = 10;
|
||||||
|
|||||||
+6
@@ -2,6 +2,12 @@ package cn.novalon.manage.common.dto;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页响应结果封装类
|
||||||
|
*
|
||||||
|
* @author 张翔
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
public class PageResponse<T> {
|
public class PageResponse<T> {
|
||||||
private List<T> content;
|
private List<T> content;
|
||||||
private int totalPages;
|
private int totalPages;
|
||||||
|
|||||||
+6
@@ -1,5 +1,11 @@
|
|||||||
package cn.novalon.manage.common.exception;
|
package cn.novalon.manage.common.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常类
|
||||||
|
*
|
||||||
|
* @author 张翔
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
public class BusinessException extends RuntimeException {
|
public class BusinessException extends RuntimeException {
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package cn.novalon.manage.common.handler;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常日志服务接口
|
||||||
|
*
|
||||||
|
* 文件定义:定义异常日志记录的抽象接口
|
||||||
|
* 涉及业务:异常日志记录、错误追踪
|
||||||
|
* 算法:使用响应式编程实现异步日志记录
|
||||||
|
*
|
||||||
|
* @author 张翔
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
|
public interface ExceptionLogService {
|
||||||
|
Mono<Void> logException(String title, String exceptionName, String exceptionMsg,
|
||||||
|
String methodName, String ip, String stackTrace);
|
||||||
|
}
|
||||||
+181
@@ -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<Map<String, Object>> handleRuntimeException(RuntimeException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Runtime exception: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> 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<String, Object> 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<Map<String, Object>> handleIllegalArgumentException(IllegalArgumentException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Illegal argument: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Validation failed: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("code", HttpStatus.BAD_REQUEST.value());
|
||||||
|
response.put("message", "Validation failed");
|
||||||
|
response.put("timestamp", LocalDateTime.now());
|
||||||
|
|
||||||
|
Map<String, String> 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<Map<String, Object>> handleServerWebInputException(ServerWebInputException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Invalid input: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleResponseStatusException(ResponseStatusException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Response status exception: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleDuplicateKeyException(DuplicateKeyException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Duplicate key: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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<Map<String, Object>> handleDataIntegrityViolationException(DataIntegrityViolationException ex, ServerWebExchange exchange) {
|
||||||
|
logger.warn("Data integrity violation: ", ex);
|
||||||
|
|
||||||
|
Map<String, Object> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+79
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
+88
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -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<MeterRegistry> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -1,11 +1,11 @@
|
|||||||
package cn.novalon.manage.common.util;
|
package cn.novalon.manage.common.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author zhangxiang
|
* 数据库字段名常量定义
|
||||||
* @version 1.0
|
*
|
||||||
* @description 数据库字段名常量
|
* @author 张翔
|
||||||
* @date 2026/03/11
|
* @date 2026-03-13
|
||||||
**/
|
*/
|
||||||
public class FieldConstants {
|
public class FieldConstants {
|
||||||
|
|
||||||
public static final String USERNAME = "username";
|
public static final String USERNAME = "username";
|
||||||
|
|||||||
+5
-5
@@ -1,11 +1,11 @@
|
|||||||
package cn.novalon.manage.common.util;
|
package cn.novalon.manage.common.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author zhangxiang
|
* 菜单类型常量定义
|
||||||
* @version 1.0
|
*
|
||||||
* @description 菜单类型常量
|
* @author 张翔
|
||||||
* @date 2026/03/11
|
* @date 2026-03-13
|
||||||
**/
|
*/
|
||||||
public class MenuTypeConstants {
|
public class MenuTypeConstants {
|
||||||
|
|
||||||
public static final String DIRECTORY = "M";
|
public static final String DIRECTORY = "M";
|
||||||
|
|||||||
+6
-9
@@ -6,17 +6,14 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
import java.util.concurrent.locks.LockSupport;
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter的Snowflake算法实现(性能优化版)
|
* 雪花算法ID生成器
|
||||||
*
|
*
|
||||||
* 优化点:
|
* 文件定义:基于Twitter Snowflake算法的分布式唯一ID生成器
|
||||||
* 1. 使用自适应等待策略,减少CPU空转
|
* 涉及业务:为系统所有实体生成唯一ID,支持分布式环境下的ID生成
|
||||||
* 2. 时间戳缓存机制,降低系统调用频率
|
* 算法:使用雪花算法,结合时间戳、机器ID和序列号生成唯一ID,支持高并发场景
|
||||||
* 3. 增强CAS重试策略,提升并发性能
|
|
||||||
* 4. 完善异常处理和资源管理
|
|
||||||
*
|
*
|
||||||
* @author zhangxiang
|
* @author 张翔
|
||||||
* @version 2.0
|
* @date 2026-03-13
|
||||||
* @date 2026/03/11
|
|
||||||
*/
|
*/
|
||||||
public final class SnowflakeId {
|
public final class SnowflakeId {
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -1,11 +1,15 @@
|
|||||||
package cn.novalon.manage.common.util;
|
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 class StatusConstants {
|
||||||
|
|
||||||
public static final Integer DISABLED = 0;
|
public static final Integer DISABLED = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user