可以使用"枚举统一接口 + 自定义注解 + ConstraintValidator"实现。你的项目是 Spring Boot 3,校验包应使用 jakarta.validation。
1. 定义枚举统一接口
package com.ruoyi.common.enums;
/**
* 具有业务编码的枚举。
*
* @param <T> 编码类型
*/
public interface CodeEnum<T>
{
T getCode();
}
2. 让业务枚举实现接口
例如内容类型枚举:
package com.ruoyi.GMLG.enums;
import com.ruoyi.common.enums.CodeEnum;
public enum ContentTypeEnum implements CodeEnum<String>
{
VIDEO("video", "视频"),
AUDIO("audio", "音频"),
ARTICLE("article", "图文");
private final String code;
private final String label;
ContentTypeEnum(String code, String label)
{
this.code = code;
this.label = label;
}
@Override
public String getCode()
{
return code;
}
public String getLabel()
{
return label;
}
}
Long 类型也可以直接实现:
public enum ContentStatusEnum implements CodeEnum<Long>
{
PENDING_SUBMIT(0L, "待提交"),
APPROVING(1L, "审批中"),
APPROVED(2L, "已通过"),
REJECTED(3L, "已驳回");
private final Long code;
private final String label;
ContentStatusEnum(Long code, String label)
{
this.code = code;
this.label = label;
}
@Override
public Long getCode()
{
return code;
}
public String getLabel()
{
return label;
}
}
你现有枚举已经有 getCode(),只需要添加 implements CodeEnum<String> 或 implements CodeEnum<Long>。
3. 创建自定义校验注解
package com.ruoyi.common.annotation;
import com.ruoyi.common.validation.EnumValueValidator;
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;
@Documented
@Target({
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValueValidator.class)
public @interface EnumValue
{
/**
* 需要校验的枚举类。
*/
Class<? extends Enum<?>> enumClass();
String message() default "参数值不在允许的枚举范围内";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
4. 实现枚举校验器
package com.ruoyi.common.validation;
import com.ruoyi.common.annotation.EnumValue;
import com.ruoyi.common.enums.CodeEnum;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
public class EnumValueValidator
implements ConstraintValidator<EnumValue, Object>
{
private Set<Object> allowedValues;
@Override
public void initialize(EnumValue annotation)
{
Enum<?>[] enumConstants = annotation.enumClass().getEnumConstants();
if (enumConstants == null)
{
throw new IllegalArgumentException(
annotation.enumClass().getName() + " 不是枚举类"
);
}
allowedValues = Arrays.stream(enumConstants)
.map(item -> {
if (!(item instanceof CodeEnum<?> codeEnum))
{
throw new IllegalArgumentException(
annotation.enumClass().getName()
+ " 必须实现 CodeEnum 接口"
);
}
return codeEnum.getCode();
})
.collect(Collectors.toUnmodifiableSet());
}
@Override
public boolean isValid(
Object value,
ConstraintValidatorContext context)
{
// null 是否允许由 @NotNull 单独负责
if (value == null)
{
return true;
}
return allowedValues.contains(value);
}
}
这里直接比较实际类型:
"video"匹配String0反序列化到Long字段后匹配Long- 不会把字符串
"0"错当成长整型0L
5. 在实体字段上使用
import com.ruoyi.GMLG.enums.ClassifyStatusEnum;
import com.ruoyi.GMLG.enums.ContentStatusEnum;
import com.ruoyi.GMLG.enums.ContentTypeEnum;
import com.ruoyi.GMLG.enums.PromoteStatusEnum;
import com.ruoyi.GMLG.enums.PublishScopeEnum;
import com.ruoyi.common.annotation.EnumValue;
import jakarta.validation.constraints.NotNull;
public class GmLgContent
{
@NotNull(message = "内容类型不能为空")
@EnumValue(
enumClass = ContentTypeEnum.class,
message = "内容类型只能是 video、audio 或 article"
)
private String contentType;
@EnumValue(
enumClass = PublishScopeEnum.class,
message = "发布范围只能是 all、city 或 third"
)
private String publishScope;
@EnumValue(
enumClass = ContentStatusEnum.class,
message = "内容状态只能是 0、1、2 或 3"
)
private Long status;
@EnumValue(
enumClass = PromoteStatusEnum.class,
message = "推广状态只能是 0、1 或 2"
)
private Long promoteStatus;
@EnumValue(
enumClass = ClassifyStatusEnum.class,
message = "归类状态只能是 0 或 1"
)
private Long classifyStatus;
}
6. Controller 必须触发参数校验
你当前的 GMLG Controller 没有使用 @Validated,因此只给字段添加注解还不会生效。
新增接口应改成:
import org.springframework.validation.annotation.Validated;
@PostMapping
public AjaxResult add(
@Validated @RequestBody GmLgContent gmLgContent)
{
return toAjax(
gmLgContentService.insertGmLgContent(gmLgContent)
);
}
修改接口同样添加:
@PutMapping
public AjaxResult edit(
@Validated @RequestBody GmLgContent gmLgContent)
{
return toAjax(
gmLgContentService.updateGmLgContent(gmLgContent)
);
}
若提交:
{
"contentType": "document",
"status": 9
}
若依现有的 GlobalExceptionHandler 会处理 MethodArgumentNotValidException,返回类似:
{
"code": 500,
"msg": "内容类型只能是 video、audio 或 article"
}
注意:@EnumValue 默认允许 null,需要必填的字段再叠加 @NotNull 或 @NotBlank。这样新增、修改等不同场景也更容易通过校验分组控制。