“mitao”
2026-03-27 cde5977f64ff1dfa928d1076c059e8865c02d70b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.tcm.training.admin.common;
 
import cn.dev33.satoken.exception.NotLoginException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
 
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeParseException;
 
@RestControllerAdvice
public class GlobalExceptionHandler {
 
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    @ExceptionHandler(NotLoginException.class)
    public ApiResponse<Void> handleNotLogin(NotLoginException e, HttpServletRequest request) {
        log.warn("未登录访问 [{} {}]: {}", request.getMethod(), request.getRequestURI(), e.getMessage());
        return ApiResponse.fail(401, "未登录");
    }
 
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public ApiResponse<Void> handleValidate(Exception e, HttpServletRequest request) {
        log.warn("请求参数校验失败 [{} {}]", request.getMethod(), request.getRequestURI(), e);
        if (e instanceof MethodArgumentNotValidException) {
            FieldError fieldError = ((MethodArgumentNotValidException) e).getBindingResult().getFieldError();
            if (fieldError != null && fieldError.getDefaultMessage() != null) {
                return ApiResponse.fail(400, fieldError.getDefaultMessage());
            }
        }
        if (e instanceof BindException) {
            FieldError fieldError = ((BindException) e).getBindingResult().getFieldError();
            if (fieldError != null && fieldError.getDefaultMessage() != null) {
                return ApiResponse.fail(400, fieldError.getDefaultMessage());
            }
        }
        return ApiResponse.fail(400, "参数错误");
    }
 
    @ExceptionHandler(IllegalArgumentException.class)
    public ApiResponse<Void> handleIllegalArgument(IllegalArgumentException e, HttpServletRequest request) {
        log.warn("业务参数异常 [{} {}]: {}", request.getMethod(), request.getRequestURI(), e.getMessage(), e);
        return ApiResponse.fail(400, e.getMessage());
    }
 
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ApiResponse<Void> handleHttpMessageNotReadable(HttpMessageNotReadableException e, HttpServletRequest request) {
        log.warn("请求体解析失败 [{} {}]", request.getMethod(), request.getRequestURI(), e);
        return ApiResponse.fail(400, resolveReadableMessage(e));
    }
 
    @ExceptionHandler(IOException.class)
    public ApiResponse<Void> handleIO(IOException e, HttpServletRequest request) {
        log.error("IO异常 [{} {}]", request.getMethod(), request.getRequestURI(), e);
        return ApiResponse.fail(500, "文件操作失败: " + e.getMessage());
    }
 
    @ExceptionHandler(Exception.class)
    public ApiResponse<Void> handleException(Exception e, HttpServletRequest request) {
        log.error("未捕获异常 [{} {}]", request.getMethod(), request.getRequestURI(), e);
        return ApiResponse.fail(500, "服务器内部错误");
    }
 
    private String resolveReadableMessage(Throwable throwable) {
        Throwable current = throwable;
        while (current != null) {
            if (current instanceof InvalidFormatException) {
                String message = resolveTimeFormatMessage(((InvalidFormatException) current).getTargetType());
                if (message != null) {
                    return message;
                }
            }
            if (current instanceof DateTimeParseException) {
                return "时间格式错误,请使用 "
                        + DateTimes.LOCAL_DATE_TIME_PATTERN + " / "
                        + DateTimes.LOCAL_DATE_PATTERN + " / "
                        + DateTimes.LOCAL_TIME_PATTERN;
            }
            current = current.getCause();
        }
        return "请求参数格式错误";
    }
 
    private String resolveTimeFormatMessage(Class<?> targetType) {
        if (targetType == null) {
            return null;
        }
        if (LocalDateTime.class.isAssignableFrom(targetType)) {
            return "时间格式错误,需 " + DateTimes.LOCAL_DATE_TIME_PATTERN;
        }
        if (LocalDate.class.isAssignableFrom(targetType)) {
            return "日期格式错误,需 " + DateTimes.LOCAL_DATE_PATTERN;
        }
        if (LocalTime.class.isAssignableFrom(targetType)) {
            return "时间格式错误,需 " + DateTimes.LOCAL_TIME_PATTERN;
        }
        return null;
    }
}