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 ? "服务处理失败" : "请求不合法";
|
};
|
}
|
}
|