拓展swagger插件,简化字典类属性注释

9 篇文章 0 订阅
1 篇文章 0 订阅

为了生成接口文档我们很多人会使用到swagger组件,在我们使用swagger注解进行属性配置的时候,经常会有如下一部分代码;如果这些属性取自字典或枚举的话就会变得复杂臃肿且不易维护:

@Data
@ApiModel("Xxx desc")
public class Xxx {
    @ApiModelProperty(value = "课程状态;取自字典 course_state")
    private Integer state;

    @ApiModelProperty(value = "排序字段:startTime 课程开始时间、endTime 课程结束时间、orderIncomeNum 成单数、orderRefundNum 退单数、orderIncomeAmount 成单金额、orderRefundAmount 退单金额、transferNum 转化人数、refundNum 退款人数")
    private String orderBy;

    @ApiModelProperty(value = "排序方向:asc 升序、desc 降序")
    private String direct;
    ...
}

我们之前已经建设了完善的字典组件支持,结合拓展swagger组件,自动适配枚举或字典类型编码,可以实现自动生成接口文档,这样只要修改了枚举定义或字典配置,文档会自动更新。效果如下:

 定义一个字典属性枚举:

package com.xxx.xxx.api.enums;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author zhaochao
 * @date 2021/6/11 14:19
 * @desc 字典枚举属性,用于生成接口文档
 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictionaryProperty {

    /**
     * 属性名
     */
    String value();

    /**
     * 是否必须存在
     */
    boolean required() default false;

    /**
     * 排序序号
     */
    int position() default 0;

    /**
     * 是否隐藏
     */
    boolean hidden() default false;

    /**
     * 字典枚举类
     */
    Class<? extends DictionaryEnum> enumClass() default DictionaryEnum.class;

    /**
     * 字典类型编码
     */
    String dictTypeCode() default "";
}

其中dictTypeCode是字典类型编码,属于某一类字典的唯一标识编码,enumClass是字典枚举类接口DictionaryEnum的实现;DictionaryEnum定义如下:

package com.xxx.xxx.api.enums;

/**
 * @author zhaochao
 * @date 2021/3/31 17:53
 * @desc 字典枚举接口 实现以支持生成字典
 */
public interface DictionaryEnum {
    /**
     * 获取字典编码
     *
     * @return 返回object 使用的时候转换为string
     */
    Object getCode();

    /**
     * 获取编码对应描述
     * @return String
     */
    String getValue();

    /**
     * 序号
     * @return int
     */
    int getOrder();
}

编写插件适配:

package com.xxx.xxx.plugin;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.nacos.common.utils.Objects;
import com.xxx.xxx.api.enums.DictionaryProperty;
import com.xxx.xxx.api.vo.DictionaryDataResVo;
import com.xxx.xxx.api.vo.DictionaryTypeResVo;
import com.xxx.xxx.api.enums.DictionaryEnum;
import com.xxx.xxx.service.DictionaryQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Annotations;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;

/**
 * @author 赵超
 * @date 2021-6-11
 * @desc 针对字典组件适配swagger的插件
 */
@Slf4j
@ConditionalOnProperty(prefix = "swagger", value = "enabled", havingValue = "true")
@Component
public class DictionaryPropertyPlugin implements ModelPropertyBuilderPlugin {

    @Autowired
    private DictionaryQueryService dictionaryQueryService;

    @Override
    public void apply(ModelPropertyContext context) {
        Optional<DictionaryProperty> annotation = Optional.empty();
        if (context.getAnnotatedElement().isPresent()) {
            annotation = findDictionaryPropertyAnnotation(context.getAnnotatedElement().get());
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = Annotations.findPropertyAnnotation(context.getBeanPropertyDefinition().get(), DictionaryProperty.class);
        }
        if (!annotation.isPresent()) {
            return;
        }
        DictionaryProperty d = annotation.get();
        context.getSpecificationBuilder()
                .isHidden(d.hidden())
                .required(d.required())
                .position(d.position())
                .description(getDesc(d));
    }

