006-自定义枚举注解

自定义枚举注解

一、业务需求描述

1.问题描述

在字段使用 @ApiModelProperty 描述枚举字段含义,后续业务拓展,如果有新增加枚举值,每一个用到该枚举的字段的地方都要维护一遍,极其不方便,增加了维护成本。

2.解决方案

增加一个自定义枚举注解 @ApiPropertyEnum,来代替 @ApiModelProperty 。这样以后只需要维护对应的枚举类即可。

二、创建一个描述注解

主要功能:为了统一字段描述,用于映射枚举对应的字段,因为定义枚举字段因人而异

java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SwaggerDisplayEnum {

    String code() default "code";
    String desc() default "desc";

}

三、创建一个枚举注解

java 复制代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiPropertyEnum {
    // 字段名称
    String name() default "";
    // 字段简要描述
    String value() default "";
    boolean hidden() default false;
    // 标识字段是否必填
    boolean required() default  false;
    // 指定取值对应的枚举类
    Class<? extends Enum> enumClazz();
}

四、创建一个枚举

java 复制代码
import com.xiaogang.annotation.SwaggerDisplayEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @Description: 模板业务类型枚举
 * @Author: hzg
 * @Date: 2024/9/5 13:52
 */
@Getter
@AllArgsConstructor
@SwaggerDisplayEnum(code = "key", desc = "value")
public enum TemplateBusinessTypeEnum {

    ACQUISITION_TEMPLATE("1","收购订单模板"),
    SELL_TEMPLATE("2","销售订单模板"),
    LEASE_TEMPLATE("3","租赁单模板"),
    LEASE_ATR_TEMPLATE("4","租赁归还模板"),
    ALLOCATE_TEMPLATE("5","车辆调拨模板"),
    ;

    String key;
    String value;

}

五、创建一个配置文件

java 复制代码
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.ApiPropertyEnum;
import com.ruoyi.common.annotation.SwaggerDisplayEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import springfox.documentation.builders.PropertySpecificationBuilder;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.schema.Annotations;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;

import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description: swagger 关于注解处理
 * @Auther: hzg
 * @Date: 2024/11/21 14:40
 * @version: 1.0
 */
@Slf4j
@Configuration
public class SwaggerModelPropertyParameterConfig implements ModelPropertyBuilderPlugin, ParameterBuilderPlugin, ExpandedParameterBuilderPlugin {

    /**
     * 是否开启swagger
     */
    @Value("${swagger.enabled}")
    private boolean enabled;

    @Override
    public void apply(ModelPropertyContext context) {
        if (!enabled) {
            return;
        }
        // 为枚举字段设置注释
        descForModelEnumFields(context);
    }

    @Override
    public void apply(ParameterContext context) {
        if (!enabled) {
            return;
        }
        // 为枚举字段设置注释
        descForMethodParameterEnumFields(context);
    }

    @Override
    public void apply(ParameterExpansionContext context) {
        if (!enabled) {
            return;
        }
        // 为枚举字段设置注释
        descForMethodExpandedParameterEnumFields(context);
    }

    /**
     * 返回是否应根据给定的分隔符调用插件
     */
    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }

    /**
     * @Description: 针对 Model 里面字段枚举
     * @Author: hzg
     * @Date: 2024-11-21 16:57:23
     * @Params: [modelPropertyContext]
     * @Return: void
     */
    private void descForModelEnumFields(ModelPropertyContext modelPropertyContext) {
        if (!modelPropertyContext.getBeanPropertyDefinition().isPresent()) {
            return;
        }

        Optional<ApiPropertyEnum> annotation = Optional.empty();

        // 找到 @ApiModelProperty 注解修饰的枚举类
        if (modelPropertyContext.getBeanPropertyDefinition().isPresent()) {
            annotation = Annotations.findPropertyAnnotation(modelPropertyContext.getBeanPropertyDefinition().get(), ApiPropertyEnum.class);
        }

        // 没有@ApiModelProperty 或者 notes 属性没有值,直接返回
        if (!annotation.isPresent()) {
            return;
        }

        // 生成需要拼接的取值含义描述内容
        String valueDesc = generateValueDesc(annotation.get());
        PropertySpecificationBuilder propertySpecificationBuilder = modelPropertyContext.getSpecificationBuilder();
        propertySpecificationBuilder.description(valueDesc);
        propertySpecificationBuilder.required(annotation.get().required());
        propertySpecificationBuilder.isHidden(annotation.get().hidden());
    }

    /**
     * @Description: 针对 非Model controller 方法请求参数【单个字段对象】枚举
     * @Author: hzg
     * @Date: 2024-11-21 16:58:35
     * @Params: [context]
     * @Return: void
     */
    private void descForMethodParameterEnumFields(ParameterContext context) {
        Optional<ApiPropertyEnum> apiPropertyEnumOptional = context.resolvedMethodParameter().findAnnotation(ApiPropertyEnum.class);
        if (!apiPropertyEnumOptional.isPresent()) {
            return;
        }
        ApiPropertyEnum reference = apiPropertyEnumOptional.get();
        String desc = generateValueDesc(reference);
        if (StrUtil.isNotEmpty(reference.name())) {
            context.requestParameterBuilder().name(reference.name());
        }
        RequestParameterBuilder requestParameterBuilder = context.requestParameterBuilder();
        requestParameterBuilder.description(desc);
        requestParameterBuilder.required(reference.required());
    }

    /**
     * @Description: 针对 非Model controller 方法请求参数【实体对象】枚举
     * @Author: hzg
     * @Date: 2024-11-22 13:56:39
     * @Params: [context]
     * @Return: void
     */
    private void descForMethodExpandedParameterEnumFields(ParameterExpansionContext context) {
        Optional<ApiPropertyEnum> apiPropertyEnumOptional = context.findAnnotation(ApiPropertyEnum.class);
        if (!apiPropertyEnumOptional.isPresent()) {
            return;
        }
        ApiPropertyEnum reference = apiPropertyEnumOptional.get();
        String desc = generateValueDesc(reference);
        RequestParameterBuilder requestParameterBuilder = context.getRequestParameterBuilder();
        requestParameterBuilder.description(desc);
        requestParameterBuilder.required(reference.required());
        context.getParameterBuilder().hidden(reference.hidden());
    }

    private String generateValueDesc(ApiPropertyEnum propertyReference) {
        Class<? extends Enum> rawPrimaryType = propertyReference.enumClazz();
        SwaggerDisplayEnum swaggerDisplayEnum = AnnotationUtils.findAnnotation(rawPrimaryType,
                SwaggerDisplayEnum.class);
        String enumFullDesc = Arrays.stream(rawPrimaryType.getEnumConstants())
                .filter(Objects::nonNull)
                .map(enumConsts -> {
                    assert swaggerDisplayEnum != null;
                    Object fieldValue = ReflectUtil.getFieldValue(enumConsts, swaggerDisplayEnum.code());
                    Object fieldDesc = ReflectUtil.getFieldValue(enumConsts, swaggerDisplayEnum.desc());
                    return enumConsts + "(" + fieldValue + "," + fieldDesc + ")";
                }).collect(Collectors.joining(";"));
        return propertyReference.value() + "【" + enumFullDesc + "】";
    }

}

