1、新增system服务国际化配置文件;2、资金管理请求校验国际化;3、全局异常GlobalError增加国际化提示;

This commit is contained in:
gaoqr
2025-11-19 10:45:15 +08:00
parent 4452029813
commit d294b73d81
48 changed files with 661 additions and 174 deletions
@@ -18,19 +18,23 @@ public class GlobalErrorCodeConstants {
// ========== 客户端错误段 ==========
public static final ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
public static final ErrorCode BAD_REQUEST = new ErrorCode(400, "global.error.bad.request");
public static final ErrorCode REQUEST_ORGAN_ID_NOT_EXIST = new ErrorCode(400, "global.error.request.organId.notExist");
public static final ErrorCode NO_PERMISSION_TO_VISIT_ORG = new ErrorCode(400, "global.error.no.permission.to.visit.org");
public static final ErrorCode REQUEST_PARAM_TYPE_ERROR = new ErrorCode(400, "global.error.request.param.type.error");
public static final ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
public static final ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
public static final ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
public static final ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
public static final ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
public static final ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
public static final ErrorCode ORG_PRODUCT_EXPIRED = new ErrorCode(410, "组织产品套餐已过期,请续费后继续使用");
public static final ErrorCode USER_PRODUCTID_NOT_EXIST = new ErrorCode(411, "账号登录异常,套餐信息不存在,请重新登陆");
public static final ErrorCode FORBIDDEN = new ErrorCode(403, "global.error.no.permission");
public static final ErrorCode REQUEST_PARAM_MISSING = new ErrorCode(403, "global.error.request.param.missing");
public static final ErrorCode NOT_FOUND = new ErrorCode(404, "global.error.request.not.found");
public static final ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "global.error.request.method.error");
public static final ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "global.error.request.too.many");
public static final ErrorCode ORG_PRODUCT_EXPIRED = new ErrorCode(410, "global.org.product.expired");
public static final ErrorCode USER_PRODUCTID_NOT_EXIST = new ErrorCode(410, "global.org.product.not.exist");
// ========== 服务端错误段 ==========
public static final ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统繁忙,请稍后再试");
public static final ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "global.error.default.error");
public static final ErrorCode DUPLICATE_DATA_ERROR = new ErrorCode(500, "global.error.request.duplicate.data.error");
public static final ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
// ES文档更新失败
@@ -41,6 +45,5 @@ public class GlobalErrorCodeConstants {
public static final ErrorCode DATA_DATA_ERROR = new ErrorCode(506, "文档数据异常,请勿频繁操作,请稍后重试");
// ========== 自定义错误段 ==========
public static final ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
public static final ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
}
@@ -48,6 +48,12 @@ public class CommonResult<T> implements Serializable {
@JsonInclude(JsonInclude.Include.NON_NULL)
private String traceId;
/**
* 占位补充参数
*/
@JsonIgnore
private Object[] args;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
@@ -74,10 +80,28 @@ public class CommonResult<T> implements Serializable {
return result;
}
public static <T> CommonResult<T> error(Integer code, String message, Object... args) {
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!");
CommonResult<T> result = new CommonResult<>();
result.code = code;
result.msg = message;
result.args = args;
String traceId = MDC.get(TRACE_ID);
if (StringUtils.hasText(traceId)) {
result.traceId = traceId;
}
return result;
}
public static <T> CommonResult<T> error(ErrorCode errorCode) {
return error(errorCode.getCode(), errorCode.getMsg());
}
public static <T> CommonResult<T> error(ErrorCode errorCode, Object... args) {
return error(errorCode.getCode(), errorCode.getMsg(), args);
}
public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>();
result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
@@ -23,7 +23,7 @@ public class CommonStatisticsReqVO {
* 统计维度单位
*/
@Schema(description = "统计维度单位", example = "0", allowableValues = {"0", "1", "2", "3"}, type = "integer")
@NotNull(message = "统计维度单位不能为空")
@NotNull(message = "{statistics.unit.notNull}")
@StatisticsUnitInEnum
private Integer unit;
}
@@ -23,14 +23,14 @@ public class PageParam implements Serializable {
public static final Integer PAGE_SIZE_NONE = -1;
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
@NotNull(message = "页码不能为空")
@Min(value = 1, message = "页码最小值为 1")
@NotNull(message = "{page.pageNo.notNull}")
@Min(value = 1, message = "{page.pageNo.min}")
private Integer pageNo = PAGE_NO;
@Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "每页条数不能为空")
@Min(value = -1, message = "每页条数最小值为 -1")
@Max(value = 100, message = "每页条数最大值为 100")
@NotNull(message = "{page.pageSize.notNull}")
@Min(value = -1, message = "{page.pageSize.min}")
@Max(value = 100, message = "{page.pageSize.max}")
private Integer pageSize = PAGE_SIZE;
}
@@ -0,0 +1,32 @@
package com.cf.imes.framework.common.util.i18n.core.util;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
/**
* 国际化工具类
*
* @author Gqr
* @since 2025/11/18 11:18
*/
@Slf4j
@Component
@AllArgsConstructor
public class I18nUtils {
private MessageSource messageSource;
/**
* 获取国际化描述
*
* @param code
* @param args
* @return
*/
public String getMessage(String code, Object... args) {
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}
}
@@ -0,0 +1,4 @@
/**
* 针对 MessageSource 的基础封装
*/
package com.cf.imes.framework.common.util.i18n;
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
validatedBy = {StatisticsUnitInEnumValidator.class}
)
public @interface StatisticsUnitInEnum {
String message() default "统计维度单位[unit]错误,请检查";
String message() default "{statistics.unit.invalid}";
Class<?>[] groups() default {};
@@ -26,7 +26,7 @@ public @interface InEnum {
*/
Class<? extends IntArrayValuable> value();
String message() default "必须在指定范围 {value}";
String message() default "inEnum.message";
Class<?>[] groups() default {};
@@ -5,16 +5,26 @@ import com.cf.imes.framework.common.core.IntArrayValuable;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Component
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
private List<Integer> values;
@Autowired
private MessageSource messageSource;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
@@ -31,10 +41,12 @@ public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Co
if (CollUtil.containsAll(values, list)) {
return true;
}
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
Locale locale = LocaleContextHolder.getLocale();
String message = messageSource.getMessage(defaultConstraintMessageTemplate, new Object[]{CollUtil.join(list, ",")}, locale);
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replace("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); // 重新添加错误提示语句
return false;
}
@@ -4,15 +4,25 @@ import com.cf.imes.framework.common.core.IntArrayValuable;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Component
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
private List<Integer> values;
@Autowired
private MessageSource messageSource;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
@@ -33,10 +43,12 @@ public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
if (values.contains(value)) {
return true;
}
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
Locale locale = LocaleContextHolder.getLocale();
String message = messageSource.getMessage(defaultConstraintMessageTemplate, new Object[]{values.toString()}, locale);
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replace("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); // 重新添加错误提示语句
return false;
}
@@ -40,7 +40,7 @@ public @interface NumberValid {
*
* @return
*/
String message() default "数字不合法";
String message() default "";
Class<?>[] groups() default {};
@@ -5,19 +5,26 @@ import org.apache.commons.lang3.StringUtils;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Locale;
/**
* @author Gqr
* @since 2024/10/17 11:27
*/
@Component
public class NumberValidator implements ConstraintValidator<NumberValid, Number> {
private static final String ZERO = "0";
private static final String GREATER_THAN_ZERO_NOTIFICATION = "%s必须大于0";
private static final String GREATER_THAN_ZERO_NOTIFICATION = "number.greaterThanZero";
private static final String FLOAT_NUMBER_LENGTH_ERROR_NOTIFICATION = "%s长度不合法,总长度不超过%d位,小数点后不超过%d位";
private static final String FLOAT_NUMBER_LENGTH_ERROR_NOTIFICATION = "number.float.length.error";
private String name;
@@ -25,13 +32,15 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
private int fraction;
@Autowired
private MessageSource messageSource;
@Override
public void initialize(NumberValid constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
this.name = constraintAnnotation.name();
this.integer = constraintAnnotation.integer();
this.fraction = constraintAnnotation.fraction();
;
}
@Override
@@ -67,7 +76,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
*/
private String handleInteger(Integer value) {
if (value < Integer.parseInt(ZERO)) {
return String.format(GREATER_THAN_ZERO_NOTIFICATION, name);
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(GREATER_THAN_ZERO_NOTIFICATION, new Object[]{
messageSource.getMessage(name, null, locale)
}, locale);
}
return null;
}
@@ -80,7 +92,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
*/
private String handleLong(Long value) {
if (value < Long.parseLong(ZERO)) {
return String.format(GREATER_THAN_ZERO_NOTIFICATION, name);
Locale locale = LocaleContextHolder.getLocale();
return messageSource.getMessage(GREATER_THAN_ZERO_NOTIFICATION, new Object[]{
messageSource.getMessage(name, null, locale)
}, locale);
}
return null;
}
@@ -92,8 +107,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
* @return
*/
private String handleDouble(Double value) {
Locale locale = LocaleContextHolder.getLocale();
String fieldName = messageSource.getMessage(name, null, locale);
if (value < Double.parseDouble(ZERO)) {
return String.format(GREATER_THAN_ZERO_NOTIFICATION, name);
return messageSource.getMessage(GREATER_THAN_ZERO_NOTIFICATION, new Object[]{fieldName}, locale);
}
// 处理科学计数法,保留小数点后四位
DecimalFormat decimalFormat = new DecimalFormat("#.####");
@@ -110,7 +127,9 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
// 总位数不超过 8 位,小数点后位数不超过 3 位
if (integerPartLength > integer || fractionalPartLength > fraction) {
return String.format(FLOAT_NUMBER_LENGTH_ERROR_NOTIFICATION, name, integer, fraction);
return messageSource.getMessage(FLOAT_NUMBER_LENGTH_ERROR_NOTIFICATION, new Object[]{
fieldName, integer, fraction
}, locale);
}
return null;
}
@@ -122,8 +141,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
* @return
*/
private String handleBigDecimal(BigDecimal value) {
Locale locale = LocaleContextHolder.getLocale();
String fieldName = messageSource.getMessage(name, null, locale);
if (value.compareTo(BigDecimal.ZERO) < 0) {
return String.format(GREATER_THAN_ZERO_NOTIFICATION, name);
return messageSource.getMessage(GREATER_THAN_ZERO_NOTIFICATION, new Object[]{fieldName}, locale);
}
String[] split = value.toPlainString().split("\\.");
@@ -137,7 +158,9 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
// 总位数不超过 8 位,小数点后位数不超过 3 位
if (integerPartLength > integer || fractionalPartLength > fraction) {
return String.format(FLOAT_NUMBER_LENGTH_ERROR_NOTIFICATION, name, integer, fraction);
return messageSource.getMessage(FLOAT_NUMBER_LENGTH_ERROR_NOTIFICATION, new Object[]{
fieldName, integer, fraction
}, locale);
}
return null;
}
@@ -1,6 +1,7 @@
package com.cf.imes.framework.organ.config;
import com.cf.imes.framework.common.enums.WebFilterOrderEnum;
import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils;
import com.cf.imes.framework.mybatis.core.util.MyBatisUtils;
import com.cf.imes.framework.redis.config.ChenfengCacheProperties;
import com.cf.imes.framework.organ.core.aop.OrganIgnoreAspect;
@@ -80,12 +81,13 @@ public class ChenfengOrganAutoConfiguration {
@Bean
public FilterRegistrationBean<OrganSecurityWebFilter> organSecurityWebFilter(OrganProperties tenantProperties,
WebProperties webProperties,
GlobalExceptionHandler globalExceptionHandler,
OrganFrameworkService organFrameworkService) {
WebProperties webProperties,
GlobalExceptionHandler globalExceptionHandler,
OrganFrameworkService organFrameworkService,
I18nUtils i18nUtils) {
FilterRegistrationBean<OrganSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new OrganSecurityWebFilter(tenantProperties, webProperties,
globalExceptionHandler, organFrameworkService));
globalExceptionHandler, organFrameworkService, i18nUtils));
registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);
return registrationBean;
}
@@ -7,6 +7,7 @@ import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.util.servlet.ServletUtils;
import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.organ.config.OrganProperties;
@@ -28,6 +29,9 @@ import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.NO_PERMISSION_TO_VISIT_ORG;
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.REQUEST_ORGAN_ID_NOT_EXIST;
/**
* 多组织 Security Web 过滤器
* 1. 如果是登陆的用户,校验是否有权限访问该组织,避免越权问题。
@@ -48,15 +52,19 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
private final GlobalExceptionHandler globalExceptionHandler;
private final OrganFrameworkService organFrameworkService;
private final I18nUtils i18nUtils;
public OrganSecurityWebFilter(OrganProperties organProperties,
WebProperties webProperties,
GlobalExceptionHandler globalExceptionHandler,
OrganFrameworkService organFrameworkService) {
OrganFrameworkService organFrameworkService,
I18nUtils i18nUtils) {
super(webProperties);
this.organProperties = organProperties;
this.pathMatcher = new AntPathMatcher();
this.globalExceptionHandler = globalExceptionHandler;
this.organFrameworkService = organFrameworkService;
this.i18nUtils = i18nUtils;
}
@Override
@@ -77,8 +85,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
log.error("[doFilterInternal][组织({}) User({}/{}) 越权访问组织({}) URL({}/{})]",
user.getOrganId(), user.getId(), user.getUserType(),
OrganContextHolder.getOrganId(), request.getRequestURI(), request.getMethod());
ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.FORBIDDEN.getCode(),
"您无权访问该组织的数据"));
ServletUtils.writeJSON(response, CommonResult.error(NO_PERMISSION_TO_VISIT_ORG.getCode(), i18nUtils.getMessage(NO_PERMISSION_TO_VISIT_ORG.getMsg())));
return;
}
}
@@ -88,8 +95,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
// 2. 如果请求未带组织的编号,不允许访问。
if (organId == null) {
log.error("[doFilterInternal][URL({}/{}) 未传递组织编号]", request.getRequestURI(), request.getMethod());
ServletUtils.writeJSON(response, CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
"请求的组织标识未传递,请进行排查"));
ServletUtils.writeJSON(response, CommonResult.error(REQUEST_ORGAN_ID_NOT_EXIST.getCode(), i18nUtils.getMessage(REQUEST_ORGAN_ID_NOT_EXIST.getMsg())));
return;
}
// 3. 校验组织是合法,例如说被禁用、到期
@@ -108,6 +114,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
// 组织失效返回401踢出系统
needUnauthorizedWhenOrgExpired(ex, result);
result.setMsg(i18nUtils.getMessage(result.getMsg(), null));
ServletUtils.writeJSON(response, result);
return;
}
@@ -133,7 +140,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
// 非管理端校验产品的有效性
if (!isManageEndPoint) {
if (ObjectUtil.isNull(productId)) {
throw new ServiceException(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), GlobalErrorCodeConstants.USER_PRODUCTID_NOT_EXIST.getMsg());
throw new ServiceException(GlobalErrorCodeConstants.USER_PRODUCTID_NOT_EXIST);
}
if (!isProductIgnoreUrl(request)) {
organFrameworkService.validOrgProduct(user.getOrganId(), productId);
@@ -2,6 +2,7 @@ package com.cf.imes.framework.web.config;
import com.cf.imes.framework.aop.ControllerResolver;
import com.cf.imes.framework.common.enums.WebFilterOrderEnum;
import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils;
import com.cf.imes.framework.web.core.filter.CacheRequestBodyFilter;
import com.cf.imes.framework.web.core.filter.DataSourceFilter;
import com.cf.imes.framework.web.core.handler.GlobalExceptionHandler;
@@ -15,6 +16,7 @@ import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfigu
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestController;
@@ -59,7 +61,12 @@ public class ChenfengWebAutoConfiguration implements WebMvcConfigurer {
}
@Bean
public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogApi apiErrorLogApi) {
public I18nUtils i18nUtils(MessageSource messageSource) {
return new I18nUtils(messageSource);
}
@Bean
public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogApi apiErrorLogApi, I18nUtils i18nUtils) {
return new GlobalExceptionHandler(applicationName, apiErrorLogApi);
}
@@ -11,7 +11,6 @@ import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.servlet.ServletUtils;
import com.cf.imes.module.infra.api.logger.ApiErrorLogApi;
import com.cf.imes.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -33,12 +32,9 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.*;
@@ -109,7 +105,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public CommonResult missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
log.warn(String.format("==========[missingServletRequestParameterExceptionHandler]==========%s", ex.getMessage()));
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数缺失:%s", ex.getParameterName()));
return CommonResult.error(REQUEST_PARAM_MISSING, ex.getParameterName());
}
/**
@@ -120,7 +116,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public CommonResult methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) {
log.warn(String.format("==========[missingServletRequestParameterExceptionHandler]==========%s", ex.getMessage()));
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误,%s:%s", ex.getName(), ex.getValue()));
return CommonResult.error(REQUEST_PARAM_TYPE_ERROR, ex.getName() + ":" + ex.getValue());
}
/**
@@ -147,7 +143,7 @@ public class GlobalExceptionHandler {
if (CharSequenceUtil.isNotEmpty(defaultMessage) && fieldError.contains(ConstraintViolation.class)) {
return CommonResult.error(BAD_REQUEST.getCode(), defaultMessage);
} else {
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确,%s:%s", fieldError.getField(), fieldError.getRejectedValue()));
return CommonResult.error(BAD_REQUEST, fieldError.getField() + ":" + fieldError.getRejectedValue());
}
}
@@ -162,7 +158,7 @@ public class GlobalExceptionHandler {
if (StringUtils.isNotEmpty(message)) {
return CommonResult.error(BAD_REQUEST.getCode(), message);
} else {
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", message));
return CommonResult.error(BAD_REQUEST, message);
}
}
@@ -186,7 +182,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(NoHandlerFoundException.class)
public CommonResult noHandlerFoundExceptionHandler(NoHandlerFoundException ex) {
log.warn(String.format("==========[noHandlerFoundExceptionHandler]==========%s", ex.getMessage()));
return CommonResult.error(NOT_FOUND.getCode(), String.format("请求地址不存在:%s", ex.getRequestURL()));
return CommonResult.error(NOT_FOUND, ex.getRequestURL());
}
/**
@@ -197,7 +193,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public CommonResult httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) {
log.warn(String.format("==========[httpRequestMethodNotSupportedExceptionHandler]==========%s", ex.getMessage()));
return CommonResult.error(METHOD_NOT_ALLOWED.getCode(), String.format("请求方法不正确:%s", ex.getMessage()));
return CommonResult.error(METHOD_NOT_ALLOWED, ex.getMessage());
}
/**
@@ -223,25 +219,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public CommonResult httpMessageNotReadableExceptionHandler(HttpServletRequest req, HttpMessageNotReadableException ex) {
log.warn(String.format("==========[httpMessageNotReadableExceptionHandler]==========%s", ex.getMessage()));
Throwable rootCause = ex.getRootCause();
// 无效格式异常处理。
if (rootCause instanceof InvalidFormatException) { // MismatchedInputException
InvalidFormatException e = (InvalidFormatException) rootCause;
return CommonResult.error(BAD_REQUEST.getCode(), String.format("参数校验失败:'%s' 格式出错", e.getValue()));
}
// 文件格式错误处理
if (rootCause instanceof IOException) {
// 使用正则提取数值
Pattern pattern = Pattern.compile("Numeric value \\(([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)\\) out of range");
Matcher matcher = pattern.matcher(rootCause.getMessage());
if (matcher.find()) {
String invalidValue = matcher.group(1);
return CommonResult.error(BAD_REQUEST.getCode(), String.format("参数校验失败:'%s' 超出范围", invalidValue));
}
}
return CommonResult.error(BAD_REQUEST.getCode(), "请求参数不合法");
return CommonResult.error(BAD_REQUEST);
}
/**
@@ -269,7 +247,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value = DuplicateKeyException.class)
public CommonResult duplicateKeyExceptionHandler(DuplicateKeyException ex) {
log.warn(String.format("==========[duplicateKeyExceptionHandler]==========%s", ex.getMessage()));
return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), "请求数据已存在,请检查");
return CommonResult.error(DUPLICATE_DATA_ERROR);
}
/**
@@ -293,7 +271,7 @@ public class GlobalExceptionHandler {
// 插入异常日志
this.createExceptionLog(req, ex);
// 返回 ERROR CommonResult
return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
return CommonResult.error(INTERNAL_SERVER_ERROR);
}
private void createExceptionLog(HttpServletRequest req, Throwable e) {
@@ -3,6 +3,9 @@ package com.cf.imes.framework.web.core.handler;
import com.cf.imes.framework.apilog.core.filter.ApiAccessLogFilter;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.web.core.util.WebFrameworkUtils;
import jakarta.annotation.Resource;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
@@ -24,6 +27,9 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class GlobalResponseBodyHandler implements ResponseBodyAdvice {
@Resource
private MessageSource messageSource;
@Override
@SuppressWarnings("NullableProblems") // 避免 IDEA 警告
public boolean supports(MethodParameter returnType, Class converterType) {
@@ -38,6 +44,14 @@ public class GlobalResponseBodyHandler implements ResponseBodyAdvice {
@SuppressWarnings("NullableProblems") // 避免 IDEA 警告
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof CommonResult) {
CommonResult<?> res = (CommonResult<?>) body;
if (!res.isSuccess()) {
String key = res.getMsg();
String i18nMsg = messageSource.getMessage(key, res.getArgs(), key, LocaleContextHolder.getLocale());
res.setMsg(i18nMsg);
}
}
// 记录 Controller 结果
WebFrameworkUtils.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult<?>) body);
return body;