ruoyi 若依 自定义注解 参数校验

可以使用"枚举统一接口 + 自定义注解 + 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" 匹配 String
  • 0 反序列化到 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。这样新增、修改等不同场景也更容易通过校验分组控制。

相关推荐
赤龙ERP10 小时前
ERP周报:连接器开闸、国产GPU进场与智能工厂253家
数据库·人工智能·大模型·rpa·erp·企业数字化·builderpattern
Wang's Blog10 小时前
Java 项目实战:交友项目 - 联系人列表
java·开发语言·交友
lusklusklusk10 小时前
SQL Server补丁版本号为什么“日期新反而版本低”?——GDR、CU、CU+GDR 版本号规则与升级路径详解
数据库
我命由我1234510 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
key_3_feng10 小时前
评估与对齐:Agent 自我进化的护栏
前端·数据库·agent
TangGeeA10 小时前
数据库、SQL 与 DB AI
数据库·人工智能·sql
IvorySQL10 小时前
开源数据库进校园| 11 月 3 日,IvorySQL 校园行走进浙大宁波校区:从 PostgreSQL 扩展生态到 AI 实战
数据库·postgresql·开源
福建佰胜张工11 小时前
深度解析 Allen-Bradley 150-B135NBDA 智能电机控制器:选型、参数与工业应用
运维·数据库·单片机·嵌入式硬件·安全·自动化
`Levie11 小时前
MySQL-数据库服务日志管理与数据闪回
数据库·mysql
香蕉大香蕉(06敬畏市场)11 小时前
MySQL 单点详解(01)
java·数据库·mysql·面试·博客·实习