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。这样新增、修改等不同场景也更容易通过校验分组控制。

相关推荐
Wang's Blog1 小时前
Java 项目部署之 Docker工具快速入门: Docker 是什么以及它如何解决部署环境问题
java·开发语言·docker
老木避暑研究所1 小时前
从原始数据到洞察:一次完整的避暑房环境数据采集、清洗与可视化分析
java·开发语言
禾小西2 小时前
06丨Redis 数据同步:主从库如何实现数据一致?
数据库·redis·php
前端 贾公子2 小时前
LangGraph == 图的状态(State)管理 (上)
java·开发语言·数据库
huaweichenai2 小时前
spring boot 实现file文件上传
java·spring boot·后端
薛晓刚2 小时前
PGA 超限的一次应急处置:扩容、清游标、杀会话
数据库
拆房老料2 小时前
ONLYOFFICE也能像Microsoft Word和WPS一样分别设置中西文字体
前端·html·word·开源软件·wps
CJi0NG2 小时前
【自用】MySQL-事务
数据库·mysql
VX_bysjlw9852 小时前
数码设备销售网站设计与实现39138-计算机毕设原创(免费领源码+带部署教程)
java·vue.js·spring boot·mysql·tomcat·mybatis·idea