package com.xly.exception; import com.xly.constant.ErrorCode; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.web.server.ResponseStatusException; 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.RestControllerAdvice; import org.springframework.web.servlet.NoHandlerFoundException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { private Map error(Integer code, String message) { Map body = new LinkedHashMap<>(); body.put("code", code); body.put("message", message); body.put("timestamp", System.currentTimeMillis()); return body; } /** * 鉴权类异常按**真实 HTTP 状态**返回(401/403),不被下面的兜底包装成 200。 * 否则客户端/网关/日志都看不出这是一次被拒绝的越权访问。 */ @ExceptionHandler(ResponseStatusException.class) public ResponseEntity> handleResponseStatus(ResponseStatusException e) { log.warn("拒绝访问 {}: {}", e.getStatusCode(), e.getReason()); return ResponseEntity.status(e.getStatusCode()) .body(error(e.getStatusCode().value(), e.getReason() == null ? "拒绝访问" : e.getReason())); } @ExceptionHandler(Exception.class) public Map handleException(HttpServletRequest request, Exception e) { log.error("请求地址: {}, 全局异常: ", request.getRequestURI(), e); return error(ErrorCode.SYSTEM_ERROR.getCode(), "系统异常: " + e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public Map handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { List errors = e.getBindingResult().getFieldErrors() .stream() .map(err -> err.getField() + ": " + err.getDefaultMessage()) .collect(Collectors.toList()); String message = String.join("; ", errors); log.warn("参数校验失败: {}", message); return error(ErrorCode.PARAM_ERROR.getCode(), message); } @ExceptionHandler(ConstraintViolationException.class) public Map handleConstraintViolationException(ConstraintViolationException e) { List errors = e.getConstraintViolations() .stream() .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage()) .collect(Collectors.toList()); String message = String.join("; ", errors); log.warn("参数约束违反: {}", message); return error(ErrorCode.PARAM_ERROR.getCode(), message); } @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public Map handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { log.warn("HTTP方法不支持: {} {}", e.getMethod(), e.getSupportedHttpMethods()); return error(ErrorCode.BAD_REQUEST.getCode(), "请求方法 '" + e.getMethod() + "' 不支持"); } @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public Map handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { log.warn("媒体类型不支持: {}", e.getContentType()); return error(ErrorCode.BAD_REQUEST.getCode(), "媒体类型不支持: " + e.getContentType()); } @ExceptionHandler(NoHandlerFoundException.class) public Map handleNoHandlerFoundException(NoHandlerFoundException e) { log.warn("接口不存在: {} {}", e.getHttpMethod(), e.getRequestURL()); return error(ErrorCode.NOT_FOUND.getCode(), "接口不存在: " + e.getRequestURL()); } @ExceptionHandler(HttpMessageConversionException.class) public Map handleHttpMessageConversionException(HttpMessageConversionException e) { log.warn("HTTP消息转换异常: {}", e.getMessage()); return error(ErrorCode.PARAM_ERROR.getCode(), "参数格式错误"); } @ExceptionHandler(DataAccessException.class) public Map handleDataAccessException(DataAccessException e) { log.error("数据库异常: {}", e.getMessage(), e); if (e instanceof DuplicateKeyException) { return error(ErrorCode.DATA_EXISTS.getCode(), "数据已存在"); } else if (e instanceof DataIntegrityViolationException) { return error(ErrorCode.DATA_ERROR.getCode(), "数据完整性违反"); } else if (e instanceof BadSqlGrammarException) { return error(ErrorCode.DB_ERROR.getCode(), "SQL语法错误"); } return error(ErrorCode.DB_ERROR.getCode(), ErrorCode.DB_ERROR.getMessage()); } }