add 新增 ruoyi-common-doc 整合 springdoc 基于 javadoc 实现无注解零入侵生成接口文档

2.X
疯狂的狮子li 3 years ago
parent 9256e938c6
commit bbec3b5d8d

@ -63,10 +63,6 @@ dubbo:
log-level: info
spring:
mvc:
pathmatch:
# 适配 boot 2.6 路由与 springfox 兼容
matching-strategy: ANT_PATH_MATCHER
# 资源信息
messages:
# 国际化资源文件路径
@ -242,17 +238,25 @@ mybatis-plus:
swagger:
# 是否开启swagger
enabled: true
# 标题
title: '标题RuoYi-Cloud-Plus微服务权限管理系统_接口文档'
# 描述
description: '描述:微服务权限管理系统, 具体包括XXX,XXX模块...'
# 版本
version: '版本号:系统版本...'
# 作者信息
contact:
name: Lion Li
email: crazylionli@163.com
url: https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus
info:
# 标题
title: '标题RuoYi-Cloud-Plus微服务权限管理系统_接口文档'
# 描述
description: '描述:微服务权限管理系统, 具体包括XXX,XXX模块...'
# 版本
version: '版本号:系统版本...'
# 作者信息
contact:
name: Lion Li
email: crazylionli@163.com
url: https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: ${sa-token.token-name}
# seata配置
seata:

@ -28,6 +28,8 @@
<dynamic-ds.version>3.5.1</dynamic-ds.version>
<velocity.version>2.3</velocity.version>
<fastjson.version>1.2.83</fastjson.version>
<swagger.core.version>2.2.0</swagger.core.version>
<springdoc.version>1.6.9</springdoc.version>
<poi.version>5.2.2</poi.version>
<easyexcel.version>3.1.1</easyexcel.version>
<hutool.version>5.8.4</hutool.version>
@ -35,6 +37,7 @@
<lock4j.version>2.2.2</lock4j.version>
<xxl-job.version>2.3.1</xxl-job.version>
<satoken.version>1.30.0</satoken.version>
<lombok.version>1.18.24</lombok.version>
<logstash.version>7.1.1</logstash.version>
<!-- 统一 guava 版本 解决隐式漏洞问题 -->
@ -178,6 +181,30 @@
<version>${p6spy.version}</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${swagger.core.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-javadoc</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
@ -300,6 +327,23 @@
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.13.0</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>

@ -46,6 +46,11 @@
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-web</artifactId>

@ -16,6 +16,7 @@
<module>ruoyi-common-excel</module>
<module>ruoyi-common-core</module>
<module>ruoyi-common-redis</module>
<module>ruoyi-common-doc</module>
<module>ruoyi-common-security</module>
<module>ruoyi-common-satoken</module>
<module>ruoyi-common-web</module>

@ -22,6 +22,13 @@
<version>${project.version}</version>
</dependency>
<!-- 接口模块 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 安全模块 -->
<dependency>
<groupId>com.ruoyi</groupId>

@ -73,6 +73,11 @@
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>

@ -9,7 +9,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-swagger</artifactId>
<artifactId>ruoyi-common-doc</artifactId>
<description>
ruoyi-common-swagger系统接口
@ -23,15 +23,14 @@
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<!-- SpringBoot Web -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-web</artifactId>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-javadoc</artifactId>
</dependency>
</dependencies>

@ -0,0 +1,78 @@
package com.ruoyi.common.doc.config;
import com.ruoyi.common.doc.handler.OpenApiHandler;
import com.ruoyi.common.doc.config.properties.SwaggerProperties;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.*;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.providers.JavadocProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Swagger
*
* @author Lion Li
*/
@RequiredArgsConstructor
@AutoConfiguration(before = SpringDocConfiguration.class)
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true", matchIfMissing = true)
public class SwaggerAutoConfiguration {
private final SwaggerProperties swaggerProperties;
@Bean
@ConditionalOnMissingBean(OpenAPI.class)
public OpenAPI openApi() {
OpenAPI openApi = new OpenAPI();
// 文档基本信息
SwaggerProperties.InfoProperties infoProperties = swaggerProperties.getInfo();
Info info = convertInfo(infoProperties);
openApi.info(info);
// 扩展文档信息
openApi.externalDocs(swaggerProperties.getExternalDocs());
openApi.tags(swaggerProperties.getTags());
openApi.paths(swaggerProperties.getPaths());
openApi.components(swaggerProperties.getComponents());
List<SecurityRequirement> list = new ArrayList<>();
list.add(new SecurityRequirement().addList("apikey"));
openApi.security(list);
return openApi;
}
private Info convertInfo(SwaggerProperties.InfoProperties infoProperties) {
Info info = new Info();
info.setTitle(infoProperties.getTitle());
info.setDescription(infoProperties.getDescription());
info.setContact(infoProperties.getContact());
info.setLicense(infoProperties.getLicense());
info.setVersion(infoProperties.getVersion());
return info;
}
/**
* openapi
*/
@Bean
public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
}
}

