feat: 完善系统配置审计通知功能并优化异常处理
- 新增异常处理体系(BaseException及其子类) - 优化密码、邮箱、用户名等基础类型 - 添加字典管理、登录日志、操作日志的E2E测试 - 完善API集成测试和安全测试 - 添加性能测试配置和脚本 - 优化OpenAPI配置和全局异常处理器
This commit is contained in:
+39
@@ -0,0 +1,39 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class BaseException extends RuntimeException {
|
||||
|
||||
private final String errorCode;
|
||||
private final Map<String, Object> context;
|
||||
|
||||
protected BaseException(String errorCode, String message) {
|
||||
super(message);
|
||||
this.errorCode = errorCode;
|
||||
this.context = new HashMap<>();
|
||||
}
|
||||
|
||||
protected BaseException(String errorCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorCode = errorCode;
|
||||
this.context = new HashMap<>();
|
||||
}
|
||||
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public Map<String, Object> getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public BaseException addContext(String key, Object value) {
|
||||
context.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract HttpStatus getHttpStatus();
|
||||
}
|
||||
+8
-17
@@ -1,28 +1,19 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
/**
|
||||
* 业务异常类
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
public class BusinessException extends RuntimeException {
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
public class BusinessException extends BaseException {
|
||||
|
||||
public BusinessException(String code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
public BusinessException(String errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
public BusinessException(String errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
public HttpStatus getHttpStatus() {
|
||||
return HttpStatus.BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class ConflictException extends BusinessException {
|
||||
|
||||
public ConflictException(String errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public ConflictException(String errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getHttpStatus() {
|
||||
return HttpStatus.CONFLICT;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
public class ErrorCode {
|
||||
|
||||
public static final String VALIDATION_PREFIX = "VALIDATION_";
|
||||
public static final String NOT_FOUND_PREFIX = "NOT_FOUND_";
|
||||
public static final String PERMISSION_PREFIX = "PERMISSION_";
|
||||
public static final String CONFLICT_PREFIX = "CONFLICT_";
|
||||
public static final String SYSTEM_PREFIX = "SYSTEM_";
|
||||
|
||||
public static final String VALIDATION_REQUIRED = VALIDATION_PREFIX + "001";
|
||||
public static final String VALIDATION_INVALID_FORMAT = VALIDATION_PREFIX + "002";
|
||||
public static final String VALIDATION_INVALID_LENGTH = VALIDATION_PREFIX + "003";
|
||||
public static final String VALIDATION_INVALID_VALUE = VALIDATION_PREFIX + "004";
|
||||
|
||||
public static final String NOT_FOUND_USER = NOT_FOUND_PREFIX + "001";
|
||||
public static final String NOT_FOUND_ROLE = NOT_FOUND_PREFIX + "002";
|
||||
public static final String NOT_FOUND_MENU = NOT_FOUND_PREFIX + "003";
|
||||
public static final String NOT_FOUND_DICTIONARY = NOT_FOUND_PREFIX + "004";
|
||||
|
||||
public static final String PERMISSION_DENIED = PERMISSION_PREFIX + "001";
|
||||
public static final String PERMISSION_INSUFFICIENT = PERMISSION_PREFIX + "002";
|
||||
|
||||
public static final String CONFLICT_DUPLICATE = CONFLICT_PREFIX + "001";
|
||||
public static final String CONFLICT_DUPLICATE_USER = CONFLICT_PREFIX + "002";
|
||||
public static final String CONFLICT_DUPLICATE_ROLE = CONFLICT_PREFIX + "003";
|
||||
public static final String CONFLICT_DUPLICATE_DICTIONARY = CONFLICT_PREFIX + "004";
|
||||
|
||||
public static final String SYSTEM_INTERNAL_ERROR = SYSTEM_PREFIX + "001";
|
||||
public static final String SYSTEM_DATABASE_ERROR = SYSTEM_PREFIX + "002";
|
||||
public static final String SYSTEM_NETWORK_ERROR = SYSTEM_PREFIX + "003";
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class NotFoundException extends BusinessException {
|
||||
|
||||
public NotFoundException(String errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public NotFoundException(String errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getHttpStatus() {
|
||||
return HttpStatus.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class PermissionException extends BusinessException {
|
||||
|
||||
public PermissionException(String errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public PermissionException(String errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getHttpStatus() {
|
||||
return HttpStatus.FORBIDDEN;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class SystemException extends BaseException {
|
||||
|
||||
public SystemException(String errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public SystemException(String errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getHttpStatus() {
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.novalon.manage.common.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class ValidationException extends BusinessException {
|
||||
|
||||
public ValidationException(String errorCode, String message) {
|
||||
super(errorCode, message);
|
||||
}
|
||||
|
||||
public ValidationException(String errorCode, String message, Throwable cause) {
|
||||
super(errorCode, message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatus getHttpStatus() {
|
||||
return HttpStatus.BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
+17
@@ -1,5 +1,6 @@
|
||||
package cn.novalon.manage.common.handler;
|
||||
|
||||
import cn.novalon.manage.common.exception.BaseException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
@@ -40,6 +41,22 @@ public class GlobalExceptionHandler {
|
||||
this.exceptionLogService = exceptionLogService;
|
||||
}
|
||||
|
||||
@ExceptionHandler(BaseException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleBaseException(BaseException ex, ServerWebExchange exchange) {
|
||||
logger.warn("Business exception: ", ex);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("code", ex.getErrorCode());
|
||||
response.put("message", ex.getMessage());
|
||||
response.put("timestamp", LocalDateTime.now());
|
||||
|
||||
if (!ex.getContext().isEmpty()) {
|
||||
response.put("context", ex.getContext());
|
||||
}
|
||||
|
||||
return ResponseEntity.status(ex.getHttpStatus()).body(response);
|
||||
}
|
||||
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException ex, ServerWebExchange exchange) {
|
||||
logger.warn("Runtime exception: ", ex);
|
||||
|
||||
+13
-6
@@ -1,5 +1,9 @@
|
||||
package cn.novalon.manage.common.util;
|
||||
|
||||
import cn.novalon.manage.common.exception.ErrorCode;
|
||||
import cn.novalon.manage.common.exception.SystemException;
|
||||
import cn.novalon.manage.common.exception.ValidationException;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -60,7 +64,8 @@ public final class SnowflakeId {
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Failed to generate ID after " + MAX_RETRIES + " retries");
|
||||
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR,
|
||||
"Failed to generate ID after " + MAX_RETRIES + " retries");
|
||||
}
|
||||
|
||||
private static long nextIdInternal() {
|
||||
@@ -151,23 +156,25 @@ public final class SnowflakeId {
|
||||
|
||||
private static void validateBits(int workerBits, int seqBits) {
|
||||
if (workerBits < 0 || workerBits > 22) {
|
||||
throw new IllegalArgumentException("WorkerID位数必须在0-22之间");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE, "WorkerID位数必须在0-22之间");
|
||||
}
|
||||
if (seqBits < 0 || seqBits > 22) {
|
||||
throw new IllegalArgumentException("序列号位数必须在0-22之间");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE, "序列号位数必须在0-22之间");
|
||||
}
|
||||
if (workerBits + seqBits > 22) {
|
||||
throw new IllegalArgumentException("WorkerID和序列号位数总和不能超过22位,当前为: " + (workerBits + seqBits));
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE,
|
||||
"WorkerID和序列号位数总和不能超过22位,当前为: " + (workerBits + seqBits));
|
||||
}
|
||||
if (workerBits + seqBits == 0) {
|
||||
throw new IllegalArgumentException("WorkerID和序列号位数总和不能为0");
|
||||
throw new ValidationException(ErrorCode.VALIDATION_INVALID_VALUE, "WorkerID和序列号位数总和不能为0");
|
||||
}
|
||||
}
|
||||
|
||||
private static long resolveWorkerId(long maxWorkerId) {
|
||||
long id = generateNewId();
|
||||
if (id < 0 || id > maxWorkerId) {
|
||||
throw new IllegalStateException("WorkerID超出有效范围: " + id + " (有效范围: 0-" + maxWorkerId + ")");
|
||||
throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR,
|
||||
"WorkerID超出有效范围: " + id + " (有效范围: 0-" + maxWorkerId + ")");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user