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()); } }