六、场景实战

1.在 @RequestParam 前面使用
java 复制代码
@ApiOperation(value = "查询8实体信息",tags = {"查询API"})
@GetMapping("/test1")
public TestDateTimeFormat test1(
        @ApiParam(value = "传ID", required = false, name = "id") String id,
        @ApiParam(value = "店铺状态") @RequestParam(name = "customerParticipantType", required = true) String customerParticipantType,
        @ApiPropertyEnum(value = "参数一",name = "param1",enumClazz = TemplateBusinessTypeEnum.class,required = true) @RequestParam(name = "param1", required = true) String param1){
    TestDateTimeFormat testDateTimeFormat = new TestDateTimeFormat();
    testDateTimeFormat.setName("xiaogang");
    testDateTimeFormat.setPhoneNumber("111111111111");
    testDateTimeFormat.setAddress("beijing");
    testDateTimeFormat.setBirthday(new Date());
    return testDateTimeFormat;
}
2.在非 Model 的实体类上使用
java 复制代码
@ApiOperation(value = "查询7实体信息", tags = {"查询API"})
@GetMapping("/test1")
public TestJsonFormat test1(TestClass testDateTimeFormat) {
    TestJsonFormat testJsonFiled = new TestJsonFormat();
    testJsonFiled.setName("xiaogang");
    testJsonFiled.setPhoneNumber("111111111111");
    testJsonFiled.setAddress("beijing");
    testJsonFiled.setBirthday(new Date());
    return testJsonFiled;
}
java 复制代码
@Data
@AllArgsConstructor
public class TestClass {

    @ApiPropertyEnum(value = "测试姓名", enumClazz = TemplateBusinessTypeEnum.class,required = true,hidden = false)
    private String name;

    private int age;

    private String address;

}
3.在 @RequestBody 对应的实体类中使用

当然也可以直接在Response返回体实体对象中直接使用

java 复制代码
@ApiOperation(value = "新增",tags = {"查询API"})
@PostMapping("/test2")
public void test2(@RequestBody TestDateTimeFormat testDateTimeFormat){
    log.info("实体类:{}",testDateTimeFormat);
    // 实体类:TestJsonFormat(name=小明, address=北京市, phoneNumber=123456789, birthday=Thu Oct 10 14:42:20 CST 2024)
}
java 复制代码
@Data
@ApiModel(description = "日期实体")
public class TestDateTimeFormat {

    @ApiModelProperty(value = "姓名")
    private String name;

    @ApiModelProperty(value = "地址")
    private String address;

    @ApiPropertyEnum(value = "电话号码", enumClazz = TemplateBusinessTypeEnum.class,required = true)
    private String phoneNumber;

    @ApiModelProperty(value = "生日")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date birthday;

}

七、效果展示


相关推荐
数据小爬虫@2 小时前
深入解析:使用 Python 爬虫获取苏宁商品详情
开发语言·爬虫·python
健胃消食片片片片2 小时前
Python爬虫技术:高效数据收集与深度挖掘
开发语言·爬虫·python
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
空の鱼3 小时前
java开发,IDEA转战VSCODE配置(mac)
java·vscode
一只小bit4 小时前
C++之初识模版
开发语言·c++
P7进阶路4 小时前
Tomcat异常日志中文乱码怎么解决
java·tomcat·firefox
王磊鑫4 小时前
C语言小项目——通讯录
c语言·开发语言
钢铁男儿4 小时前
C# 委托和事件(事件)
开发语言·c#
Ai 编码助手5 小时前
在 Go 语言中如何高效地处理集合
开发语言·后端·golang
小丁爱养花5 小时前
Spring MVC:HTTP 请求的参数传递2.0
java·后端·spring