diff --git a/config/dev/application-common.yml b/config/dev/application-common.yml index 69b04b59..2882c5ff 100644 --- a/config/dev/application-common.yml +++ b/config/dev/application-common.yml @@ -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: diff --git a/pom.xml b/pom.xml index b13f58f5..74a2e35f 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,8 @@ 3.5.1 2.3 1.2.83 + 2.2.0 + 1.6.9 5.2.2 3.1.1 5.8.4 @@ -35,6 +37,7 @@ 2.2.2 2.3.1 1.30.0 + 1.18.24 7.1.1 @@ -178,6 +181,30 @@ ${p6spy.version} + + io.swagger.core.v3 + swagger-annotations + ${swagger.core.version} + + + + org.springdoc + springdoc-openapi-webmvc-core + ${springdoc.version} + + + + org.springdoc + springdoc-openapi-javadoc + ${springdoc.version} + + + + org.projectlombok + lombok + ${lombok.version} + + org.apache.poi poi @@ -300,6 +327,23 @@ ${java.version} ${java.version} ${project.build.sourceEncoding} + + + com.github.therapi + therapi-runtime-javadoc-scribe + 0.13.0 + + + org.projectlombok + lombok + ${lombok.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + diff --git a/ruoyi-auth/pom.xml b/ruoyi-auth/pom.xml index dd9eeb4d..e29d8556 100644 --- a/ruoyi-auth/pom.xml +++ b/ruoyi-auth/pom.xml @@ -46,6 +46,11 @@ ruoyi-common-security + + com.ruoyi + ruoyi-common-doc + + com.ruoyi ruoyi-common-web diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 2c40aeeb..13f4039e 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -16,6 +16,7 @@ ruoyi-common-excel ruoyi-common-core ruoyi-common-redis + ruoyi-common-doc ruoyi-common-security ruoyi-common-satoken ruoyi-common-web diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 3567b8f6..57654bbe 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -22,6 +22,13 @@ ${project.version} + + + com.ruoyi + ruoyi-common-doc + ${project.version} + + com.ruoyi diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index ef2d3dba..12fc1537 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -73,6 +73,11 @@ javax.servlet-api + + io.swagger.core.v3 + swagger-annotations + + org.projectlombok lombok diff --git a/ruoyi-common/ruoyi-common-swagger/pom.xml b/ruoyi-common/ruoyi-common-doc/pom.xml similarity index 75% rename from ruoyi-common/ruoyi-common-swagger/pom.xml rename to ruoyi-common/ruoyi-common-doc/pom.xml index 4de434d2..d1455329 100644 --- a/ruoyi-common/ruoyi-common-swagger/pom.xml +++ b/ruoyi-common/ruoyi-common-doc/pom.xml @@ -9,7 +9,7 @@ 4.0.0 - ruoyi-common-swagger + ruoyi-common-doc ruoyi-common-swagger系统接口 @@ -23,15 +23,14 @@ ruoyi-common-core - - com.ruoyi - ruoyi-common-web + org.springdoc + springdoc-openapi-webmvc-core - cn.dev33 - sa-token-core + org.springdoc + springdoc-openapi-javadoc diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerAutoConfiguration.java b/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerAutoConfiguration.java new file mode 100644 index 00000000..14aa3564 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/SwaggerAutoConfiguration.java @@ -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 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, + SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomisers, + Optional> serverBaseUrlCustomisers, Optional javadocProvider) { + return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider); + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java b/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java new file mode 100644 index 00000000..7e157c1f --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/config/properties/SwaggerProperties.java @@ -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 tags = null; + + /** + * 路径 + */ + @NestedConfigurationProperty + private Paths paths = null; + + /** + * 组件 + */ + @NestedConfigurationProperty + private Components components = null; + + /** + *

+ * 文档的基础属性信息 + *

+ * + * @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; + + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/handler/OpenApiHandler.java b/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/handler/OpenApiHandler.java new file mode 100644 index 00000000..4e0dbd49 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/com/ruoyi/common/doc/handler/OpenApiHandler.java @@ -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 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)) { + 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)) { + Tag tag = new 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); + }); + }); + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..e3302e60 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.ruoyi.common.doc.config.SwaggerAutoConfiguration diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java deleted file mode 100644 index 58df8cff..00000000 --- a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerAutoConfiguration.java +++ /dev/null @@ -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 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> 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> 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)); - } - - /** - * 安全模式,这里指定token通过Authorization头请求头传递 - */ - private List securitySchemes() { - List apiKeyList = new ArrayList<>(); - String header = saTokenConfig.getTokenName(); - apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue())); - return apiKeyList; - } - - /** - * 安全上下文 - */ - private List securityContexts() { - List securityContexts = new ArrayList<>(); - securityContexts.add( - SecurityContext.builder() - .securityReferences(defaultAuth()) - .operationSelector(o -> o.requestMappingPattern().matches("/.*")) - .build()); - return securityContexts; - } - - /** - * 默认的全局鉴权策略 - */ - private List defaultAuth() { - AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); - AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; - authorizationScopes[0] = authorizationScope; - List 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(); - } -} diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerBeanPostProcessor.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerBeanPostProcessor.java deleted file mode 100644 index 2436a550..00000000 --- a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/SwaggerBeanPostProcessor.java +++ /dev/null @@ -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 void customizeSpringfoxHandlerMappings(List mappings) { - mappings.removeIf(mapping -> mapping.getPatternParser() != null); - } - - private List getHandlerMappings(Object bean) { - try { - Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); - field.setAccessible(true); - return (List) field.get(bean); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new IllegalStateException(e); - } - } -} diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/properties/SwaggerProperties.java b/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/properties/SwaggerProperties.java deleted file mode 100644 index 12cf880c..00000000 --- a/ruoyi-common/ruoyi-common-swagger/src/main/java/com/ruoyi/common/swagger/config/properties/SwaggerProperties.java +++ /dev/null @@ -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 = ""; - - /** - * swagger会解析的url规则 - **/ - private List basePath = new ArrayList<>(); - - /** - * 在basePath基础上需要排除的url规则 - **/ - private List 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 { - - /** - * 鉴权策略ID,需要和SecurityReferences ID保持一致 - */ - private String name = ""; - - /** - * 需要开启鉴权URL的正则 - */ - private String authRegex = "^.*$"; - - /** - * 鉴权作用域列表 - */ - private List authorizationScopeList = new ArrayList<>(); - - private List tokenUrlList = new ArrayList<>(); - - } - - @Data - @NoArgsConstructor - public static class AuthorizationScope { - - /** - * 作用域名称 - */ - private String scope = ""; - - /** - * 作用域描述 - */ - private String description = ""; - - } - -} diff --git a/ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index bab0482f..00000000 --- a/ruoyi-common/ruoyi-common-swagger/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1,2 +0,0 @@ -com.ruoyi.common.swagger.config.SwaggerAutoConfiguration -com.ruoyi.common.swagger.config.SwaggerBeanPostProcessor diff --git a/ruoyi-example/ruoyi-demo/pom.xml b/ruoyi-example/ruoyi-demo/pom.xml index 27e496af..d8c74b3d 100644 --- a/ruoyi-example/ruoyi-demo/pom.xml +++ b/ruoyi-example/ruoyi-demo/pom.xml @@ -52,6 +52,11 @@ ruoyi-common-log + + com.ruoyi + ruoyi-common-doc + + com.ruoyi ruoyi-common-web diff --git a/ruoyi-example/ruoyi-stream-mq/pom.xml b/ruoyi-example/ruoyi-stream-mq/pom.xml index c31ab311..d9bed52b 100644 --- a/ruoyi-example/ruoyi-stream-mq/pom.xml +++ b/ruoyi-example/ruoyi-stream-mq/pom.xml @@ -61,6 +61,11 @@ ruoyi-common-security + + com.ruoyi + ruoyi-common-doc + + com.ruoyi ruoyi-common-web diff --git a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java index 3a05cdd7..b0d99b5b 100644 --- a/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java +++ b/ruoyi-gateway/src/main/java/com/ruoyi/gateway/handler/GatewayExceptionHandler.java @@ -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 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); - } -} \ No newline at end of file +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 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); + } +} diff --git a/ruoyi-modules/ruoyi-gen/pom.xml b/ruoyi-modules/ruoyi-gen/pom.xml index 5b636428..e381cc68 100644 --- a/ruoyi-modules/ruoyi-gen/pom.xml +++ b/ruoyi-modules/ruoyi-gen/pom.xml @@ -59,6 +59,11 @@ ruoyi-common-log + + com.ruoyi + ruoyi-common-doc + + com.ruoyi ruoyi-common-web diff --git a/ruoyi-modules/ruoyi-resource/pom.xml b/ruoyi-modules/ruoyi-resource/pom.xml index ffdd0376..11806ca6 100644 --- a/ruoyi-modules/ruoyi-resource/pom.xml +++ b/ruoyi-modules/ruoyi-resource/pom.xml @@ -47,6 +47,11 @@ spring-boot-starter-actuator + + com.ruoyi + ruoyi-common-doc + + com.ruoyi ruoyi-common-dubbo diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml index 59602495..639aa3b1 100644 --- a/ruoyi-modules/ruoyi-system/pom.xml +++ b/ruoyi-modules/ruoyi-system/pom.xml @@ -58,6 +58,11 @@ ruoyi-common-dict + + com.ruoyi + ruoyi-common-doc + + com.ruoyi ruoyi-common-web