|
|
@@ -1,16 +1,23 @@
|
|
|
package com.zsjz.ai.common.exception;
|
|
|
|
|
|
-import cn.dev33.satoken.exception.NotLoginException;
|
|
|
-import cn.dev33.satoken.exception.NotRoleException;
|
|
|
import com.zsjz.ai.common.config.Result;
|
|
|
+import jakarta.validation.ConstraintViolationException;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
|
|
+import org.springframework.validation.BindException;
|
|
|
+import org.springframework.web.HttpMediaTypeNotSupportedException;
|
|
|
+import org.springframework.web.HttpRequestMethodNotSupportedException;
|
|
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
|
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
|
+import org.springframework.web.bind.annotation.PathVariable;
|
|
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|
|
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
|
|
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
|
|
+import org.springframework.web.servlet.NoHandlerFoundException;
|
|
|
+import org.springframework.web.servlet.resource.NoResourceFoundException;
|
|
|
|
|
|
/**
|
|
|
* 全局异常处理器:统一错误响应格式为 Result。
|
|
|
@@ -29,23 +36,6 @@ public class GlobalExceptionHandler {
|
|
|
.body(Result.failure(e.getCode(), e.getMessage()));
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * sa-token 未登录:返回 401
|
|
|
- */
|
|
|
- @ExceptionHandler(NotLoginException.class)
|
|
|
- public ResponseEntity<Result<Void>> handleNotLogin(NotLoginException e) {
|
|
|
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
|
|
- .body(Result.failure(401, "未登录或登录已过期"));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * sa-token 角色不足:返回 403
|
|
|
- */
|
|
|
- @ExceptionHandler(NotRoleException.class)
|
|
|
- public ResponseEntity<Result<Void>> handleNotRole(NotRoleException e) {
|
|
|
- return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
|
|
- .body(Result.failure(403, "无权限执行该操作"));
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
* 参数校验异常:@Valid 校验失败
|
|
|
@@ -61,15 +51,6 @@ public class GlobalExceptionHandler {
|
|
|
return ResponseEntity.badRequest().body(Result.failure(400, message));
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 请求体解析异常:JSON 格式错误
|
|
|
- */
|
|
|
- @ExceptionHandler(HttpMessageNotReadableException.class)
|
|
|
- public ResponseEntity<Result<Void>> handleHttpMessageNotReadable(HttpMessageNotReadableException e) {
|
|
|
- log.warn("请求体解析失败: {}", e.getMessage());
|
|
|
- return ResponseEntity.badRequest().body(Result.failure(400, "请求体格式错误"));
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 兜底异常:记录完整日志,不向客户端泄露内部细节
|
|
|
*/
|
|
|
@@ -80,6 +61,82 @@ public class GlobalExceptionHandler {
|
|
|
.body(Result.failure(500, "系统异常,请稍后重试"));
|
|
|
}
|
|
|
|
|
|
+ @ExceptionHandler(BindException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleBind(BindException e) {
|
|
|
+ String message = e.getFieldErrors().stream()
|
|
|
+ .map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
|
|
|
+ .findFirst().orElse("参数绑定失败");
|
|
|
+ log.info("参数绑定失败: {}", message);
|
|
|
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
|
|
+ .body(Result.failure(400, message));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(ConstraintViolationException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleConstraint(ConstraintViolationException e) {
|
|
|
+ log.info("约束校验失败: {}", e.getMessage());
|
|
|
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
|
|
+ .body(Result.failure(400, e.getMessage()));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @ExceptionHandler(HttpMessageNotReadableException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleNotReadable(
|
|
|
+ org.springframework.http.converter.HttpMessageNotReadableException e) {
|
|
|
+ log.info("请求体解析失败: {}", e.getMessage());
|
|
|
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
|
|
+ .body(Result.failure(400, "请求体格式错误"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(NoHandlerFoundException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleNoHandler(NoHandlerFoundException e) {
|
|
|
+ log.info("接口不存在: {} {}", e.getHttpMethod(), e.getRequestURL());
|
|
|
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
|
|
+ .body(Result.failure(404, "接口不存在"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(NoResourceFoundException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleNoResource(NoResourceFoundException e) {
|
|
|
+ log.info("资源不存在: {}", e.getMessage());
|
|
|
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
|
|
+ .body(Result.failure(404, "资源不存在"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleMethodNotAllowed(
|
|
|
+ HttpRequestMethodNotSupportedException e) {
|
|
|
+ log.info("请求方法不支持: method={}", e.getMethod());
|
|
|
+ return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
|
|
+ .body(Result.failure(405, "请求方法不支持"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleMediaTypeNotSupported(
|
|
|
+ HttpMediaTypeNotSupportedException e) {
|
|
|
+ log.info("不支持的媒体类型: {}", e.getContentType());
|
|
|
+ return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
|
|
|
+ .body(Result.failure(415, "不支持的Content-Type"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(MaxUploadSizeExceededException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleMaxUploadSize(MaxUploadSizeExceededException e) {
|
|
|
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
|
|
+ .body(Result.failure(400, "上传文件大小超过限制"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
|
|
+ public ResponseEntity<Result<Void>> handleTypeMismatch(MethodArgumentTypeMismatchException e) {
|
|
|
+ // 路径变量转换失败视为资源不存在(如超长/非法 ID 无法匹配任何资源),复用前端"资源不存在"识别逻辑
|
|
|
+ boolean pathVariable = e.getParameter().hasParameterAnnotation(PathVariable.class);
|
|
|
+ if (pathVariable) {
|
|
|
+ log.info("路径参数无效: name={}, value={}", e.getName(), e.getValue());
|
|
|
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
|
|
+ .body(Result.failure(404, "资源不存在"));
|
|
|
+ }
|
|
|
+ log.info("请求参数格式错误: name={}, value={}", e.getName(), e.getValue());
|
|
|
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
|
|
+ .body(Result.failure(400, "请求参数格式错误"));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 业务码映射为 HTTP 状态码,非法值按 400 处理
|
|
|
*/
|