diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/validation/CheckUtils.java b/continew-starter-core/src/main/java/top/continew/starter/core/validation/CheckUtils.java
new file mode 100644
index 00000000..5225cbf2
--- /dev/null
+++ b/continew-starter-core/src/main/java/top/continew/starter/core/validation/CheckUtils.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.starter.core.validation;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import top.continew.starter.core.constant.StringConstants;
+import top.continew.starter.core.exception.BusinessException;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * 业务参数校验工具类(抛出 500 ServiceException)
+ *
+ * @author Charles7c
+ * @see BusinessException
+ * @since 1.0.0
+ */
+public class CheckUtils extends Validator {
+
+ private static final Class EXCEPTION_TYPE = BusinessException.class;
+
+ private CheckUtils() {
+ }
+
+ /**
+ * 如果不存在,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param entityName 实体名
+ * @param fieldName 字段名
+ * @param fieldValue 字段值
+ */
+ public static void throwIfNotExists(Object obj, String entityName, String fieldName, Object fieldValue) {
+ String message = "%s 为 [%s] 的 %s 记录已不存在".formatted(fieldName, fieldValue, CharSequenceUtil
+ .replace(entityName, "DO", StringConstants.EMPTY));
+ throwIfNull(obj, message, EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNull(Object obj, String template, Object... params) {
+ throwIfNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotNull(Object obj, String template, Object... params) {
+ throwIfNotNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果存在,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param entityName 实体名
+ * @param fieldName 字段名
+ * @param fieldValue 字段值
+ */
+ public static void throwIfExists(Object obj, String entityName, String fieldName, Object fieldValue) {
+ String message = "%s 为 [%s] 的 %s 记录已存在".formatted(fieldName, fieldValue, entityName);
+ throwIfNotNull(obj, message, EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfEmpty(Object obj, String template, Object... params) {
+ throwIfEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotEmpty(Object obj, String template, Object... params) {
+ throwIfNotEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param str 被检测的字符串
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfBlank(CharSequence str, String template, Object... params) {
+ throwIfBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param str 被检测的字符串
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotBlank(CharSequence str, String template, Object... params) {
+ throwIfNotBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果相同,抛出异常
+ *
+ * @param obj1 要比较的对象1
+ * @param obj2 要比较的对象2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfEqual(Object obj1, Object obj2, String template, Object... params) {
+ throwIfEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不相同,抛出异常
+ *
+ * @param obj1 要比较的对象1
+ * @param obj2 要比较的对象2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotEqual(Object obj1, Object obj2, String template, Object... params) {
+ throwIfNotEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果相同,抛出异常(不区分大小写)
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String template, Object... params) {
+ throwIfEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不相同,抛出异常(不区分大小写)
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotEqualIgnoreCase(CharSequence str1,
+ CharSequence str2,
+ String template,
+ Object... params) {
+ throwIfNotEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param condition 条件
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIf(boolean condition, String template, Object... params) {
+ throwIf(condition, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param conditionSupplier 条件
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIf(BooleanSupplier conditionSupplier, String template, Object... params) {
+ throwIf(conditionSupplier, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+}
diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/validation/ValidationUtils.java b/continew-starter-core/src/main/java/top/continew/starter/core/validation/ValidationUtils.java
new file mode 100644
index 00000000..22c1f0d8
--- /dev/null
+++ b/continew-starter-core/src/main/java/top/continew/starter/core/validation/ValidationUtils.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.starter.core.validation;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import top.continew.starter.core.exception.BadRequestException;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * 基本参数校验工具类(抛出 400 BadRequestException)
+ *
+ * @author Charles7c
+ * @see BadRequestException
+ * @since 1.0.0
+ */
+public class ValidationUtils extends Validator {
+
+ private static final Class EXCEPTION_TYPE = BadRequestException.class;
+
+ private ValidationUtils() {
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNull(Object obj, String template, Object... params) {
+ throwIfNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotNull(Object obj, String template, Object... params) {
+ throwIfNotNull(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfEmpty(Object obj, String template, Object... params) {
+ throwIfEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotEmpty(Object obj, String template, Object... params) {
+ throwIfNotEmpty(obj, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param str 被检测的字符串
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfBlank(CharSequence str, String template, Object... params) {
+ throwIfBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param str 被检测的字符串
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotBlank(CharSequence str, String template, Object... params) {
+ throwIfNotBlank(str, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果相同,抛出异常
+ *
+ * @param obj1 要比较的对象1
+ * @param obj2 要比较的对象2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfEqual(Object obj1, Object obj2, String template, Object... params) {
+ throwIfEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不相同,抛出异常
+ *
+ * @param obj1 要比较的对象1
+ * @param obj2 要比较的对象2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotEqual(Object obj1, Object obj2, String template, Object... params) {
+ throwIfNotEqual(obj1, obj2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果相同,抛出异常(不区分大小写)
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfEqualIgnoreCase(CharSequence str1, CharSequence str2, String template, Object... params) {
+ throwIfEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果不相同,抛出异常(不区分大小写)
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIfNotEqualIgnoreCase(CharSequence str1,
+ CharSequence str2,
+ String template,
+ Object... params) {
+ throwIfNotEqualIgnoreCase(str1, str2, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param condition 条件
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIf(boolean condition, String template, Object... params) {
+ throwIf(condition, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param conditionSupplier 条件
+ * @param template 异常信息模板,被替换的部分用 {} 表示,如果模板为 null,返回 "null"
+ * @param params 参数值
+ */
+ public static void throwIf(BooleanSupplier conditionSupplier, String template, Object... params) {
+ throwIf(conditionSupplier, CharSequenceUtil.format(template, params), EXCEPTION_TYPE);
+ }
+}
diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/validation/Validator.java b/continew-starter-core/src/main/java/top/continew/starter/core/validation/Validator.java
new file mode 100644
index 00000000..dba74a48
--- /dev/null
+++ b/continew-starter-core/src/main/java/top/continew/starter/core/validation/Validator.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.starter.core.validation;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+
+import java.util.Set;
+import java.util.function.BooleanSupplier;
+
+/**
+ * 校验器
+ *
+ * @author Charles7c
+ * @since 1.0.0
+ */
+public class Validator {
+
+ private static final jakarta.validation.Validator VALIDATOR = SpringUtil
+ .getBean(jakarta.validation.Validator.class);
+
+ protected Validator() {
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfNull(Object obj, String message, Class extends RuntimeException> exceptionType) {
+ throwIf(null == obj, message, exceptionType);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfNotNull(Object obj, String message, Class extends RuntimeException> exceptionType) {
+ throwIf(null != obj, message, exceptionType);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfEmpty(Object obj, String message, Class extends RuntimeException> exceptionType) {
+ throwIf(ObjectUtil.isEmpty(obj), message, exceptionType);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param obj 被检测的对象
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfNotEmpty(Object obj, String message, Class extends RuntimeException> exceptionType) {
+ throwIf(ObjectUtil.isNotEmpty(obj), message, exceptionType);
+ }
+
+ /**
+ * 如果为空,抛出异常
+ *
+ * @param str 被检测的字符串
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfBlank(CharSequence str,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ throwIf(CharSequenceUtil.isBlank(str), message, exceptionType);
+ }
+
+ /**
+ * 如果不为空,抛出异常
+ *
+ * @param str 被检测的字符串
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfNotBlank(CharSequence str,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ throwIf(CharSequenceUtil.isNotBlank(str), message, exceptionType);
+ }
+
+ /**
+ * 如果相同,抛出异常
+ *
+ * @param obj1 要比较的对象1
+ * @param obj2 要比较的对象2
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfEqual(Object obj1,
+ Object obj2,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ throwIf(ObjectUtil.equal(obj1, obj2), message, exceptionType);
+ }
+
+ /**
+ * 如果不相同,抛出异常
+ *
+ * @param obj1 要比较的对象1
+ * @param obj2 要比较的对象2
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfNotEqual(Object obj1,
+ Object obj2,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ throwIf(ObjectUtil.notEqual(obj1, obj2), message, exceptionType);
+ }
+
+ /**
+ * 如果相同,抛出异常(不区分大小写)
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfEqualIgnoreCase(CharSequence str1,
+ CharSequence str2,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ throwIf(CharSequenceUtil.equalsIgnoreCase(str1, str2), message, exceptionType);
+ }
+
+ /**
+ * 如果不相同,抛出异常(不区分大小写)
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIfNotEqualIgnoreCase(CharSequence str1,
+ CharSequence str2,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ throwIf(!CharSequenceUtil.equalsIgnoreCase(str1, str2), message, exceptionType);
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param condition 条件
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIf(boolean condition, String message, Class extends RuntimeException> exceptionType) {
+ if (condition) {
+ throw ReflectUtil.newInstance(exceptionType, message);
+ }
+ }
+
+ /**
+ * 如果条件成立,抛出异常
+ *
+ * @param conditionSupplier 条件
+ * @param message 错误信息
+ * @param exceptionType 异常类型
+ */
+ protected static void throwIf(BooleanSupplier conditionSupplier,
+ String message,
+ Class extends RuntimeException> exceptionType) {
+ if (null != conditionSupplier && conditionSupplier.getAsBoolean()) {
+ throw ReflectUtil.newInstance(exceptionType, message);
+ }
+ }
+
+ /**
+ * JSR 303 校验
+ *
+ * @param obj 被校验对象
+ * @param groups 分组
+ * @since 2.3.0
+ */
+ public static void validate(Object obj, Class>... groups) {
+ Set> violations = VALIDATOR.validate(obj, groups);
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(violations);
+ }
+ }
+}
diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/validation/constraints/EnumValue.java b/continew-starter-core/src/main/java/top/continew/starter/core/validation/constraints/EnumValue.java
new file mode 100644
index 00000000..0fa2ae57
--- /dev/null
+++ b/continew-starter-core/src/main/java/top/continew/starter/core/validation/constraints/EnumValue.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.starter.core.validation.constraints;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+
+/**
+ * 枚举校验注解
+ *
+ *
+ * {@code @EnumValue(value = XxxEnum.class, message = "参数值非法")}
+ * {@code @EnumValue(enumValues = {"F", "M"} ,message = "性别只允许为F或M")}
+ *
+ *
+ * @author Jasmine
+ * @author Charles7c
+ * @since 2.7.3
+ */
+@Documented
+@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = EnumValueValidator.class)
+public @interface EnumValue {
+
+ /**
+ * 枚举类
+ *
+ * @return 枚举类
+ */
+ Class extends Enum> value() default Enum.class;
+
+ /**
+ * 枚举值
+ *
+ * @return 枚举值
+ */
+ String[] enumValues() default {};
+
+ /**
+ * 获取枚举值的方法名
+ *
+ * @return 获取枚举值的方法名
+ */
+ String method() default "";
+
+ /**
+ * 提示消息
+ *
+ * @return 提示消息
+ */
+ String message() default "参数值非法";
+
+ /**
+ * 分组
+ *
+ * @return 分组
+ */
+ Class>[] groups() default {};
+
+ /**
+ * 负载
+ *
+ * @return 负载
+ */
+ Class extends Payload>[] payload() default {};
+}
diff --git a/continew-starter-core/src/main/java/top/continew/starter/core/validation/constraints/EnumValueValidator.java b/continew-starter-core/src/main/java/top/continew/starter/core/validation/constraints/EnumValueValidator.java
new file mode 100644
index 00000000..86ba9896
--- /dev/null
+++ b/continew-starter-core/src/main/java/top/continew/starter/core/validation/constraints/EnumValueValidator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package top.continew.starter.core.validation.constraints;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.function.Function;
+
+/**
+ * 枚举校验注解校验器
+ *
+ * @author Charles7c
+ * @author Jasmine
+ * @since 2.7.3
+ */
+public class EnumValueValidator implements ConstraintValidator {
+
+ private static final Logger log = LoggerFactory.getLogger(EnumValueValidator.class);
+ private Class extends Enum> enumClass;
+ private String[] enumValues;
+ private String enumMethod;
+
+ @Override
+ public void initialize(EnumValue enumValue) {
+ this.enumClass = enumValue.value();
+ this.enumValues = enumValue.enumValues();
+ this.enumMethod = enumValue.method();
+ }
+
+ @Override
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ }
+ // 优先校验 enumValues
+ if (enumValues.length > 0) {
+ return Arrays.asList(enumValues).contains(value.toString());
+ }
+ Enum[] enumConstants = enumClass.getEnumConstants();
+ if (enumConstants.length == 0) {
+ return false;
+ }
+ if (CharSequenceUtil.isBlank(enumMethod)) {
+ return findEnumValue(enumConstants, Enum::toString, value.toString());
+ }
+ try {
+ // 枚举类指定了方法名,则调用指定方法获取枚举值
+ Method method = enumClass.getMethod(enumMethod);
+ for (Enum enumConstant : enumConstants) {
+ if (method.invoke(enumConstant).equals(value)) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ log.error("An error occurred while validating the enum value, please check the @EnumValue parameter configuration.", e);
+ }
+ return false;
+ }
+
+ /**
+ * 遍历枚举类,判断是否包含指定值
+ *
+ * @param enumConstants 枚举类数组
+ * @param function 获取枚举值的函数
+ * @param value 待校验的值
+ * @return 是否包含指定值
+ */
+ private boolean findEnumValue(Enum[] enumConstants, Function function, Object value) {
+ for (Enum enumConstant : enumConstants) {
+ if (function.apply(enumConstant).equals(value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java b/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java
index 9232b2b2..8247171e 100644
--- a/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java
+++ b/continew-starter-data/continew-starter-data-mf/src/main/java/top/continew/starter/data/mf/util/QueryWrapperHelper.java
@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.core.util.ReflectUtils;
-import top.continew.starter.core.util.validate.ValidationUtils;
+import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.core.annotation.Query;
import top.continew.starter.data.core.annotation.QueryIgnore;
import top.continew.starter.data.core.enums.QueryType;
diff --git a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/service/impl/ServiceImpl.java b/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/service/impl/ServiceImpl.java
index 8b93dfc9..5f30b138 100644
--- a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/service/impl/ServiceImpl.java
+++ b/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/service/impl/ServiceImpl.java
@@ -18,7 +18,7 @@ package top.continew.starter.data.mp.service.impl;
import cn.hutool.core.util.ClassUtil;
import top.continew.starter.core.util.ReflectUtils;
-import top.continew.starter.core.util.validate.CheckUtils;
+import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.data.mp.base.BaseMapper;
import top.continew.starter.data.mp.service.IService;
diff --git a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/util/QueryWrapperHelper.java b/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/util/QueryWrapperHelper.java
index 81734f96..75c6986a 100644
--- a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/util/QueryWrapperHelper.java
+++ b/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/mp/util/QueryWrapperHelper.java
@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.core.util.ReflectUtils;
-import top.continew.starter.core.util.validate.ValidationUtils;
+import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.core.annotation.Query;
import top.continew.starter.data.core.annotation.QueryIgnore;
import top.continew.starter.data.core.enums.QueryType;