package com.xly.erp.common.exception;
import com.xly.erp.common.response.Result;
import com.xly.erp.common.response.ResultCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
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;
/**
* 全局异常处理(docs/04 § 1.5 SSoT)。
*
*
REQ-USR-001 T2:把 BusinessException / 参数校验失败 / 唯一键冲突 / 未捕获异常
* 统一转为 {@link Result},失败响应不抛栈到前端。
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 业务异常 → 对应错误码。
*/
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException ex) {
return Result.fail(ex.getResultCode(), ex.getMessage());
}
/**
* Bean Validation(@Valid)失败 → 40001,message 取首个字段错误提示。
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleValidationException(MethodArgumentNotValidException ex) {
FieldError fieldError = ex.getBindingResult().getFieldError();
String message = fieldError != null
? fieldError.getField() + ": " + fieldError.getDefaultMessage()
: ResultCode.PARAM_INVALID.getMessage();
return Result.fail(ResultCode.PARAM_INVALID, message);
}
/**
* 唯一键冲突(并发兜底)→ 40901 用户名已存在。
*/
@ExceptionHandler(DuplicateKeyException.class)
public Result handleDuplicateKeyException(DuplicateKeyException ex) {
return Result.fail(ResultCode.USERNAME_EXISTS, ResultCode.USERNAME_EXISTS.getMessage());
}
/**
* 兜底:未捕获异常记录 ERROR 日志(含栈),对外只返回通用错误码,不泄露内部细节。
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception ex) {
LOG.error("系统异常", ex);
return Result.fail(ResultCode.SYSTEM_ERROR, ResultCode.SYSTEM_ERROR.getMessage());
}
}