package com.dl.framework.handler; import cn.hutool.core.io.IoUtil; import io.swagger.v3.core.jackson.TypeNameResolver; import io.swagger.v3.core.util.AnnotationsUtils; import io.swagger.v3.oas.annotations.tags.Tags; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.tags.Tag; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springdoc.core.OpenAPIService; import org.springdoc.core.PropertyResolverUtils; import org.springdoc.core.SecurityService; import org.springdoc.core.SpringDocConfigProperties; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.providers.JavadocProvider; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.CollectionUtils; import org.springframework.web.method.HandlerMethod; import java.io.StringReader; import java.lang.reflect.Method; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 自定义 openapi 处理器 * 对源码功能进行修改 增强使用 */ @SuppressWarnings("all") public class OpenApiHandler extends OpenAPIService { /** * The constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIService.class); /** * The Context. */ private ApplicationContext context; /** * The Security parser. */ private final SecurityService securityParser; /** * The Mappings map. */ private final Map mappingsMap = new HashMap<>(); /** * The Springdoc tags. */ private final Map springdocTags = new HashMap<>(); /** * The Open api builder customisers. */ private final Optional> openApiBuilderCustomisers; /** * The server base URL customisers. */ private final Optional> serverBaseUrlCustomizers; /** * The Spring doc config properties. */ private final SpringDocConfigProperties springDocConfigProperties; /** * The Open api. */ private OpenAPI openAPI; /** * The Cached open api map. */ private final Map cachedOpenAPI = new HashMap<>(); /** * The Is servers present. */ private boolean isServersPresent; /** * The Server base url. */ private String serverBaseUrl; /** * The Property resolver utils. */ private final PropertyResolverUtils propertyResolverUtils; /** * The javadoc provider. */ private final Optional javadocProvider; /** * The Basic error controller. */ private static Class basicErrorController; static { try { //spring-boot 2 basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController"); } catch (ClassNotFoundException e) { //spring-boot 1 try { basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.BasicErrorController"); } catch (ClassNotFoundException classNotFoundException) { //Basic error controller class not found LOGGER.trace(classNotFoundException.getMessage()); } } } /** * Instantiates a new Open api builder. * * @param openAPI the open api * @param securityParser the security parser * @param springDocConfigProperties the spring doc config properties * @param propertyResolverUtils the property resolver utils * @param openApiBuilderCustomizers the open api builder customisers * @param serverBaseUrlCustomizers the server base url customizers * @param javadocProvider the javadoc provider */ public OpenApiHandler(Optional openAPI, SecurityService securityParser, SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, Optional> openApiBuilderCustomizers, Optional> serverBaseUrlCustomizers, Optional javadocProvider) { super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); if (openAPI.isPresent()) { this.openAPI = openAPI.get(); if (this.openAPI.getComponents() == null) this.openAPI.setComponents(new Components()); if (this.openAPI.getPaths() == null) this.openAPI.setPaths(new Paths()); if (!CollectionUtils.isEmpty(this.openAPI.getServers())) this.isServersPresent = true; } this.propertyResolverUtils = propertyResolverUtils; this.securityParser = securityParser; this.springDocConfigProperties = springDocConfigProperties; this.openApiBuilderCustomisers = openApiBuilderCustomizers; this.serverBaseUrlCustomizers = serverBaseUrlCustomizers; this.javadocProvider = javadocProvider; if (springDocConfigProperties.isUseFqn()) TypeNameResolver.std.setUseFqn(true); } @Override public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { Set tags = new HashSet<>(); Set tagsStr = new HashSet<>(); buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); if (!CollectionUtils.isEmpty(tagsStr)) tagsStr = tagsStr.stream() .map(str -> propertyResolverUtils.resolve(str, locale)) .collect(Collectors.toSet()); if (springdocTags.containsKey(handlerMethod)) { io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod); tagsStr.add(tag.getName()); if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { openAPI.addTagsItem(tag); } } if (!CollectionUtils.isEmpty(tagsStr)) { if (CollectionUtils.isEmpty(operation.getTags())) operation.setTags(new ArrayList<>(tagsStr)); else { Set operationTagsSet = new HashSet<>(operation.getTags()); operationTagsSet.addAll(tagsStr); operation.getTags().clear(); operation.getTags().addAll(operationTagsSet); } } if (isAutoTagClasses(operation)) { if (javadocProvider.isPresent()) { String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType()); if (StringUtils.isNotBlank(description)) { io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); // 自定义部分 修改使用java注释当tag名 List list = IoUtil.readLines(new StringReader(description), new ArrayList<>()); // tag.setName(tagAutoName); tag.setName(list.get(0)); operation.addTagsItem(list.get(0)); tag.setDescription(description); if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { openAPI.addTagsItem(tag); } } } else { String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName()); operation.addTagsItem(tagAutoName); } } if (!CollectionUtils.isEmpty(tags)) { // Existing tags List openApiTags = openAPI.getTags(); if (!CollectionUtils.isEmpty(openApiTags)) tags.addAll(openApiTags); openAPI.setTags(new ArrayList<>(tags)); } // Handle SecurityRequirement at operation level io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser .getSecurityRequirements(handlerMethod); if (securityRequirements != null) { if (securityRequirements.length == 0) operation.setSecurity(Collections.emptyList()); else securityParser.buildSecurityRequirement(securityRequirements, operation); } return operation; } private void buildTagsFromMethod(Method method, Set tags, Set tagsStr, Locale locale) { // method tags Set tagsSet = AnnotatedElementUtils .findAllMergedAnnotations(method, Tags.class); Set methodTags = tagsSet.stream() .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); if (!CollectionUtils.isEmpty(methodTags)) { tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet())); List allTags = new ArrayList<>(methodTags); addTags(allTags, tags, locale); } } private void addTags(List sourceTags, Set tags, Locale locale) { Optional> optionalTagSet = AnnotationsUtils .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); optionalTagSet.ifPresent(tagsSet -> { tagsSet.forEach(tag -> { tag.name(propertyResolverUtils.resolve(tag.getName(), locale)); tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale)); if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) tags.add(tag); }); }); } }