    private String getDesc(DictionaryProperty d) {
        String dictTypeCode = d.dictTypeCode();
        if (StringUtils.isNotBlank(dictTypeCode)) {
            return d.value() + getDescByDictTypeCode(dictTypeCode);
        }
        return d.value() + getDescByDictionaryEnum(d.enumClass());
    }

    private String getDescByDictionaryEnum(Class<? extends DictionaryEnum> clazz) {
        if (Objects.isNull(clazz) || Objects.equals(DictionaryEnum.class, clazz)) {
            return "";
        }
        DictionaryEnum[] enums = clazz.getEnumConstants();
        if (enums == null || enums.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder(":");
        DictionaryEnum e;
        for (int i = 0; i < enums.length; i++) {
            e = enums[i];
            sb.append(e.getCode()).append(" ").append(e.getValue());
            if (i != enums.length - 1) {
                sb.append("、");
            }
        }
        return sb.toString();
    }

    private String getDescByDictTypeCode(String dictTypeCode) {
        try {
            StringBuilder sb = new StringBuilder("(字典类型编码:" + dictTypeCode + ")");
            DictionaryTypeResVo dictTypeRes = dictionaryQueryService.getByTypeCode(dictTypeCode);
            if (Objects.isNull(dictTypeRes) || CollectionUtil.isEmpty(dictTypeRes.getData())) {
                return sb.toString();
            }
            sb.append(":");
            DictionaryDataResVo e;
            List<DictionaryDataResVo> data = dictTypeRes.getData();
            for (int i = 0; i < data.size(); i++) {
                e = data.get(i);
                sb.append(e.getCode()).append(" ").append(e.getValue());
                if (i != data.size() - 1) {
                    sb.append("、");
                }
            }
            return sb.toString();
        } catch (Exception e) {
            log.error("运行字典组件swagger插件出错");
        }
        return "";
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }

    private Optional<DictionaryProperty> findDictionaryPropertyAnnotation(AnnotatedElement annotated) {
        Optional<DictionaryProperty> annotation = Optional.empty();

        if (annotated instanceof Method) {
            // If the annotated element is a method we can use this information to check superclasses as well
            annotation = Optional.ofNullable(AnnotationUtils.findAnnotation(((Method) annotated), DictionaryProperty.class));
        }

        return annotation.map(Optional::of).orElse(Optional.ofNullable(AnnotationUtils.getAnnotation(annotated,
                DictionaryProperty.class)));
    }
}

修改原接口出入参对象的注解:

@Data
@ApiModel("Xxx desc")
public class Xxx {
    @DictionaryProperty(value = "课程状态", dictTypeCode = "course_state")
    private Integer state;

    @DictionaryProperty(value = "排序字段", enumClass = OrderColumn.class)
    private String orderBy;

    @DictionaryProperty(value = "排序方向", enumClass = SortDirectionEnum.class)
    private String direct;
    ...

    @AllArgsConstructor
    @Getter
    public enum OrderColumn implements DictionaryEnum {
        // 课程开始时间
        START_TIME("startTime", "课程开始时间"),
        // 课程结束时间
        END_TIME("endTime", "课程结束时间"),
        // 成单数
        ORDER_INCOME_NUM("orderIncomeNum", "成单数"),
        // 退单数
        ORDER_REFUND_NUM("orderRefundNum", "退单数"),
        // 成单金额
        ORDER_INCOME_AMOUNT("orderIncomeAmount", "成单金额"),
        // 退单金额
        ORDER_REFUND_AMOUNT("orderRefundAmount", "退单金额"),
        // 转化人数
        TRANSFER_NUM("transferNum", "转化人数"),
        // 转化人数
        REFUND_NUM("refundNum", "退款人数"),
        ;

        private final String column;
        private final String desc;

        public static OrderColumn parse(String orderBy) {
            if (StringUtils.isBlank(orderBy)) {
                return null;
            }
            for (OrderColumn value : values()) {
                if (Objects.equals(orderBy, value.getColumn())) {
                    return value;
                }
            }
            return null;
        }

        @Override
        public Object getCode() {
            return column;
        }

        @Override
        public String getValue() {
            return desc;
        }

        @Override
        public int getOrder() {
            return ordinal();
        }
    }
}

然后启动项目,打开swagger即可查看效果!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值