自定义枚举注解
- 一、业务需求描述
- 二、创建一个描述注解
- 三、创建一个枚举注解
- 四、创建一个枚举
- 五、创建一个配置文件
- 六、场景实战
-
-
-
-
-
- [1.在 @RequestParam 前面使用](#1.在 @RequestParam 前面使用)
- [2.在非 Model 的实体类上使用](#2.在非 Model 的实体类上使用)
- [3.在 @RequestBody 对应的实体类中使用](#3.在 @RequestBody 对应的实体类中使用)
-
-
-
-
- 七、效果展示
一、业务需求描述
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;
}
七、效果展示