@ -0,0 +1,99 @@
package com.ruoyi.common.doc.config.properties;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import java.util.List;
/**
* swagger
*
* @author Lion Li
*/
@Data
@ConfigurationProperties(prefix = "swagger")
public class SwaggerProperties {
/**
* openApi
*/
private Boolean enabled = true;
/**
*
*/
@NestedConfigurationProperty
private InfoProperties info = new InfoProperties();
/**
*
*/
@NestedConfigurationProperty
private ExternalDocumentation externalDocs;
/**
*
*/
private List<Tag> tags = null;
/**
*
*/
@NestedConfigurationProperty
private Paths paths = null;
/**
*
*/
@NestedConfigurationProperty
private Components components = null;
/**
* <p>
*
* </p>
*
* @see io.swagger.v3.oas.models.info.Info
*
* springboot
*/
@Data
public static class InfoProperties {
/**
*
*/
private String title = null;
/**
*
*/
private String description = null;
/**
*
*/
@NestedConfigurationProperty
private Contact contact = null;
/**
*
*/
@NestedConfigurationProperty
private License license = null;
/**
*
*/
private String version = null;
}
}

@ -0,0 +1,272 @@
package com.ruoyi.common.doc.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<String, Object> mappingsMap = new HashMap<>();
/**
* The Springdoc tags.
*/
private final Map<HandlerMethod, Tag> springdocTags = new HashMap<>();
/**
* The Open api builder customisers.
*/
private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
/**
* The server base URL customisers.
*/
private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
/**
* The Spring doc config properties.
*/
private final SpringDocConfigProperties springDocConfigProperties;
/**
* The Open api.
*/
private OpenAPI openAPI;
/**
* The Cached open api map.
*/
private final Map<String, OpenAPI> 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> 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> openAPI, SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
Optional<JavadocProvider> 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<Tag> tags = new HashSet<>();
Set<String> 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)) {
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<String> 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)) {
Tag tag = new Tag();
// 自定义部分 修改使用java注释当tag名
List<String> 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<Tag> 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<Tag> tags, Set<String> tagsStr, Locale locale) {
// method tags
Set<Tags> tagsSet = AnnotatedElementUtils
.findAllMergedAnnotations(method, Tags.class);
Set<io.swagger.v3.oas.annotations.tags.Tag> 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<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
addTags(allTags, tags, locale);
}
}
private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<Tag> tags, Locale locale) {
Optional<Set<Tag>> 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);
});
});
}
}

@ -1,133 +0,0 @@
package com.ruoyi.common.swagger.config;
import cn.dev33.satoken.config.SaTokenConfig;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.swagger.config.properties.SwaggerProperties;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* Swagger
*
* @author Lion Li
*/
@AutoConfiguration
@EnableKnife4j
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration {
@Autowired
private SaTokenConfig saTokenConfig;
@Value("${spring.application.name}")
private String appName;
/**
* Spring Boot
*/
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
private static final String BASE_PATH = "/**";
@Bean
public Docket api(SwaggerProperties swaggerProperties) {
// base-path处理
if (swaggerProperties.getBasePath().isEmpty()) {
swaggerProperties.getBasePath().add(BASE_PATH);
}
// noinspection unchecked
List<Predicate<String>> basePath = new ArrayList<>();
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
// exclude-path处理
if (swaggerProperties.getExcludePath().isEmpty()) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
List<Predicate<String>> excludePath = new ArrayList<>();
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
ApiSelectorBuilder builder = new Docket(DocumentationType.OAS_30)
.enable(swaggerProperties.getEnabled())
.host(swaggerProperties.getHost())
.apiInfo(apiInfo(swaggerProperties))
.select()
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));
swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));
swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
return builder.build()
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(StringUtils.substring(appName, appName.indexOf("-") + 1));
}
/**
* tokenAuthorization
*/
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> apiKeyList = new ArrayList<>();
String header = saTokenConfig.getTokenName();
apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue()));
return apiKeyList;
}
/**
*
*/
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
*
*/
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference(saTokenConfig.getTokenName(), authorizationScopes));
return securityReferences;
}
private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.license(swaggerProperties.getLicense())
.licenseUrl(swaggerProperties.getLicenseUrl())
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
.version(swaggerProperties.getVersion())
.build();
}
}

