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 UNAUTHORIZED = new ErrorCode(401, "账号未登录");
public static final ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限"); public static final ErrorCode FORBIDDEN = new ErrorCode(403, "global.error.no.permission");
public static final ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到"); public static final ErrorCode REQUEST_PARAM_MISSING = new ErrorCode(403, "global.error.request.param.missing");
public static final ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确"); public static final ErrorCode NOT_FOUND = new ErrorCode(404, "global.error.request.not.found");
public static final ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许 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, "请求过于频繁,请稍后重试"); 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, "组织产品套餐已过期,请续费后继续使用"); public static final ErrorCode ORG_PRODUCT_EXPIRED = new ErrorCode(410, "global.org.product.expired");
public static final ErrorCode USER_PRODUCTID_NOT_EXIST = new ErrorCode(411, "账号登录异常,套餐信息不存在,请重新登陆"); 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, "功能未实现/未开启"); public static final ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
// ES文档更新失败 // ES文档更新失败
@@ -41,6 +45,5 @@ public class GlobalErrorCodeConstants {
public static final ErrorCode DATA_DATA_ERROR = new ErrorCode(506, "文档数据异常,请勿频繁操作,请稍后重试"); 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, "演示模式,禁止写操作"); public static final ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
} }
@@ -48,6 +48,12 @@ public class CommonResult<T> implements Serializable {
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
private String traceId; private String traceId;
/**
* 占位补充参数
*/
@JsonIgnore
private Object[] args;
/** /**
* 将传入的 result 对象,转换成另外一个泛型结果的对象 * 将传入的 result 对象,转换成另外一个泛型结果的对象
* *
@@ -74,10 +80,28 @@ public class CommonResult<T> implements Serializable {
return result; 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) { public static <T> CommonResult<T> error(ErrorCode errorCode) {
return error(errorCode.getCode(), errorCode.getMsg()); 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) { public static <T> CommonResult<T> success(T data) {
CommonResult<T> result = new CommonResult<>(); CommonResult<T> result = new CommonResult<>();
result.code = GlobalErrorCodeConstants.SUCCESS.getCode(); result.code = GlobalErrorCodeConstants.SUCCESS.getCode();
@@ -23,7 +23,7 @@ public class CommonStatisticsReqVO {
* 统计维度单位 * 统计维度单位
*/ */
@Schema(description = "统计维度单位", example = "0", allowableValues = {"0", "1", "2", "3"}, type = "integer") @Schema(description = "统计维度单位", example = "0", allowableValues = {"0", "1", "2", "3"}, type = "integer")
@NotNull(message = "统计维度单位不能为空") @NotNull(message = "{statistics.unit.notNull}")
@StatisticsUnitInEnum @StatisticsUnitInEnum
private Integer unit; private Integer unit;
} }
@@ -23,14 +23,14 @@ public class PageParam implements Serializable {
public static final Integer PAGE_SIZE_NONE = -1; public static final Integer PAGE_SIZE_NONE = -1;
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1") @Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
@NotNull(message = "页码不能为空") @NotNull(message = "{page.pageNo.notNull}")
@Min(value = 1, message = "页码最小值为 1") @Min(value = 1, message = "{page.pageNo.min}")
private Integer pageNo = PAGE_NO; private Integer pageNo = PAGE_NO;
@Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "每页条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "每页条数不能为空") @NotNull(message = "{page.pageSize.notNull}")
@Min(value = -1, message = "每页条数最小值为 -1") @Min(value = -1, message = "{page.pageSize.min}")
@Max(value = 100, message = "每页条数最大值为 100") @Max(value = 100, message = "{page.pageSize.max}")
private Integer pageSize = PAGE_SIZE; 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} validatedBy = {StatisticsUnitInEnumValidator.class}
) )
public @interface StatisticsUnitInEnum { public @interface StatisticsUnitInEnum {
String message() default "统计维度单位[unit]错误,请检查"; String message() default "{statistics.unit.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -26,7 +26,7 @@ public @interface InEnum {
*/ */
Class<? extends IntArrayValuable> value(); Class<? extends IntArrayValuable> value();
String message() default "必须在指定范围 {value}"; String message() default "inEnum.message";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -5,16 +5,26 @@ import com.cf.imes.framework.common.core.IntArrayValuable;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; 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.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Component
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> { public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
private List<Integer> values; private List<Integer> values;
@Autowired
private MessageSource messageSource;
@Override @Override
public void initialize(InEnum annotation) { public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants(); IntArrayValuable[] values = annotation.value().getEnumConstants();
@@ -31,10 +41,12 @@ public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Co
if (CollUtil.containsAll(values, list)) { if (CollUtil.containsAll(values, list)) {
return true; return true;
} }
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
Locale locale = LocaleContextHolder.getLocale();
String message = messageSource.getMessage(defaultConstraintMessageTemplate, new Object[]{CollUtil.join(list, ",")}, locale);
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值) // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); // 重新添加错误提示语句
.replace("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
return false; return false;
} }
@@ -4,15 +4,25 @@ import com.cf.imes.framework.common.core.IntArrayValuable;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; 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.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Component
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> { public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
private List<Integer> values; private List<Integer> values;
@Autowired
private MessageSource messageSource;
@Override @Override
public void initialize(InEnum annotation) { public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants(); IntArrayValuable[] values = annotation.value().getEnumConstants();
@@ -33,10 +43,12 @@ public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
if (values.contains(value)) { if (values.contains(value)) {
return true; return true;
} }
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
Locale locale = LocaleContextHolder.getLocale();
String message = messageSource.getMessage(defaultConstraintMessageTemplate, new Object[]{values.toString()}, locale);
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值) // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate() context.buildConstraintViolationWithTemplate(message).addConstraintViolation(); // 重新添加错误提示语句
.replace("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
return false; return false;
} }
@@ -40,7 +40,7 @@ public @interface NumberValid {
* *
* @return * @return
*/ */
String message() default "数字不合法"; String message() default "";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -5,19 +5,26 @@ import org.apache.commons.lang3.StringUtils;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; 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.math.BigDecimal;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Locale;
/** /**
* @author Gqr * @author Gqr
* @since 2024/10/17 11:27 * @since 2024/10/17 11:27
*/ */
@Component
public class NumberValidator implements ConstraintValidator<NumberValid, Number> { public class NumberValidator implements ConstraintValidator<NumberValid, Number> {
private static final String ZERO = "0"; 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; private String name;
@@ -25,13 +32,15 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
private int fraction; private int fraction;
@Autowired
private MessageSource messageSource;
@Override @Override
public void initialize(NumberValid constraintAnnotation) { public void initialize(NumberValid constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation); ConstraintValidator.super.initialize(constraintAnnotation);
this.name = constraintAnnotation.name(); this.name = constraintAnnotation.name();
this.integer = constraintAnnotation.integer(); this.integer = constraintAnnotation.integer();
this.fraction = constraintAnnotation.fraction(); this.fraction = constraintAnnotation.fraction();
;
} }
@Override @Override
@@ -67,7 +76,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
*/ */
private String handleInteger(Integer value) { private String handleInteger(Integer value) {
if (value < Integer.parseInt(ZERO)) { 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; return null;
} }
@@ -80,7 +92,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
*/ */
private String handleLong(Long value) { private String handleLong(Long value) {
if (value < Long.parseLong(ZERO)) { 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; return null;
} }
@@ -92,8 +107,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
* @return * @return
*/ */
private String handleDouble(Double value) { private String handleDouble(Double value) {
Locale locale = LocaleContextHolder.getLocale();
String fieldName = messageSource.getMessage(name, null, locale);
if (value < Double.parseDouble(ZERO)) { 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("#.####"); DecimalFormat decimalFormat = new DecimalFormat("#.####");
@@ -110,7 +127,9 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
// 总位数不超过 8 位,小数点后位数不超过 3 位 // 总位数不超过 8 位,小数点后位数不超过 3 位
if (integerPartLength > integer || fractionalPartLength > fraction) { 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; return null;
} }
@@ -122,8 +141,10 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
* @return * @return
*/ */
private String handleBigDecimal(BigDecimal value) { private String handleBigDecimal(BigDecimal value) {
Locale locale = LocaleContextHolder.getLocale();
String fieldName = messageSource.getMessage(name, null, locale);
if (value.compareTo(BigDecimal.ZERO) < 0) { 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("\\."); String[] split = value.toPlainString().split("\\.");
@@ -137,7 +158,9 @@ public class NumberValidator implements ConstraintValidator<NumberValid, Number>
// 总位数不超过 8 位,小数点后位数不超过 3 位 // 总位数不超过 8 位,小数点后位数不超过 3 位
if (integerPartLength > integer || fractionalPartLength > fraction) { 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; return null;
} }
@@ -1,6 +1,7 @@
package com.cf.imes.framework.organ.config; package com.cf.imes.framework.organ.config;
import com.cf.imes.framework.common.enums.WebFilterOrderEnum; 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.mybatis.core.util.MyBatisUtils;
import com.cf.imes.framework.redis.config.ChenfengCacheProperties; import com.cf.imes.framework.redis.config.ChenfengCacheProperties;
import com.cf.imes.framework.organ.core.aop.OrganIgnoreAspect; import com.cf.imes.framework.organ.core.aop.OrganIgnoreAspect;
@@ -80,12 +81,13 @@ public class ChenfengOrganAutoConfiguration {
@Bean @Bean
public FilterRegistrationBean<OrganSecurityWebFilter> organSecurityWebFilter(OrganProperties tenantProperties, public FilterRegistrationBean<OrganSecurityWebFilter> organSecurityWebFilter(OrganProperties tenantProperties,
WebProperties webProperties, WebProperties webProperties,
GlobalExceptionHandler globalExceptionHandler, GlobalExceptionHandler globalExceptionHandler,
OrganFrameworkService organFrameworkService) { OrganFrameworkService organFrameworkService,
I18nUtils i18nUtils) {
FilterRegistrationBean<OrganSecurityWebFilter> registrationBean = new FilterRegistrationBean<>(); FilterRegistrationBean<OrganSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new OrganSecurityWebFilter(tenantProperties, webProperties, registrationBean.setFilter(new OrganSecurityWebFilter(tenantProperties, webProperties,
globalExceptionHandler, organFrameworkService)); globalExceptionHandler, organFrameworkService, i18nUtils));
registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER); registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);
return registrationBean; 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.exception.enums.GlobalErrorCodeConstants;
import com.cf.imes.framework.common.pojo.CommonResult; import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.util.servlet.ServletUtils; 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.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.organ.config.OrganProperties; import com.cf.imes.framework.organ.config.OrganProperties;
@@ -28,6 +29,9 @@ import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; 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 过滤器 * 多组织 Security Web 过滤器
* 1. 如果是登陆的用户,校验是否有权限访问该组织,避免越权问题。 * 1. 如果是登陆的用户,校验是否有权限访问该组织,避免越权问题。
@@ -48,15 +52,19 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
private final GlobalExceptionHandler globalExceptionHandler; private final GlobalExceptionHandler globalExceptionHandler;
private final OrganFrameworkService organFrameworkService; private final OrganFrameworkService organFrameworkService;
private final I18nUtils i18nUtils;
public OrganSecurityWebFilter(OrganProperties organProperties, public OrganSecurityWebFilter(OrganProperties organProperties,
WebProperties webProperties, WebProperties webProperties,
GlobalExceptionHandler globalExceptionHandler, GlobalExceptionHandler globalExceptionHandler,
OrganFrameworkService organFrameworkService) { OrganFrameworkService organFrameworkService,
I18nUtils i18nUtils) {
super(webProperties); super(webProperties);
this.organProperties = organProperties; this.organProperties = organProperties;
this.pathMatcher = new AntPathMatcher(); this.pathMatcher = new AntPathMatcher();
this.globalExceptionHandler = globalExceptionHandler; this.globalExceptionHandler = globalExceptionHandler;
this.organFrameworkService = organFrameworkService; this.organFrameworkService = organFrameworkService;
this.i18nUtils = i18nUtils;
} }
@Override @Override
@@ -77,8 +85,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
log.error("[doFilterInternal][组织({}) User({}/{}) 越权访问组织({}) URL({}/{})]", log.error("[doFilterInternal][组织({}) User({}/{}) 越权访问组织({}) URL({}/{})]",
user.getOrganId(), user.getId(), user.getUserType(), user.getOrganId(), user.getId(), user.getUserType(),
OrganContextHolder.getOrganId(), request.getRequestURI(), request.getMethod()); 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; return;
} }
} }
@@ -88,8 +95,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
// 2. 如果请求未带组织的编号,不允许访问。 // 2. 如果请求未带组织的编号,不允许访问。
if (organId == null) { if (organId == null) {
log.error("[doFilterInternal][URL({}/{}) 未传递组织编号]", request.getRequestURI(), request.getMethod()); 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; return;
} }
// 3. 校验组织是合法,例如说被禁用、到期 // 3. 校验组织是合法,例如说被禁用、到期
@@ -108,6 +114,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
// 组织失效返回401踢出系统 // 组织失效返回401踢出系统
needUnauthorizedWhenOrgExpired(ex, result); needUnauthorizedWhenOrgExpired(ex, result);
result.setMsg(i18nUtils.getMessage(result.getMsg(), null));
ServletUtils.writeJSON(response, result); ServletUtils.writeJSON(response, result);
return; return;
} }
@@ -133,7 +140,7 @@ public class OrganSecurityWebFilter extends ApiRequestFilter {
// 非管理端校验产品的有效性 // 非管理端校验产品的有效性
if (!isManageEndPoint) { if (!isManageEndPoint) {
if (ObjectUtil.isNull(productId)) { 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)) { if (!isProductIgnoreUrl(request)) {
organFrameworkService.validOrgProduct(user.getOrganId(), productId); 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.aop.ControllerResolver;
import com.cf.imes.framework.common.enums.WebFilterOrderEnum; 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.CacheRequestBodyFilter;
import com.cf.imes.framework.web.core.filter.DataSourceFilter; import com.cf.imes.framework.web.core.filter.DataSourceFilter;
import com.cf.imes.framework.web.core.handler.GlobalExceptionHandler; 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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -59,7 +61,12 @@ public class ChenfengWebAutoConfiguration implements WebMvcConfigurer {
} }
@Bean @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); 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.framework.common.util.servlet.ServletUtils;
import com.cf.imes.module.infra.api.logger.ApiErrorLogApi; import com.cf.imes.module.infra.api.logger.ApiErrorLogApi;
import com.cf.imes.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; import com.cf.imes.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -33,12 +32,9 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException; import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException; import jakarta.validation.ValidationException;
import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.*; import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.*;
@@ -109,7 +105,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value = MissingServletRequestParameterException.class) @ExceptionHandler(value = MissingServletRequestParameterException.class)
public CommonResult missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) { public CommonResult missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
log.warn(String.format("==========[missingServletRequestParameterExceptionHandler]==========%s", ex.getMessage())); 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) @ExceptionHandler(MethodArgumentTypeMismatchException.class)
public CommonResult methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) { public CommonResult methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) {
log.warn(String.format("==========[missingServletRequestParameterExceptionHandler]==========%s", ex.getMessage())); 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)) { if (CharSequenceUtil.isNotEmpty(defaultMessage) && fieldError.contains(ConstraintViolation.class)) {
return CommonResult.error(BAD_REQUEST.getCode(), defaultMessage); return CommonResult.error(BAD_REQUEST.getCode(), defaultMessage);
} else { } 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)) { if (StringUtils.isNotEmpty(message)) {
return CommonResult.error(BAD_REQUEST.getCode(), message); return CommonResult.error(BAD_REQUEST.getCode(), message);
} else { } 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) @ExceptionHandler(NoHandlerFoundException.class)
public CommonResult noHandlerFoundExceptionHandler(NoHandlerFoundException ex) { public CommonResult noHandlerFoundExceptionHandler(NoHandlerFoundException ex) {
log.warn(String.format("==========[noHandlerFoundExceptionHandler]==========%s", ex.getMessage())); 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) @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public CommonResult httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) { public CommonResult httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) {
log.warn(String.format("==========[httpRequestMethodNotSupportedExceptionHandler]==========%s", ex.getMessage())); 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) @ExceptionHandler(value = HttpMessageNotReadableException.class)
public CommonResult httpMessageNotReadableExceptionHandler(HttpServletRequest req, HttpMessageNotReadableException ex) { public CommonResult httpMessageNotReadableExceptionHandler(HttpServletRequest req, HttpMessageNotReadableException ex) {
log.warn(String.format("==========[httpMessageNotReadableExceptionHandler]==========%s", ex.getMessage())); log.warn(String.format("==========[httpMessageNotReadableExceptionHandler]==========%s", ex.getMessage()));
return CommonResult.error(BAD_REQUEST);
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(), "请求参数不合法");
} }
/** /**
@@ -269,7 +247,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value = DuplicateKeyException.class) @ExceptionHandler(value = DuplicateKeyException.class)
public CommonResult duplicateKeyExceptionHandler(DuplicateKeyException ex) { public CommonResult duplicateKeyExceptionHandler(DuplicateKeyException ex) {
log.warn(String.format("==========[duplicateKeyExceptionHandler]==========%s", ex.getMessage())); 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); this.createExceptionLog(req, ex);
// 返回 ERROR CommonResult // 返回 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) { 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.apilog.core.filter.ApiAccessLogFilter;
import com.cf.imes.framework.common.pojo.CommonResult; import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.web.core.util.WebFrameworkUtils; 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.core.MethodParameter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
@@ -24,6 +27,9 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice @ControllerAdvice
public class GlobalResponseBodyHandler implements ResponseBodyAdvice { public class GlobalResponseBodyHandler implements ResponseBodyAdvice {
@Resource
private MessageSource messageSource;
@Override @Override
@SuppressWarnings("NullableProblems") // 避免 IDEA 警告 @SuppressWarnings("NullableProblems") // 避免 IDEA 警告
public boolean supports(MethodParameter returnType, Class converterType) { public boolean supports(MethodParameter returnType, Class converterType) {
@@ -38,6 +44,14 @@ public class GlobalResponseBodyHandler implements ResponseBodyAdvice {
@SuppressWarnings("NullableProblems") // 避免 IDEA 警告 @SuppressWarnings("NullableProblems") // 避免 IDEA 警告
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) { 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 结果 // 记录 Controller 结果
WebFrameworkUtils.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult<?>) body); WebFrameworkUtils.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult<?>) body);
return body; return body;
@@ -19,12 +19,12 @@ public class AdvertisementRePublishReqVO {
@Schema(description = "投放开始时间,重新发布时传") @Schema(description = "投放开始时间,重新发布时传")
@NotNull(message = "投放开始时间不能为空") @NotNull(message = "{ad.startTime.notNull}")
private LocalDate startTime; private LocalDate startTime;
@Schema(description = "投放结束时间,重新发布时传") @Schema(description = "投放结束时间,重新发布时传")
@NotNull(message = "投放结束时间不能为空") @NotNull(message = "{ad.endTime.notNull}")
private LocalDate endTime; private LocalDate endTime;
@@ -25,41 +25,41 @@ public class AdvertisementSaveReqVO {
@Schema(description = "广告名称") @Schema(description = "广告名称")
@NotBlank(message = "广告名称不能为空") @NotBlank(message = "{ad.name.notBlank}")
@Size(max = 32, message = "广告名称长度不能超过 32 个字符") @Size(max = 32, message = "{ad.name.size}")
private String adName; private String adName;
@Schema(description = "广告位置,字典键值", example = "[1,2]") @Schema(description = "广告位置,字典键值", example = "[1,2]")
@NotEmpty(message = "广告位置不能为空") @NotEmpty(message = "{ad.position.notEmpty}")
@InScope(value = AdvertisementPositionEnum.class, desc = "广告位置") @InScope(value = AdvertisementPositionEnum.class, desc = "广告位置")
private List<Integer> adPosition; private List<Integer> adPosition;
@Schema(description = "广告图片文件路径") @Schema(description = "广告图片文件路径")
@NotBlank(message = "广告图片文件路径不能为空") @NotBlank(message = "{ad.adImagePath.notBlank}")
@Size(max = 255, message = "广告图片文件路径长度不能超过 255 个字符") @Size(max = 255, message = "{ad.adImagePath.size}")
private String adImagePath; private String adImagePath;
@Schema(description = "投放开始时间") @Schema(description = "投放开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@NotNull(message = "投放开始时间不能为空") @NotNull(message = "{ad.startTime.notNull}")
private LocalDate startTime; private LocalDate startTime;
@Schema(description = "投放结束时间") @Schema(description = "投放结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@NotNull(message = "投放结束时间不能为空") @NotNull(message = "{ad.endTime.notNull}")
private LocalDate endTime; private LocalDate endTime;
@Schema(description = "跳转路径") @Schema(description = "跳转路径")
@Size(max = 255, message = "链接长度不能超过 255 个字符") @Size(max = 255, message = "{ad.adRedirectUrl.size}")
private String adRedirectUrl; private String adRedirectUrl;
@Schema(description = "广告顺序") @Schema(description = "广告顺序")
@NumberValid(name = "广告顺序") @NumberValid(name = "ad.sort.field")
private Integer sort; private Integer sort;
} }
@@ -22,7 +22,7 @@ public class ProductDelayPageReqVO extends PageParam {
private String organName; private String organName;
@Schema(description = "产品名称") @Schema(description = "产品名称")
@Size(max = 64, message = "产品名称长度不能超过64个字符") @Size(max = 64, message = "{product.delay.page.productName.size}")
private String productName; private String productName;
@Schema(description = "延期时长") @Schema(description = "延期时长")
@@ -32,6 +32,6 @@ public class ProductDelayPageReqVO extends PageParam {
private LocalDate[] createTime; private LocalDate[] createTime;
@Schema(description = "操作人") @Schema(description = "操作人")
@Size(max = 64, message = "操作人长度不能超过64个字符") @Size(max = 64, message = "{product.delay.page.operator.size}")
private String operator; private String operator;
} }
@@ -14,23 +14,23 @@ import lombok.Data;
@Data @Data
public class ProductDelaySaveReqVO { public class ProductDelaySaveReqVO {
@Schema(description = "组织编号") @Schema(description = "组织编号")
@NotNull(message = "组织编号不能为空") @NotNull(message = "{product.delay.save.organId.notNull}")
private Long organId; private Long organId;
@Schema(description = "购买记录编号") @Schema(description = "购买记录编号")
@NotNull(message = "购买记录编号不能为空") @NotNull(message = "{product.delay.save.purchaseId.notNull}")
private Long purchaseId; private Long purchaseId;
@Schema(description = "延期时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "延期时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "延期时长不能为空") @NotNull(message = "{product.delay.save.duration.notNull}")
private Integer delayDuration; private Integer delayDuration;
@Schema(description = "延期时长单位,0:年、1:月、2:日", example = "1") @Schema(description = "延期时长单位,0:年、1:月、2:日", example = "1")
@NotNull(message = "延期时长单位不能为空") @NotNull(message = "{product.delay.save.duration.unit.notNull}")
@DurationUnitValid @DurationUnitValid
private Integer delayDurationUnit; private Integer delayDurationUnit;
@Schema(description = "备注") @Schema(description = "备注")
@Size(max = 1024, message = "备注不能超过1024个字符") @Size(max = 1024, message = "{product.delay.save.remark.size}")
private String remark; private String remark;
} }
@@ -29,14 +29,14 @@ public class InvoiceAmountAndAvailablePageReqVO extends PageParam {
private LocalDateTime[] createTime; private LocalDateTime[] createTime;
@Schema(description = "开票金额起") @Schema(description = "开票金额起")
@DecimalMin(value = "0", message = "开票金额必须大于等于零") @DecimalMin(value = "0", message = "{invoce.page.amount.min}")
@DecimalMax(value = "100000", message = "开票金额不能超过十万") @DecimalMax(value = "100000", message = "{invoice.page.amount.max}")
@Digits(integer = 6, fraction = 2, message = "开票金额格式不正确") @Digits(integer = 6, fraction = 2, message = "{invoice.page.amount.format}")
private BigDecimal invoiceAmountStart; private BigDecimal invoiceAmountStart;
@Schema(description = "开票金额止") @Schema(description = "开票金额止")
@DecimalMin(value = "0", message = "开票金额必须大于等于零") @DecimalMin(value = "0", message = "{invoce.page.amount.min}")
@DecimalMax(value = "100000", message = "开票金额不能超过十万") @DecimalMax(value = "100000", message = "{invoice.page.amount.max}")
@Digits(integer = 6, fraction = 2, message = "开票金额格式不正确") @Digits(integer = 6, fraction = 2, message = "{invoice.page.amount.format}")
private BigDecimal invoiceAmountEnd; private BigDecimal invoiceAmountEnd;
} }
@@ -12,7 +12,7 @@ public class InvoiceConfirmReqVO {
@Schema(description = "发票申请记录编号",example = "1") @Schema(description = "发票申请记录编号",example = "1")
@NotNull(message = "发票申请记录编号不能为空") @NotNull(message = "{invoice.confirm.id.notNull}")
private Long id; private Long id;
@@ -21,11 +21,11 @@ public class InvoiceConfirmReqVO {
@Schema(description = "发票号") @Schema(description = "发票号")
@Length(max = 32, message = "发票号长度不能超过 32 个字符") @Length(max = 32, message = "{invoice.confirm.invoiceNo.length}")
private String invoiceNo; private String invoiceNo;
@Schema(description = "备注") @Schema(description = "备注")
@Length(max = 1024, message = "备注1长度不能超过 1024 个字符") @Length(max = 1024, message = "{invoice.confirm.remark.length}")
private String remark; private String remark;
} }
@@ -2,6 +2,7 @@ package com.cf.imes.module.system.controller.admin.funds.invoice.vo;
import com.cf.imes.framework.common.pojo.PageParam; import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.module.system.validation.invoice.InvoiceStatusValid;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.DecimalMax; import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.DecimalMin;
@@ -32,6 +33,7 @@ public class InvoiceDetailsPageReqVO extends PageParam {
@Schema(description = "发票状态,1待开票 2已开票 3开票失败") @Schema(description = "发票状态,1待开票 2已开票 3开票失败")
@InvoiceStatusValid
private Integer status; private Integer status;
@@ -44,15 +46,15 @@ public class InvoiceDetailsPageReqVO extends PageParam {
@Schema(description = "发票金额起") @Schema(description = "发票金额起")
@DecimalMin(value = "0", message = "开票金额必须大于等于零") @DecimalMin(value = "0", message = "{invoice.page.amount.min}")
@DecimalMax(value = "100000", message = "开票金额不能超过十万") @DecimalMax(value = "100000", message = "{invoice.page.amount.max}")
@Digits(integer = 6, fraction = 2, message = "开票金额格式不正确") @Digits(integer = 6, fraction = 2, message = "{invoice.page.amount.format}")
private BigDecimal invoiceAmountStart; private BigDecimal invoiceAmountStart;
@Schema(description = "发票金额起") @Schema(description = "发票金额起")
@DecimalMin(value = "0", message = "开票金额必须大于等于零") @DecimalMin(value = "0", message = "{invoice.page.amount.min}")
@DecimalMax(value = "100000", message = "开票金额不能超过十万") @DecimalMax(value = "100000", message = "{invoice.page.amount.max}")
@Digits(integer = 6, fraction = 2, message = "开票金额格式不正确") @Digits(integer = 6, fraction = 2, message = "{invoice.page.amount.format}")
private BigDecimal invoiceAmountEnd; private BigDecimal invoiceAmountEnd;
@Schema(description = "开票时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]") @Schema(description = "开票时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
@@ -13,8 +13,8 @@ import java.util.List;
public class InvoiceRecordsReqVO { public class InvoiceRecordsReqVO {
@Schema(description = "购买记录ID") @Schema(description = "购买记录ID")
@Size(max = 100, message = "单次开票选择记录不能超过100条") @Size(max = 100, message = "{invoice.apply.purchase.size}")
@NotEmpty @NotEmpty(message = "{invoice.apply.purchase.list.notEmpty}")
private List<Long> purchaseRecordId; private List<Long> purchaseRecordId;
} }
@@ -21,13 +21,13 @@ public class InvoiceTitleSaveReqVO {
@Schema(description = "发票抬头") @Schema(description = "发票抬头")
@NotBlank(message = "发票抬头不能为空") @NotBlank(message = "{invoice.title.save.title.notBlank}")
@Size(max = 64, message = "发票抬头长度不能超过64个字符") @Size(max = 64, message = "{invoice.title.save.title.size}")
private String invoiceTitle; private String invoiceTitle;
@Schema(description = "开具类型,0企业 1个人") @Schema(description = "开具类型,0企业 1个人")
@NotNull(message = "开具类型不能为空") @NotNull(message = "{invoice.title.save.issueType.notNull}")
@InvoiceIssueTypeValid @InvoiceIssueTypeValid
private Integer issueType; private Integer issueType;
@@ -39,34 +39,34 @@ public class InvoiceTitleSaveReqVO {
@Schema(description = "统一社会信用代码") @Schema(description = "统一社会信用代码")
@Size(max = 20, message = "统一社会信用代码长度不能超过18个字符") @Size(max = 20, message = "{invoice.title.save.taxNumber.size}")
private String taxNumber; private String taxNumber;
@Schema(description = "开户银行名称") @Schema(description = "开户银行名称")
@Size(max = 64, message = "开户银行名称长度不能超过64个字符") @Size(max = 64, message = "{invoice.title.save.bankName.size}")
private String bankName; private String bankName;
@Schema(description = "开户银行账号") @Schema(description = "开户银行账号")
@Size(max = 40, message = "开户银行账号长度不能超过40个字符") @Size(max = 40, message = "{invoice.title.save.bankAccount.size}")
private String bankAccount; private String bankAccount;
@Schema(description = "注册场所地址") @Schema(description = "注册场所地址")
@Size(max = 64, message = "注册场所地址长度不能超过64个字符") @Size(max = 64, message = "{invoice.title.save.registeredAddress.size}")
private String registeredAddress; private String registeredAddress;
@Schema(description = "注册固定电话") @Schema(description = "注册固定电话")
@Size(max = 20, message = "注册固定电话长度不能超过20个字符") @Size(max = 20, message = "{invoice.title.save.fixedPhone.size}")
private String fixedPhone; private String fixedPhone;
@Schema(description = "邮箱") @Schema(description = "邮箱")
@NotNull(message = "邮箱不能为空") @NotNull(message = "{invoice.title.save.email.notNull}")
@Size(max = 64, message = "邮箱长度不能超过64个字符") @Size(max = 64, message = "{invoice.title.save.email.size}")
@Email(message = "邮箱格式不正确") @Email(message = "{invoice.title.save.email.format}")
private String email; private String email;
@@ -15,37 +15,37 @@ public class ProductPromotionSaveReqVO {
private Long id; private Long id;
@Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") @Schema(description = "活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@NotNull(message = "活动名称不能为空") @NotNull(message = "{product.promotion.save.activityName.notNull}")
private String activityName; private String activityName;
@Schema(description = "购买时长",example = "1") @Schema(description = "购买时长",example = "1")
@NotNull(message = "购买时长不能为空") @NotNull(message = "{product.promotion.save.purchase.duration.notNull}")
private Integer purchaseDuration; private Integer purchaseDuration;
@Schema(description = "产品ID", example = "1") @Schema(description = "产品ID", example = "1")
@NotNull(message = "关联产品不能为空") @NotNull(message = "{product.promotion.save.productId.notNull}")
private Long productId; private Long productId;
@Schema(description = "购买时长单位,0:年、1:月、2:日",example = "1") @Schema(description = "购买时长单位,0:年、1:月、2:日",example = "1")
@DurationUnitValid @DurationUnitValid
@NotNull(message = "购买时长单位不能为空") @NotNull(message = "{product.promotion.save.purchase.duration.unit.notNull}")
private Integer purchaseDurationUnit; private Integer purchaseDurationUnit;
@Schema(description = "赠送时长",example = "1") @Schema(description = "赠送时长",example = "1")
@NotNull(message = "赠送时长不能为空") @NotNull(message = "{product.promotion.save.gift.duration.notNull}")
private Integer giftDuration; private Integer giftDuration;
@Schema(description = "赠送时长单位",example = "1") @Schema(description = "赠送时长单位",example = "1")
@DurationUnitValid @DurationUnitValid
@NotNull(message = "赠送时长单位不能为空") @NotNull(message = "{product.promotion.save.gift.duration.unit.notNull}")
private Integer giftDurationUnit; private Integer giftDurationUnit;
@Schema(description = "开始时间") @Schema(description = "开始时间")
@NotNull(message = "开始时间不能为空") @NotNull(message = "{product.promotion.save.startTime.notNull}")
private LocalDateTime startTime; private LocalDateTime startTime;
@Schema(description = "结束时间") @Schema(description = "结束时间")
@NotNull(message = "结束时间不能为空") @NotNull(message = "{product.promotion.save.endTime.notNull}")
private LocalDateTime endTime; private LocalDateTime endTime;
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@@ -29,8 +29,8 @@ public class ProductPageReqVO extends PageParam {
private Integer status; private Integer status;
@Schema(description = "单价") @Schema(description = "单价")
@DecimalMin(value = "0.01", message = "单价必须大于零") @DecimalMin(value = "0.01", message = "{product.page.price.min}")
@DecimalMax(value = "100000", message = "单价不能超过十万") @DecimalMax(value = "100000", message = "{product.page.price.max}")
@Digits(integer = 6, fraction = 2, message = "单价格式不正确") @Digits(integer = 6, fraction = 2, message = "{product.page.price.format}")
private BigDecimal price; private BigDecimal price;
} }
@@ -21,15 +21,15 @@ public class ProductSaveReqVO {
private Long id; private Long id;
@Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@NotEmpty(message = "产品名称不能为空") @NotEmpty(message = "{product.save.productName.notEmpty}")
@Size(max = 64, message = "产品名称长度不能超过64") @Size(max = 64, message = "{product.save.productName.size}")
private String productName; private String productName;
@Schema(description = "产品建议价格") @Schema(description = "产品建议价格")
@NotNull(message = "产品建议价格不能为空") @NotNull(message = "{product.save.price.notNull}")
@DecimalMin(value = "0.01", message = "产品建议价格必须大于零") @DecimalMin(value = "0.01", message = "{product.save.price.min}")
@DecimalMax(value = "100000", message = "产品建议价格不能超过十万") @DecimalMax(value = "100000", message = "{product.save.price.max}")
@Digits(integer = 6, fraction = 2, message = "产品建议价格格式不正确") @Digits(integer = 6, fraction = 2, message = "{product.save.price.format}")
private BigDecimal price; private BigDecimal price;
@Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED)
@@ -19,24 +19,24 @@ public class ProductDetailsSaveReqVO {
private Long id; private Long id;
@Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "产品名称不能为空") @NotNull(message = "{product.detail.save.productId.notNull}")
private Long productId; private Long productId;
@Schema(description = "产品时长,格式:时长值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "产品时长,格式:时长值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Min(value = 1, message = "产品时长必须大于0") @Min(value = 1, message = "{product.detail.save.duration.min}")
@NotNull(message = "产品时长不能为空") @NotNull(message = "{product.detail.duration.notNull}")
private Integer duration; private Integer duration;
@Schema(description = "产品时长单位,0:年", example = "1") @Schema(description = "产品时长单位,0:年", example = "1")
@ProductDurationUnitValid @ProductDurationUnitValid
@NotNull(message = "产品时长单位不能为空") @NotNull(message = "{product.detail.duration.unit.notNull}")
private Integer durationUnit; private Integer durationUnit;
@Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8") @Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
@DecimalMax(value = "100", message = "折扣必须小于等于100") @DecimalMax(value = "100", message = "{product.detail.save.discount.max}")
@DecimalMin(value = "0", message = "折扣必须大于等于0") @DecimalMin(value = "0", message = "{product.detail.save.discount.min}")
@Digits(integer = 3, fraction = 0, message = "折扣必须是0 ~ 100间的整数") @Digits(integer = 3, fraction = 0, message = "{product.detail.save.discount.format}")
@NotNull(message = "折扣不能为空") @NotNull(message = "{product.detail.save.discount.notNull}")
private BigDecimal discount; private BigDecimal discount;
} }
@@ -50,7 +50,7 @@ public class PurchaseRecordPageReqVO extends PageParam {
private String creator; private String creator;
@Schema(description = "订单号") @Schema(description = "订单号")
@Size(max = 20, message = "订单号长度不能超过20个字符") @Size(max = 20, message = "{purchase.record.page.id.size}")
private String id; private String id;
@Schema(description = "有效时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]") @Schema(description = "有效时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
@@ -61,8 +61,8 @@ public class PurchaseRecordPageReqVO extends PageParam {
private LocalDateTime[] endTimeSection; private LocalDateTime[] endTimeSection;
@Schema(description = "产品金额") @Schema(description = "产品金额")
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零") @DecimalMin(value = "0.01", message = "{purchase.record.page.productPrice.min}")
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万") @DecimalMax(value = "100000", message = "{purchase.record.page.productPrice.max}")
@Digits(integer = 6, fraction = 2, message = "产品购买金额格式不正确") @Digits(integer = 6, fraction = 2, message = "{purchase.record.page.productPrice.format}")
private BigDecimal productPrice; private BigDecimal productPrice;
} }
@@ -32,13 +32,13 @@ public class SalesDetailsPageReqVO extends PageParam {
private Long id; private Long id;
@Schema(description = "产品金额") @Schema(description = "产品金额")
@DecimalMin(value = "0.01", message = "产品购买金额必须大于零") @DecimalMin(value = "0.01", message = "{sales.detail.page.productPrice.min}")
@DecimalMax(value = "100000", message = "产品购买金额不能超过十万") @DecimalMax(value = "100000", message = "{sales.detail.page.productPrice.max}")
@Digits(integer = 6, fraction = 2, message = "产品购买金额格式不正确") @Digits(integer = 6, fraction = 2, message = "{sales.detail.page.productPrice.format}")
private BigDecimal productPrice; private BigDecimal productPrice;
@Schema(description = "操作人") @Schema(description = "操作人")
@Size(max = 64, message = "操作人长度不能超过64个字符") @Size(max = 64, message = "{sales.detail.page.operator.size}")
private String operator; private String operator;
@Schema(description = "交易时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]") @Schema(description = "交易时间", example = "[2022-07-01 01:01:01 ,2022-07-01 01:01:01]")
@@ -1,10 +0,0 @@
package com.cf.imes.module.system.controller.admin.pay.vo;
/**
* 产品购买请求校验组
*
* @author Gqr
* @since 2025/9/24 16:28
*/
public interface PayProductGroup {
}
@@ -19,15 +19,15 @@ public class PayProductOrderUnifiedReqVO {
* 产品定价规则编号 * 产品定价规则编号
*/ */
@Schema(description = "产品定价规则编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "产品定价规则编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "产品定价规则编号不能为空") @NotNull(message = "{pay.product.productDetailsId.notNull}")
private Long productDetailsId; private Long productDetailsId;
@Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "产品ID不能为空") @NotNull(message = "{pay.product.productId.notNull}")
private Long productId; private Long productId;
@Schema(description = "产品支付渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @Schema(description = "产品支付渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "产品支付渠道编码不能为空", groups = PayProductGroup.class) @NotNull(message = "{pay.product.productChannelCode.notNull}")
@PayChannelCodeValid @PayChannelCodeValid
private Integer productChannelCode; private Integer productChannelCode;
@@ -1,9 +1,20 @@
package com.cf.imes.module.system.framework.trans.config; package com.cf.imes.module.system.framework.trans.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import java.util.Locale;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TransProperties.class) @EnableConfigurationProperties(TransProperties.class)
public class TransConfiguration { public class TransConfiguration {
@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
} }
@@ -19,7 +19,7 @@ import java.lang.annotation.*;
validatedBy = {AdvertisementStatusValidator.class} validatedBy = {AdvertisementStatusValidator.class}
) )
public @interface AdvertisementStatus { public @interface AdvertisementStatus {
String message() default "广告状态不合法,0:全部、1:未发布、2:已发布、3:已结束"; String message() default "{ad.status.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
validatedBy = {InvoiceIssueTypeValidtor.class} validatedBy = {InvoiceIssueTypeValidtor.class}
) )
public @interface InvoiceIssueTypeValid { public @interface InvoiceIssueTypeValid {
String message() default "不支持的发票开具类型"; String message() default "{invoice.title.issueType.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -0,0 +1,32 @@
package com.cf.imes.module.system.validation.invoice;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 发票状态校验注解
*
* @author Gqr
* @since 2025/11/17 16:37
*/
@Target({
ElementType.FIELD,
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {InvoiceStatusValidtor.class}
)
public @interface InvoiceStatusValid {
String message() default "{invoice.status.invalid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,27 @@
package com.cf.imes.module.system.validation.invoice;
import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* 发票状态校验器
*
* @author Gqr
* @since 2025/8/21 14:37
*/
public class InvoiceStatusValidtor implements ConstraintValidator<InvoiceTypeValid, Integer> {
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
if (ObjectUtil.isNull(integer)) {
return true;
}
for (InvoiceStatusEnum invoiceStatusEnum : InvoiceStatusEnum.values()) {
if (invoiceStatusEnum.getCode().equals(integer)) {
return true;
}
}
return false;
}
}
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
validatedBy = {InvoiceTypeValidtor.class} validatedBy = {InvoiceTypeValidtor.class}
) )
public @interface InvoiceTypeValid { public @interface InvoiceTypeValid {
String message() default "不支持的发票类型"; String message() default "{invoice.title.type.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -22,7 +22,7 @@ import java.lang.annotation.Target;
validatedBy = {PayChannelCodeValidator.class} validatedBy = {PayChannelCodeValidator.class}
) )
public @interface PayChannelCodeValid { public @interface PayChannelCodeValid {
String message() default "不支持的支付渠道"; String message() default "{pay.channelCode.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
validatedBy = {DurationUnitValidator.class} validatedBy = {DurationUnitValidator.class}
) )
public @interface DurationUnitValid { public @interface DurationUnitValid {
String message() default "时长单位不合法"; String message() default "{product.duration.unit.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
validatedBy = {ProductDurationUnitValidator.class} validatedBy = {ProductDurationUnitValidator.class}
) )
public @interface ProductDurationUnitValid { public @interface ProductDurationUnitValid {
String message() default "时长单位不合法"; String message() default "{product.detail.duration.unit.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -18,7 +18,7 @@ import java.lang.annotation.*;
) )
public @interface ProductStatus { public @interface ProductStatus {
String message() default "产品状态不合法,0:待上架 1:已上架 2:已下架"; String message() default "{product.status.invalid}";
Class<?>[] groups() default {}; Class<?>[] groups() default {};
@@ -97,7 +97,8 @@ spring:
redis: redis:
repositories: repositories:
enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度
messages:
basename: message
trans: trans:
config: config:
appId: 20251110002494620 appId: 20251110002494620
@@ -0,0 +1,102 @@
ad.adImagePath.notBlank=广告图片文件路径不能为空
ad.adImagePath.size=广告图片文件路径长度不能超过 255 个字符
ad.adRedirectUrl.size=链接长度不能超过 255 个字符
ad.endTime.notNull=投放结束时间不能为空
ad.name.notBlank=广告名称不能为空
ad.name.size=广告名称长度不能超过 32 个字符
ad.position.notEmpty=广告位置不能为空
ad.sort.field=广告顺序
ad.startTime.notNull=投放开始时间不能为空
ad.status.invalid=广告状态不合法
inEnum.message=必须在指定范围 {0}
invoice.apply.purchase.list.notEmpty=单词开票选择购买记录不能为空
invoice.apply.purchase.size=单次开票选择购买记录不能超过100条
invoice.confirm.id.notNull=发票申请记录编号不能为空
invoice.confirm.invoiceNo.length=发票号长度不能超过 32 个字符
invoice.confirm.remark.length=备注长度不能超过 1024 个字符
invoice.page.amount.format=开票金额格式不正确
invoice.page.amount.max=开票金额不能超过十万
invoice.page.amount.min=开票金额必须大于等于零
invoice.status.invalid=不支持的发票状态
invoice.title.issueType.invalid=不支持的发票开具类型
invoice.title.save.bankAccount.size=开户银行账号长度不能超过40个字符
invoice.title.save.bankName.size=开户银行名称长度不能超过64个字符
invoice.title.save.email.format=邮箱格式不正确
invoice.title.save.email.notNull=邮箱不能为空
invoice.title.save.email.size=邮箱长度不能超过64个字符
invoice.title.save.fixedPhone.size=注册固定电话长度不能超过20个字符
invoice.title.save.issueType.notNull=开具类型不能为空
invoice.title.save.registeredAddress.size=注册场所地址长度不能超过64个字符
invoice.title.save.taxNumber.size=统一社会信用代码长度不能超过18个字符
invoice.title.save.title.notBlank=发票抬头不能为空
invoice.title.save.title.size=发票抬头长度不能超过64个字符
invoice.title.type.invalid=不支持的发票类型
number.float.length.error={0} 长度不合法,总长度不超过{1}位,小数点后不超过{2}位
number.greaterThanZero={0} 必须大于0
page.pageNo.min=页码最小值为 1
page.pageNo.notNull=页码不能为空
page.pageSize.max=每页条数最大值为 100
page.pageSize.min=每页条数最小值为 -1
page.pageSize.notNull=每页条数不能为空
product.delay.page.operator.size=操作人长度不能超过64个字符
product.delay.page.productName.size=产品名称长度不能超过64个字符
product.delay.save.duration.notNull=延期时长不能为空
product.delay.save.duration.unit.notNull=延期时长单位不能为空
product.delay.save.organId.notNull=组织编号不能为空
product.delay.save.purchaseId.notNull=购买记录编号不能为空
product.delay.save.remark.size=备注不能超过1024个字符
product.detail.duration.notNull=产品时长不能为空
product.detail.duration.unit.invalid=产品折扣时长单位不合法
product.detail.duration.unit.notNull=产品时长单位不能为空
product.detail.save.discount.format=折扣必须是0 ~ 100间的整数
product.detail.save.discount.max=折扣必须小于等于100
product.detail.save.discount.min=折扣必须大于等于0
product.detail.save.discount.notNull=折扣不能为空
product.detail.save.duration.min=产品时长必须大于0
product.detail.save.productId.notNull=产品名称不能为空
product.duration.unit.invalid=时长单位不合法
product.page.price.format=单价格式不正确
product.page.price.max=单价不能超过十万
product.page.price.min=单价必须大于零
product.promotion.save.activityName.notNull=活动名称不能为空
product.promotion.save.endTime.notNull=结束时间不能为空
product.promotion.save.gift.duration.notNull=赠送时长不能为空
product.promotion.save.gift.duration.unit.notNull=赠送时长单位不能为空
product.promotion.save.productId.notNull=关联产品不能为空
product.promotion.save.purchase.duration.notNull=购买时长不能为空
product.promotion.save.purchase.duration.unit.notNull=购买时长单位不能为空
product.promotion.save.startTime.notNull=开始时间不能为空
product.save.price.format=产品建议价格格式不正确
product.save.price.max=产品建议价格不能超过十万
product.save.price.min=产品建议价格必须大于零
product.save.price.notNull=产品建议价格不能为空
product.save.productName.notEmpty=产品名称不能为空
product.save.productName.size=产品名称长度不能超过64
product.status.invalid=产品状态不合法
purchase.record.page.id.size=订单号长度不能超过20个字符
purchase.record.page.productPrice.format=产品购买金额格式不正确
purchase.record.page.productPrice.max=产品购买金额不能超过十万
purchase.record.page.productPrice.min=产品购买金额必须大于零
sales.detail.page.operator.size=操作人长度不能超过64个字符
sales.detail.page.productPrice.format=产品购买金额格式不正确
sales.detail.page.productPrice.max=产品购买金额不能超过十万
sales.detail.page.productPrice.min=产品购买金额必须大于零
statistics.unit.invalid=统计维度单位不合法
statistics.unit.notNull=统计维度单位不能为空
pay.channelCode.invalid=不支持的支付渠道
pay.product.productChannelCode.notNull=产品支付渠道编码不能为空
pay.product.productDetailsId.notNull=产品定价规则编号不能为空
pay.product.productId.notNull=产品ID不能为空
global.error.request.param.missing=请求参数缺失:{0}
global.error.bad.request=请求参数不正确:{0}
global.error.request.param.type.error=请求参数类型错误:{0}
global.error.request.not.found=请求未找到:{0}
global.error.request.method.error=请求方法不正确:{0}
global.error.request.too.many=请求过于频繁,请稍后重试
global.error.request.duplicate.data.error=请求数据已存在,请检查
global.error.default.error=系统繁忙,请稍后再试
global.error.no.permission=没有该操作权限
global.error.no.permission.to.visit.org=您无权访问该组织的数据
global.error.request.organId.notExist=请求的组织标识未传递,请进行排查
global.org.product.not.exist=账号登录异常,套餐信息不存在,请重新登陆
global.org.product.expired=组织产品套餐已过期,请续费后继续使用
@@ -0,0 +1,102 @@
ad.adImagePath.notBlank=廣告圖片檔案路徑不能為空
ad.adImagePath.size=廣告圖片檔案路徑長度不能超過 255 個字元
ad.adRedirectUrl.size=連結長度不能超過 255 個字元
ad.endTime.notNull=投放結束時間不能為空
ad.name.notBlank=廣告名稱不能為空
ad.name.size=廣告名稱長度不能超過 32 個字元
ad.position.notEmpty=廣告位置不能為空
ad.sort.field=廣告順序
ad.startTime.notNull=投放開始時間不能為空
ad.status.invalid=廣告狀態不合法
inEnum.message=必須在指定範圍 {0}
invoice.apply.purchase.list.notEmpty=單次開票選擇的購買記錄不能為空
invoice.apply.purchase.size=單次開票選擇的購買記錄不能超過 100 條
invoice.confirm.id.notNull=發票申請記錄編號不能為空
invoice.confirm.invoiceNo.length=發票號長度不能超過 32 個字元
invoice.confirm.remark.length=備註長度不能超過 1024 個字元
invoice.page.amount.format=開票金額格式不正確
invoice.page.amount.max=開票金額不能超過十萬
invoice.page.amount.min=開票金額必須大於等於零
invoice.status.invalid=不支援的發票狀態
invoice.title.issueType.invalid=不支援的發票開具類型
invoice.title.save.bankAccount.size=開戶銀行帳號長度不能超過 40 個字元
invoice.title.save.bankName.size=開戶銀行名稱長度不能超過 64 個字元
invoice.title.save.email.format=郵箱格式不正確
invoice.title.save.email.notNull=郵箱不能為空
invoice.title.save.email.size=郵箱長度不能超過 64 個字元
invoice.title.save.fixedPhone.size=註冊固定電話長度不能超過 20 個字元
invoice.title.save.issueType.notNull=開具類型不能為空
invoice.title.save.registeredAddress.size=註冊場所地址長度不能超過 64 個字元
invoice.title.save.taxNumber.size=統一社會信用代碼長度不能超過 18 個字元
invoice.title.save.title.notBlank=發票抬頭不能為空
invoice.title.save.title.size=發票抬頭長度不能超過 64 個字元
invoice.title.type.invalid=不支援的發票類型
number.float.length.error={0} 長度不合法,總長度不能超過 {1} 位,小數點後不能超過 {2} 位
number.greaterThanZero={0} 必須大於 0
page.pageNo.min=頁碼最小值為 1
page.pageNo.notNull=頁碼不能為空
page.pageSize.max=每頁條數最大值為 100
page.pageSize.min=每頁條數最小值為 -1
page.pageSize.notNull=每頁條數不能為空
product.delay.page.operator.size=操作人長度不能超過 64 個字元
product.delay.page.productName.size=產品名稱長度不能超過 64 個字元
product.delay.save.duration.notNull=延期時長不能為空
product.delay.save.duration.unit.notNull=延期時長單位不能為空
product.delay.save.organId.notNull=組織編號不能為空
product.delay.save.purchaseId.notNull=購買記錄編號不能為空
product.delay.save.remark.size=備註不能超過 1024 個字元
product.detail.duration.notNull=產品時長不能為空
product.detail.duration.unit.invalid=產品折扣時長單位不合法
product.detail.duration.unit.notNull=產品時長單位不能為空
product.detail.save.discount.format=折扣必須是 0 ~ 100 之間的整數
product.detail.save.discount.max=折扣必須小於等於 100
product.detail.save.discount.min=折扣必須大於等於 0
product.detail.save.discount.notNull=折扣不能為空
product.detail.save.duration.min=產品時長必須大於 0
product.detail.save.productId.notNull=產品名稱不能為空
product.duration.unit.invalid=時長單位不合法
product.page.price.format=單價格式不正確
product.page.price.max=單價不能超過十萬
product.page.price.min=單價必須大於零
product.promotion.save.activityName.notNull=活動名稱不能為空
product.promotion.save.endTime.notNull=結束時間不能為空
product.promotion.save.gift.duration.notNull=贈送時長不能為空
product.promotion.save.gift.duration.unit.notNull=贈送時長單位不能為空
product.promotion.save.productId.notNull=關聯產品不能為空
product.promotion.save.purchase.duration.notNull=購買時長不能為空
product.promotion.save.purchase.duration.unit.notNull=購買時長單位不能為空
product.promotion.save.startTime.notNull=開始時間不能為空
product.save.price.format=產品建議價格格式不正確
product.save.price.max=產品建議價格不能超過十萬
product.save.price.min=產品建議價格必須大於零
product.save.price.notNull=產品建議價格不能為空
product.save.productName.notEmpty=產品名稱不能為空
product.save.productName.size=產品名稱長度不能超過 64 個字元
product.status.invalid=產品狀態不合法
purchase.record.page.id.size=訂單號長度不能超過 20 個字元
purchase.record.page.productPrice.format=產品購買金額格式不正確
purchase.record.page.productPrice.max=產品購買金額不能超過十萬
purchase.record.page.productPrice.min=產品購買金額必須大於零
sales.detail.page.operator.size=操作人長度不能超過 64 個字元
sales.detail.page.productPrice.format=產品購買金額格式不正確
sales.detail.page.productPrice.max=產品購買金額不能超過十萬
sales.detail.page.productPrice.min=產品購買金額必須大於零
statistics.unit.invalid=統計維度單位不合法
statistics.unit.notNull=統計維度單位不能為空
pay.channelCode.invalid=不支援的支付渠道
pay.product.productChannelCode.notNull=產品支付渠道編碼不能為空
pay.product.productDetailsId.notNull=產品定價規則編號不能為空
pay.product.productId.notNull=產品 ID 不能為空
global.error.request.param.missing=請求參數缺失:{0}
global.error.bad.request=請求參數不正確:{0}
global.error.request.param.type.error=請求參數類型錯誤:{0}
global.error.request.not.found=請求未找到:{0}
global.error.request.method.error=請求方法不正確:{0}
global.error.request.too.many=請求過於頻繁,請稍後重試
global.error.request.duplicate.data.error=請求數據已存在,請檢查
global.error.default.error=系統繁忙,請稍後再試
global.error.no.permission=您沒有該操作的權限
global.error.no.permission.to.visit.org=您無權訪問該組織的數據
global.error.request.organId.notExist=請求的組織標識未傳遞,請進行排查
global.org.product.not.exist=帳號登入異常,套餐資訊不存在,請重新登入
global.org.product.expired=組織產品套餐已過期,請續費後繼續使用
@@ -0,0 +1,102 @@
ad.adImagePath.notBlank=Ad image file path cannot be empty
ad.adImagePath.size=Ad image file path length cannot exceed 255 characters
ad.adRedirectUrl.size=Redirect URL length cannot exceed 255 characters
ad.endTime.notNull=End time cannot be empty
ad.name.notBlank=Ad name cannot be empty
ad.name.size=Ad name length cannot exceed 32 characters
ad.position.notEmpty=Ad position cannot be empty
ad.sort.field=Ad sort order
ad.startTime.notNull=Start time cannot be empty
ad.status.invalid=Invalid ad status
inEnum.message=Must be within the specified range {0}
invoice.apply.purchase.list.notEmpty=Selected purchase records cannot be empty
invoice.apply.purchase.size=You cannot select more than 100 purchase records at once
invoice.confirm.id.notNull=Invoice application ID cannot be empty
invoice.confirm.invoiceNo.length=Invoice number length cannot exceed 32 characters
invoice.confirm.remark.length=Remark length cannot exceed 1024 characters
invoice.page.amount.format=Invalid amount format
invoice.page.amount.max=Amount cannot exceed 100,000
invoice.page.amount.min=Amount must be greater than or equal to zero
invoice.status.invalid=Unsupported invoice status
invoice.title.issueType.invalid=Unsupported invoice issue type
invoice.title.save.bankAccount.size=Bank account length cannot exceed 40 characters
invoice.title.save.bankName.size=Bank name length cannot exceed 64 characters
invoice.title.save.email.format=Invalid email format
invoice.title.save.email.notNull=Email cannot be empty
invoice.title.save.email.size=Email length cannot exceed 64 characters
invoice.title.save.fixedPhone.size=Fixed phone number length cannot exceed 20 characters
invoice.title.save.issueType.notNull=Issue type cannot be empty
invoice.title.save.registeredAddress.size=Registered address length cannot exceed 64 characters
invoice.title.save.taxNumber.size=Tax identification number length cannot exceed 18 characters
invoice.title.save.title.notBlank=Invoice title cannot be empty
invoice.title.save.title.size=Invoice title length cannot exceed 64 characters
invoice.title.type.invalid=Unsupported invoice type
number.float.length.error={0} length is invalid. Total length must not exceed {1} digits, and decimal places must not exceed {2} digits
number.greaterThanZero={0} must be greater than 0
page.pageNo.min=Page number must be at least 1
page.pageNo.notNull=Page number cannot be empty
page.pageSize.max=Page size cannot exceed 100
page.pageSize.min=Page size must be at least -1
page.pageSize.notNull=Page size cannot be empty
product.delay.page.operator.size=Operator length cannot exceed 64 characters
product.delay.page.productName.size=Product name length cannot exceed 64 characters
product.delay.save.duration.notNull=Delay duration cannot be empty
product.delay.save.duration.unit.notNull=Delay duration unit cannot be empty
product.delay.save.organId.notNull=Organization ID cannot be empty
product.delay.save.purchaseId.notNull=Purchase record ID cannot be empty
product.delay.save.remark.size=Remark length cannot exceed 1024 characters
product.detail.duration.notNull=Product duration cannot be empty
product.detail.duration.unit.invalid=Invalid product duration unit
product.detail.duration.unit.notNull=Product duration unit cannot be empty
product.detail.save.discount.format=Discount must be an integer between 0 and 100
product.detail.save.discount.max=Discount must be less than or equal to 100
product.detail.save.discount.min=Discount must be greater than or equal to 0
product.detail.save.discount.notNull=Discount cannot be empty
product.detail.save.duration.min=Product duration must be greater than 0
product.detail.save.productId.notNull=Product ID cannot be empty
product.duration.unit.invalid=Invalid duration unit
product.page.price.format=Invalid price format
product.page.price.max=Unit price cannot exceed 100,000
product.page.price.min=Unit price must be greater than zero
product.promotion.save.activityName.notNull=Activity name cannot be empty
product.promotion.save.endTime.notNull=End time cannot be empty
product.promotion.save.gift.duration.notNull=Gift duration cannot be empty
product.promotion.save.gift.duration.unit.notNull=Gift duration unit cannot be empty
product.promotion.save.productId.notNull=Product cannot be empty
product.promotion.save.purchase.duration.notNull=Purchase duration cannot be empty
product.promotion.save.purchase.duration.unit.notNull=Purchase duration unit cannot be empty
product.promotion.save.startTime.notNull=Start time cannot be empty
product.save.price.format=Invalid suggested price format
product.save.price.max=Suggested price cannot exceed 100,000
product.save.price.min=Suggested price must be greater than zero
product.save.price.notNull=Suggested price cannot be empty
product.save.productName.notEmpty=Product name cannot be empty
product.save.productName.size=Product name length cannot exceed 64 characters
product.status.invalid=Invalid product status
purchase.record.page.id.size=Order ID length cannot exceed 20 characters
purchase.record.page.productPrice.format=Invalid purchase amount format
purchase.record.page.productPrice.max=Purchase amount cannot exceed 100,000
purchase.record.page.productPrice.min=Purchase amount must be greater than zero
sales.detail.page.operator.size=Operator length cannot exceed 64 characters
sales.detail.page.productPrice.format=Invalid purchase amount format
sales.detail.page.productPrice.max=Purchase amount cannot exceed 100,000
sales.detail.page.productPrice.min=Purchase amount must be greater than zero
statistics.unit.invalid=Invalid statistics unit
statistics.unit.notNull=Statistics unit cannot be empty
pay.channelCode.invalid=Unsupported payment channel
pay.product.productChannelCode.notNull=Product payment channel code cannot be empty
pay.product.productDetailsId.notNull=Product pricing rule ID cannot be empty
pay.product.productId.notNull=Product ID cannot be empty
global.error.request.param.missing=Missing request parameter: {0}
global.error.bad.request=Invalid request parameter: {0}
global.error.request.param.type.error=Request parameter type mismatch: {0}
global.error.request.not.found=Request not found: {0}
global.error.request.method.error=Incorrect request method: {0}
global.error.request.too.many=Too many requests, please try again later
global.error.request.duplicate.data.error=Data already exists, please check
global.error.default.error=System is busy, please try again later
global.error.no.permission=You do not have permission to perform this action
global.error.no.permission.to.visit.org=You do not have permission to access this organizations data
global.error.request.organId.notExist=The requested organization ID was not provided, please check
global.org.product.not.exist=Account login exception: product package does not exist, please log in again
global.org.product.expired=The organization's product package has expired, please renew before continuing to use the service