云
9 小时以前 795bc269ba9acfae74620059c4ed64bd121aa288
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
package cn.iocoder.yudao.module.system.framework.agent;
 
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.server.ResponseStatusException;
 
import java.util.LinkedHashMap;
import java.util.Map;
 
/**
 * 智能体桥接专用异常映射:状态码必须真实,Python 才能区分 401/403/409/422。
 * 仅作用于 agent 包,避免影响其他管理端接口的既有响应格式。
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice(basePackages = {
        "cn.iocoder.yudao.module.system.controller.admin.agent",
        "cn.iocoder.yudao.module.mdm.controller.admin.agent"
})
public class AgentBridgeExceptionHandler {
 
    @ExceptionHandler(AgentBridgeException.class)
    public ResponseEntity<CommonResult<Map<String, Object>>> handleAgentBridge(AgentBridgeException ex) {
        return build(ex.getStatusCode().value(), ex.getReason(), ex.getErrorCode(),
                ex.isRetryable(), ex.getDetails());
    }
 
    @ExceptionHandler(ResponseStatusException.class)
    public ResponseEntity<CommonResult<Map<String, Object>>> handleResponseStatus(ResponseStatusException ex) {
        int status = ex.getStatusCode().value();
        return build(status, ex.getReason(), defaultCode(status), false, null);
    }
 
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<CommonResult<Map<String, Object>>> handleAccessDenied(AccessDeniedException ex) {
        return build(HttpStatus.FORBIDDEN.value(), "当前身份缺少物料导入权限(mdm:item:import)",
                "FORBIDDEN", false, null);
    }
 
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<CommonResult<Map<String, Object>>> handleInvalidArgument(MethodArgumentNotValidException ex) {
        FieldError fieldError = ex.getBindingResult().getFieldError();
        String message = fieldError == null ? "请求参数不合法"
                : fieldError.getField() + ":" + fieldError.getDefaultMessage();
        return build(HttpStatus.BAD_REQUEST.value(), message, "BAD_REQUEST", false, null);
    }
 
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<CommonResult<Map<String, Object>>> handleUnreadable(HttpMessageNotReadableException ex) {
        return build(HttpStatus.BAD_REQUEST.value(), "请求体格式不正确", "BAD_REQUEST", false, null);
    }
 
    @ExceptionHandler(Exception.class)
    public ResponseEntity<CommonResult<Map<String, Object>>> handleUnexpected(Exception ex) {
        log.error("[handleUnexpected][智能体桥接未预期异常]", ex);
        return build(HttpStatus.INTERNAL_SERVER_ERROR.value(), "智能体桥接处理失败", "INTERNAL_ERROR", true, null);
    }
 
    private ResponseEntity<CommonResult<Map<String, Object>>> build(
            int status, String message, String errorCode, boolean retryable, Map<String, Object> details) {
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("error_code", errorCode);
        data.put("retryable", retryable);
        data.put("details", details == null ? Map.of() : details);
        CommonResult<Map<String, Object>> result = CommonResult.error(status,
                message == null ? defaultMessage(status) : message);
        result.setData(data);
        return ResponseEntity.status(status).body(result);
    }
 
    private static String defaultCode(int status) {
        return switch (status) {
            case 401 -> "UNAUTHORIZED";
            case 403 -> "FORBIDDEN";
            case 404 -> "NOT_FOUND";
            case 409 -> "CONFLICT";
            case 413 -> "FILE_TOO_LARGE";
            case 422 -> "VALIDATION_FAILED";
            case 503 -> "SERVICE_UNAVAILABLE";
            default -> status >= 500 ? "INTERNAL_ERROR" : "BAD_REQUEST";
        };
    }
 
    private static String defaultMessage(int status) {
        return switch (status) {
            case 401 -> "未登录或登录已过期";
            case 403 -> "当前身份无权访问";
            case 404 -> "资源不存在";
            case 409 -> "请求冲突";
            case 413 -> "文件超过大小限制";
            case 422 -> "请求数据未通过校验";
            case 503 -> "服务暂不可用";
            default -> status >= 500 ? "服务处理失败" : "请求不合法";
        };
    }
}