package com.campus.common; import lombok.extern.slf4j.Slf4j; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.HttpStatus; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import java.util.stream.Collectors; @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(HttpMessageNotReadableException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleMessageNotReadableException(HttpMessageNotReadableException ex) { // 不输出请求原文,避免将个人资料或其他敏感参数写入日志。 Throwable cause = ex; while (cause != null) { if (cause instanceof InvalidFormatException) { InvalidFormatException invalid = (InvalidFormatException) cause; if (java.util.Date.class.equals(invalid.getTargetType()) && !invalid.getPath().isEmpty() && "birthday".equals(invalid.getPath().get(invalid.getPath().size() - 1).getFieldName())) { return Result.error(400, "生日格式不正确,请使用 yyyy-MM-dd(例如 2000-06-01)"); } } cause = cause.getCause(); } return Result.error(400, "请求体格式不正确,请检查参数类型和 JSON 格式"); } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleValidationException(MethodArgumentNotValidException ex) { String message = ex.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining("; ")); log.warn("Validation failed: {}", message); return Result.error(400, message); } @ExceptionHandler(BindException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleBindException(BindException ex) { String message = ex.getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining("; ")); log.warn("Binding failed: {}", message); return Result.error(400, message); } @ExceptionHandler(MaxUploadSizeExceededException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleMaxUploadSizeExceededException(MaxUploadSizeExceededException ex) { log.warn("File size exceeded: {}", ex.getMessage()); return Result.error(400, "文件大小超过限制,请压缩后重新上传(最大100MB)"); } @ExceptionHandler(MultipartException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleMultipartException(MultipartException ex) { log.error("Multipart parsing failed: {}", ex.getMessage()); return Result.error(400, "文件上传失败,请检查网络连接或文件格式后重试"); } @ExceptionHandler(BusinessException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result handleBusinessException(BusinessException ex) { log.warn("Business exception: {}", ex.getMessage()); return Result.error(400, ex.getMessage()); } @ExceptionHandler(ForbiddenException.class) @ResponseStatus(HttpStatus.FORBIDDEN) public Result handleForbiddenException(ForbiddenException ex) { log.warn("Forbidden operation: {}", ex.getMessage()); return Result.error(403, ex.getMessage()); } @ExceptionHandler(ServiceUnavailableException.class) @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) public Result handleServiceUnavailableException(ServiceUnavailableException ex) { log.error("Service unavailable: {}", ex.getMessage()); return Result.error(503, ex.getMessage()); } @ExceptionHandler(TooManyRequestsException.class) @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS) public Result handleTooManyRequestsException(TooManyRequestsException ex) { log.warn("Too many requests: {}", ex.getMessage()); return Result.error(429, ex.getMessage()); } @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Result handleRuntimeException(RuntimeException ex) { log.error("Runtime exception: ", ex); return Result.error(500, "服务器内部错误"); } @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Result handleException(Exception ex) { log.error("Unexpected exception: ", ex); return Result.error("服务器内部错误"); } }