@ -1,41 +0,0 @@
package com.ruoyi.common.swagger.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import java.lang.reflect.Field;
import java.util.List;
/**
* swagger springboot 2.6.x
*
* @author ruoyi
*/
@SuppressWarnings("all")
public class SwaggerBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
mappings.removeIf(mapping -> mapping.getPatternParser() != null);
}
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}

@ -1,143 +0,0 @@
package com.ruoyi.common.swagger.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* swagger
*
* @author Lion Li
*/
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* swagger
*/
private Boolean enabled;
/**
* swagger
**/
private String basePackage = "";
/**
* swaggerurl
**/
private List<String> basePath = new ArrayList<>();
/**
* basePathurl
**/
private List<String> excludePath = new ArrayList<>();
/**
*
**/
private String title = "";
/**
*
**/
private String description = "";
/**
*
**/
private String version = "";
/**
*
**/
private String license = "";
/**
* URL
**/
private String licenseUrl = "";
/**
* URL
**/
private String termsOfServiceUrl = "";
/**
* host
**/
private String host = "";
/**
*
*/
private Contact contact = new Contact();
/**
*
**/
private Authorization authorization = new Authorization();
@Data
@NoArgsConstructor
public static class Contact {
/**
*
**/
private String name = "";
/**
* url
**/
private String url = "";
/**
* email
**/
private String email = "";
}
@Data
@NoArgsConstructor
public static class Authorization {
/**
* IDSecurityReferences ID
*/
private String name = "";
/**
* URL
*/
private String authRegex = "^.*$";
/**
*
*/
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
private List<String> tokenUrlList = new ArrayList<>();
}
@Data
@NoArgsConstructor
public static class AuthorizationScope {
/**
*
*/
private String scope = "";
/**
*
*/
private String description = "";
}
}

@ -1,2 +0,0 @@
com.ruoyi.common.swagger.config.SwaggerAutoConfiguration
com.ruoyi.common.swagger.config.SwaggerBeanPostProcessor

@ -52,6 +52,11 @@
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-web</artifactId>

@ -61,6 +61,11 @@
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-web</artifactId>

@ -1,47 +1,54 @@
package com.ruoyi.gateway.handler;
import com.ruoyi.gateway.utils.WebFluxUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
*
*
* @author ruoyi
*/
@Slf4j
@Order(-1)
@Configuration
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
}
String msg;
if (ex instanceof NotFoundException) {
msg = "服务未找到";
} else if (ex instanceof ResponseStatusException) {
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
msg = responseStatusException.getMessage();
} else {
msg = "内部服务器错误";
}
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
return WebFluxUtils.webFluxResponseWriter(response, msg);
}
}
package com.ruoyi.gateway.handler;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.gateway.utils.WebFluxUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
*
*
* @author ruoyi
*/
@Slf4j
@Order(-1)
@Configuration
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
private static final String[] IGNORE_PATH = new String[]{"/favicon.ico"};
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
}
String msg;
if (ex instanceof NotFoundException) {
msg = "服务未找到";
} else if (ex instanceof ResponseStatusException) {
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
msg = responseStatusException.getMessage();
} else {
msg = "内部服务器错误";
}
RequestPath path = exchange.getRequest().getPath();
if (!StringUtils.equalsAny(path.toString(), IGNORE_PATH)) {
log.error("[网关异常处理]请求路径:{},异常信息:{}", path, ex.getMessage());
}
return WebFluxUtils.webFluxResponseWriter(response, msg);
}
}

@ -59,6 +59,11 @@
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-web</artifactId>

@ -47,6 +47,11 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-dubbo</artifactId>

@ -58,6 +58,11 @@
<artifactId>ruoyi-common-dict</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-web</artifactId>

Loading…
Cancel
Save