123
This commit is contained in:
-2
@@ -3,7 +3,6 @@ package com.aivfo.el.starter.base;
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import com.aivfo.el.starter.base.utils.StringUtils;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -41,7 +40,6 @@ import java.io.Serializable;
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@SuppressWarnings("all")
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public abstract class Result<T> implements Serializable {
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,9 +11,33 @@
|
||||
|
||||
<artifactId>aivfo-doc-spring-boot-autoconfigure</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<artifactId>aivfo-doc-spring-boot-core</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
package com.aivfo.doc.autoconfigure.knife4j;
|
||||
|
||||
import com.aivfo.doc.core.common.schema.AivfoOperationModelsProviderPlugin;
|
||||
import com.aivfo.doc.core.common.schema.AivfoOperationResponseMessagePlugin;
|
||||
import com.aivfo.doc.core.knife4j.EnableKnife4j;
|
||||
import com.github.xiaoymin.knife4j.core.extend.OpenApiExtendSetting;
|
||||
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
|
||||
import com.github.xiaoymin.knife4j.spring.filter.ProductionSecurityFilter;
|
||||
import com.github.xiaoymin.knife4j.spring.filter.SecurityBasicAuthFilter;
|
||||
import com.github.xiaoymin.knife4j.spring.model.MarkdownFiles;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import springfox.documentation.swagger2.web.Swagger2ControllerWebMvc;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
/**
|
||||
* @author: wangyl
|
||||
* @date: 2023/5/16
|
||||
* @description: Knife4j 基础自动配置类
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(value = {
|
||||
EnableKnife4j.class,
|
||||
Servlet.class,
|
||||
DispatcherServlet.class,
|
||||
Swagger2ControllerWebMvc.class
|
||||
})
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@EnableConfigurationProperties(value = {Knife4jProperties.class})
|
||||
@ComponentScan(basePackages = "com.github.xiaoymin.knife4j.spring.plugin")
|
||||
@Slf4j
|
||||
public class Knife4jAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 配置Cors
|
||||
*
|
||||
* @return cors filter
|
||||
* @since 2.0.4
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CorsFilter.class)
|
||||
public CorsFilter corsFilter() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.addAllowedOrigin("*");
|
||||
corsConfiguration.addAllowedHeader("*");
|
||||
corsConfiguration.addAllowedMethod("*");
|
||||
corsConfiguration.setMaxAge(10000L);
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown resolver
|
||||
*
|
||||
* @param knife4jProperties knife 4 j properties
|
||||
* @return the open api extension resolver
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Bean(initMethod = "start")
|
||||
@ConditionalOnMissingBean(OpenApiExtensionResolver.class)
|
||||
@ConditionalOnProperty(name = "knife4j.enable", havingValue = "true")
|
||||
public OpenApiExtensionResolver markdownResolver(Knife4jProperties knife4jProperties) {
|
||||
OpenApiExtendSetting setting = knife4jProperties.getSetting();
|
||||
if (setting == null) {
|
||||
setting = new OpenApiExtendSetting();
|
||||
}
|
||||
return new OpenApiExtensionResolver(setting, knife4jProperties.getDocuments());
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化自定义Markdown特性
|
||||
*
|
||||
* @param knife4jProperties 配置文件
|
||||
* @return markdownFiles markdown files
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Bean(initMethod = "init")
|
||||
public MarkdownFiles markdownFiles(@NotNull Knife4jProperties knife4jProperties) {
|
||||
return new MarkdownFiles(knife4jProperties.getMarkdowns() == null ? "" : knife4jProperties.getMarkdowns());
|
||||
}
|
||||
|
||||
/**
|
||||
* Security basic auth filter
|
||||
*
|
||||
* @param knife4jProperties knife 4 j properties
|
||||
* @return the security basic auth filter
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Bean
|
||||
public SecurityBasicAuthFilter securityBasicAuthFilter(@NotNull Knife4jProperties knife4jProperties) {
|
||||
|
||||
return new SecurityBasicAuthFilter(knife4jProperties.getBasic().isEnable(),
|
||||
knife4jProperties.getBasic().getUsername(),
|
||||
knife4jProperties.getBasic().getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
* Production security filter
|
||||
*
|
||||
* @return the production security filter
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Bean
|
||||
public ProductionSecurityFilter productionSecurityFilter() {
|
||||
return new ProductionSecurityFilter(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mvc operation response message plugin
|
||||
*
|
||||
* @return the mvc operation response message plugin
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AivfoOperationResponseMessagePlugin mvcOperationResponseMessagePlugin() {
|
||||
return new AivfoOperationResponseMessagePlugin(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mvc operation models provider plugin
|
||||
*
|
||||
* @return the mvc operation models provider plugin
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AivfoOperationModelsProviderPlugin mvcOperationModelsProviderPlugin() {
|
||||
return new AivfoOperationModelsProviderPlugin(null);
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.aivfo.doc.autoconfigure.knife4j;
|
||||
|
||||
import com.github.xiaoymin.knife4j.core.extend.OpenApiExtendSetting;
|
||||
import com.github.xiaoymin.knife4j.core.model.MarkdownProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = Knife4jProperties.PREFIX)
|
||||
public class Knife4jProperties {
|
||||
/**
|
||||
* PREFIX
|
||||
*/
|
||||
public static final String PREFIX = "aivfo.doc.knife4j";
|
||||
/**
|
||||
* 是否开启BasicHttp验证
|
||||
*/
|
||||
private Knife4jHttpBasic basic = new Knife4jHttpBasic();
|
||||
/**
|
||||
* markdown 路径
|
||||
*/
|
||||
private String markdowns;
|
||||
/**
|
||||
* 是否开启Knife4j增强模式
|
||||
*/
|
||||
private boolean enable = false;
|
||||
/**
|
||||
* 是否开启默认跨域
|
||||
*/
|
||||
private boolean cors = false;
|
||||
/**
|
||||
* 是否生产环境
|
||||
*/
|
||||
private boolean production = false;
|
||||
/**
|
||||
* 个性化配置
|
||||
*/
|
||||
private OpenApiExtendSetting setting;
|
||||
/**
|
||||
* 分组文档集合
|
||||
*/
|
||||
private List<MarkdownProperty> documents;
|
||||
|
||||
@Data
|
||||
public static class Knife4jHttpBasic {
|
||||
|
||||
/**
|
||||
* basic 是否开启,默认为false
|
||||
*/
|
||||
private boolean enable = false;
|
||||
/**
|
||||
* basic 用户名
|
||||
*/
|
||||
private String username = "aivfo";
|
||||
/**
|
||||
* basic 密码
|
||||
*/
|
||||
private String password = "aivfo2017";
|
||||
}
|
||||
}
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
package com.aivfo.doc.autoconfigure.swagger;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.*;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import springfox.documentation.builders.*;
|
||||
import springfox.documentation.schema.ModelRef;
|
||||
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 springfox.documentation.swagger.web.ApiKeyVehicle;
|
||||
import springfox.documentation.swagger.web.UiConfiguration;
|
||||
import springfox.documentation.swagger.web.UiConfigurationBuilder;
|
||||
import springfox.documentation.swagger2.configuration.Swagger2DocumentationWebMvcConfiguration;
|
||||
import springfox.documentation.swagger2.web.Swagger2ControllerWebMvc;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(value = {Servlet.class, DispatcherServlet.class, Swagger2ControllerWebMvc.class})
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@Import(value = {Swagger2DocumentationWebMvcConfiguration.class})
|
||||
@ConditionalOnProperty(name = SwaggerDocProperties.PREFIX + ".enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SwaggerDocProperties.class)
|
||||
public class SwaggerDocAutoConfiguration implements BeanFactoryAware {
|
||||
|
||||
/**
|
||||
* Swagger properties
|
||||
*/
|
||||
private final SwaggerDocProperties swaggerProperties;
|
||||
/**
|
||||
* Basic auth
|
||||
*/
|
||||
private static final String BASIC_AUTH = "BasicAuth";
|
||||
/**
|
||||
* None
|
||||
*/
|
||||
private static final String NONE = "None";
|
||||
/**
|
||||
* Bean factory
|
||||
*/
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Instantiates a new Swagger auto configuration.
|
||||
*
|
||||
* @param swaggerProperties the swagger properties
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public SwaggerDocAutoConfiguration(@NotNull SwaggerDocProperties swaggerProperties) {
|
||||
this.swaggerProperties = swaggerProperties;
|
||||
// 默认排除 /actuator 接口文档
|
||||
swaggerProperties.getExcludePath().add("/actuator/**");
|
||||
swaggerProperties.getExcludePath().add("/error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ui configuration ui configuration.
|
||||
*
|
||||
* @param swaggerProperties the swagger properties
|
||||
* @return the ui configuration
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Bean
|
||||
public UiConfiguration uiConfiguration(@NotNull SwaggerDocProperties swaggerProperties) {
|
||||
return UiConfigurationBuilder.builder()
|
||||
.deepLinking(swaggerProperties.getUiConfig().getDeepLinking())
|
||||
.defaultModelExpandDepth(swaggerProperties.getUiConfig().getDefaultModelExpandDepth())
|
||||
.defaultModelRendering(swaggerProperties.getUiConfig().getDefaultModelRendering())
|
||||
.defaultModelsExpandDepth(swaggerProperties.getUiConfig().getDefaultModelsExpandDepth())
|
||||
.displayOperationId(swaggerProperties.getUiConfig().getDisplayOperationId())
|
||||
.displayRequestDuration(swaggerProperties.getUiConfig().getDisplayRequestDuration())
|
||||
.docExpansion(swaggerProperties.getUiConfig().getDocExpansion())
|
||||
.maxDisplayedTags(swaggerProperties.getUiConfig().getMaxDisplayedTags())
|
||||
.operationsSorter(swaggerProperties.getUiConfig().getOperationsSorter())
|
||||
.showExtensions(swaggerProperties.getUiConfig().getShowExtensions())
|
||||
.tagsSorter(swaggerProperties.getUiConfig().getTagsSorter())
|
||||
.validatorUrl(swaggerProperties.getUiConfig().getValidatorUrl())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create rest api list.
|
||||
*
|
||||
* @param swaggerProperties the swagger properties
|
||||
* @return the list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(UiConfiguration.class)
|
||||
@ConditionalOnProperty(name = SwaggerDocProperties.PREFIX + ".enabled", matchIfMissing = true)
|
||||
public List<Docket> createRestApi(@NotNull SwaggerDocProperties swaggerProperties) {
|
||||
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) this.beanFactory;
|
||||
List<Docket> docketList = new LinkedList<>();
|
||||
|
||||
// 没有分组
|
||||
if (swaggerProperties.getDocket().size() == 0) {
|
||||
SwaggerDocProperties.DocketInfo docketInfo = new SwaggerDocProperties.DocketInfo()
|
||||
.setTitle(swaggerProperties.getTitle())
|
||||
.setDescription(swaggerProperties.getDescription())
|
||||
.setVersion(StringUtils.isEmpty(swaggerProperties.getVersion())
|
||||
? "1.0"
|
||||
: swaggerProperties.getVersion())
|
||||
.setLicense(swaggerProperties.getLicense())
|
||||
.setLicenseUrl(swaggerProperties.getLicenseUrl())
|
||||
.setBasePackage(swaggerProperties.getBasePackage())
|
||||
.setContact(new SwaggerDocProperties.Contact(swaggerProperties.getContact().getName(),
|
||||
swaggerProperties.getContact().getUrl(),
|
||||
swaggerProperties.getContact().getEmail()))
|
||||
.setTermsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl());
|
||||
|
||||
swaggerProperties.getDocket().put("wyl", docketInfo);
|
||||
}
|
||||
|
||||
// 分组创建
|
||||
return this.buildDockets(swaggerProperties, configurableBeanFactory, docketList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build dockets list
|
||||
*
|
||||
* @param swaggerProperties swagger properties
|
||||
* @param configurableBeanFactory configurable bean factory
|
||||
* @param docketList docket list
|
||||
* @return the list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Contract("_, _, _ -> param3")
|
||||
private List<Docket> buildDockets(@NotNull SwaggerDocProperties swaggerProperties,
|
||||
ConfigurableBeanFactory configurableBeanFactory,
|
||||
List<Docket> docketList) {
|
||||
for (String groupName : swaggerProperties.getDocket().keySet()) {
|
||||
SwaggerDocProperties.DocketInfo docketInfo = swaggerProperties.getDocket().get(groupName);
|
||||
|
||||
ApiInfo apiInfo = new ApiInfoBuilder()
|
||||
.title(docketInfo.getTitle().isEmpty() ? swaggerProperties.getTitle() : docketInfo.getTitle())
|
||||
.description(docketInfo.getDescription().isEmpty() ? swaggerProperties.getDescription() : docketInfo.getDescription())
|
||||
.version(docketInfo.getVersion().isEmpty() ? StringUtils.isEmpty(swaggerProperties.getVersion())
|
||||
? "1.0"
|
||||
: swaggerProperties.getVersion()
|
||||
: docketInfo.getVersion())
|
||||
.license(docketInfo.getLicense().isEmpty() ? swaggerProperties.getLicense() : docketInfo.getLicense())
|
||||
.licenseUrl(docketInfo.getLicenseUrl().isEmpty() ? swaggerProperties.getLicenseUrl() : docketInfo.getLicenseUrl())
|
||||
.contact(
|
||||
new Contact(
|
||||
docketInfo.getContact().getName().isEmpty()
|
||||
? swaggerProperties.getContact().getName()
|
||||
: docketInfo.getContact().getName(),
|
||||
docketInfo.getContact().getUrl().isEmpty()
|
||||
? swaggerProperties.getContact().getUrl()
|
||||
: docketInfo.getContact().getUrl(),
|
||||
docketInfo.getContact().getEmail().isEmpty()
|
||||
? swaggerProperties.getContact().getEmail()
|
||||
: docketInfo.getContact().getEmail()
|
||||
)
|
||||
)
|
||||
.termsOfServiceUrl(docketInfo.getTermsOfServiceUrl().isEmpty()
|
||||
? swaggerProperties.getTermsOfServiceUrl()
|
||||
: docketInfo.getTermsOfServiceUrl())
|
||||
.build();
|
||||
|
||||
Docket docketForBuilder = new Docket(DocumentationType.SWAGGER_2)
|
||||
.host(swaggerProperties.getHost())
|
||||
.apiInfo(apiInfo)
|
||||
.securityContexts(Collections.singletonList(this.securityContext()))
|
||||
|
||||
.globalOperationParameters(this.assemblyGlobalOperationParameters(
|
||||
swaggerProperties.getGlobalOperationParameters(),
|
||||
docketInfo.getGlobalOperationParameters()));
|
||||
|
||||
this.processAuthAndResponse(swaggerProperties, docketForBuilder);
|
||||
|
||||
Docket docket = this.processPath(
|
||||
docketForBuilder
|
||||
.groupName(groupName)
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage(docketInfo.getBasePackage())))
|
||||
.build();
|
||||
|
||||
// ignoredParameterTypes
|
||||
Class<?>[] array = new Class[docketInfo.getIgnoredParameterTypes().size()];
|
||||
Class<?>[] ignoredParameterTypes = docketInfo.getIgnoredParameterTypes().toArray(array);
|
||||
docket.ignoredParameterTypes(ignoredParameterTypes);
|
||||
|
||||
// 如果是 dubbo 应用, 会导致存在相同的 bean name, 因此这里添加一个前缀
|
||||
configurableBeanFactory.registerSingleton("SwaggerDoc@" + groupName, docket);
|
||||
docketList.add(docket);
|
||||
}
|
||||
return docketList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process path *
|
||||
*
|
||||
* @param apiSelectorBuilder api selector builder
|
||||
* @return the api selector builder
|
||||
* @since 1.4.0
|
||||
*/
|
||||
private ApiSelectorBuilder processPath(ApiSelectorBuilder apiSelectorBuilder) {
|
||||
// base-path 处理 当没有配置任何 path 的时候,解析 /**
|
||||
if (this.swaggerProperties.getBasePath().isEmpty()) {
|
||||
this.swaggerProperties.getBasePath().add("/**");
|
||||
}
|
||||
|
||||
for (String base : this.swaggerProperties.getBasePath()) {
|
||||
apiSelectorBuilder.paths(PathSelectors.ant(base));
|
||||
}
|
||||
|
||||
for (String exclude : this.swaggerProperties.getExcludePath()) {
|
||||
apiSelectorBuilder.paths(PathSelectors.ant(exclude).negate());
|
||||
}
|
||||
return apiSelectorBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置默认的全局鉴权策略的开关,以及通过正则表达式进行匹配; 默认 ^.*$ 匹配所有 URL
|
||||
* 其中 securityReferences 为配置启用的鉴权策略
|
||||
*
|
||||
* @return security context
|
||||
* @since 1.4.0
|
||||
*/
|
||||
private SecurityContext securityContext() {
|
||||
return SecurityContext.builder()
|
||||
.securityReferences(this.defaultAuth())
|
||||
.forPaths(PathSelectors.regex(this.swaggerProperties.getAuthorization().getAuthRegex()))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build global operation parameters from swagger properties list
|
||||
*
|
||||
* @param globalOperationParameters global operation parameters
|
||||
* @return the list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
private List<Parameter> buildGlobalOperationParametersFromSwaggerProperties(
|
||||
List<SwaggerDocProperties.GlobalOperationParameter> globalOperationParameters) {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
|
||||
if (Objects.isNull(globalOperationParameters)) {
|
||||
return parameters;
|
||||
}
|
||||
for (SwaggerDocProperties.GlobalOperationParameter globalOperationParameter : globalOperationParameters) {
|
||||
parameters.add(new ParameterBuilder()
|
||||
.name(globalOperationParameter.getName())
|
||||
.description(globalOperationParameter.getDescription())
|
||||
.modelRef(new ModelRef(globalOperationParameter.getModelRef()))
|
||||
.parameterType(globalOperationParameter.getParameterType())
|
||||
.required(Boolean.parseBoolean(globalOperationParameter.getRequired()))
|
||||
.build());
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process auth and response *
|
||||
*
|
||||
* @param swaggerProperties swagger properties
|
||||
* @param docketForBuilder docket for builder
|
||||
* @since 1.4.0
|
||||
*/
|
||||
private void processAuthAndResponse(@NotNull SwaggerDocProperties swaggerProperties, Docket docketForBuilder) {
|
||||
if (BASIC_AUTH.equalsIgnoreCase(swaggerProperties.getAuthorization().getType())) {
|
||||
docketForBuilder.securitySchemes(Collections.singletonList(this.basicAuth()));
|
||||
} else if (!NONE.equalsIgnoreCase(swaggerProperties.getAuthorization().getType())) {
|
||||
docketForBuilder.securitySchemes(Collections.singletonList(this.apiKey()));
|
||||
}
|
||||
|
||||
// 全局响应消息
|
||||
if (!swaggerProperties.isApplyDefaultResponseMessages()) {
|
||||
this.buildGlobalResponseMessage(swaggerProperties, docketForBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 局部参数按照 name 覆盖局部参数
|
||||
*
|
||||
* @param globalOperationParameters global operation parameters
|
||||
* @param docketOperationParameters docket operation parameters
|
||||
* @return list list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
private List<Parameter> assemblyGlobalOperationParameters(
|
||||
List<SwaggerDocProperties.GlobalOperationParameter> globalOperationParameters,
|
||||
List<SwaggerDocProperties.GlobalOperationParameter> docketOperationParameters) {
|
||||
|
||||
if (Objects.isNull(docketOperationParameters) || docketOperationParameters.isEmpty()) {
|
||||
return this.buildGlobalOperationParametersFromSwaggerProperties(globalOperationParameters);
|
||||
}
|
||||
|
||||
Set<String> docketNames = docketOperationParameters.stream()
|
||||
.map(SwaggerDocProperties.GlobalOperationParameter::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<SwaggerDocProperties.GlobalOperationParameter> resultOperationParameters = newArrayList();
|
||||
|
||||
if (Objects.nonNull(globalOperationParameters)) {
|
||||
for (SwaggerDocProperties.GlobalOperationParameter parameter : globalOperationParameters) {
|
||||
if (!docketNames.contains(parameter.getName())) {
|
||||
resultOperationParameters.add(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultOperationParameters.addAll(docketOperationParameters);
|
||||
return this.buildGlobalOperationParametersFromSwaggerProperties(resultOperationParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置默认的全局鉴权策略; 其中返回的 SecurityReference 中,reference 即为 ApiKey 对象里面的 name,保持一致才能开启全局鉴权
|
||||
*
|
||||
* @return list list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
private @Unmodifiable List<SecurityReference> defaultAuth() {
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
return Collections.singletonList(SecurityReference.builder()
|
||||
.reference(this.swaggerProperties.getAuthorization().getName())
|
||||
.scopes(authorizationScopes).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置基于 BasicAuth 的鉴权对象
|
||||
*
|
||||
* @return basic auth
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(" -> new")
|
||||
private BasicAuth basicAuth() {
|
||||
return new BasicAuth(this.swaggerProperties.getAuthorization().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置基于 ApiKey 的鉴权对象
|
||||
*
|
||||
* @return api key
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(" -> new")
|
||||
private ApiKey apiKey() {
|
||||
return new ApiKey(this.swaggerProperties.getAuthorization().getName(),
|
||||
this.swaggerProperties.getAuthorization().getKeyName(),
|
||||
ApiKeyVehicle.HEADER.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置全局响应消息
|
||||
*
|
||||
* @param swaggerProperties swaggerProperties 支持 POST,GET,PUT,PATCH,DELETE,HEAD,OPTIONS,TRACE
|
||||
* @param docketForBuilder swagger docket builder
|
||||
* @since 1.4.0
|
||||
*/
|
||||
private void buildGlobalResponseMessage(@NotNull SwaggerDocProperties swaggerProperties, @NotNull Docket docketForBuilder) {
|
||||
|
||||
SwaggerDocProperties.GlobalResponseMessage globalResponseMessages = swaggerProperties.getGlobalResponseMessage();
|
||||
|
||||
/* POST,GET,PUT,PATCH,DELETE,HEAD,OPTIONS,TRACE 响应消息体 **/
|
||||
List<ResponseMessage> postResponseMessages = this.getResponseMessageList(globalResponseMessages.getPost());
|
||||
List<ResponseMessage> getResponseMessages = this.getResponseMessageList(globalResponseMessages.getGet());
|
||||
List<ResponseMessage> putResponseMessages = this.getResponseMessageList(globalResponseMessages.getPut());
|
||||
List<ResponseMessage> patchResponseMessages = this.getResponseMessageList(globalResponseMessages.getPatch());
|
||||
List<ResponseMessage> deleteResponseMessages = this.getResponseMessageList(globalResponseMessages.getDelete());
|
||||
List<ResponseMessage> headResponseMessages = this.getResponseMessageList(globalResponseMessages.getHead());
|
||||
List<ResponseMessage> optionsResponseMessages = this.getResponseMessageList(globalResponseMessages.getOptions());
|
||||
List<ResponseMessage> trackResponseMessages = this.getResponseMessageList(globalResponseMessages.getTrace());
|
||||
|
||||
docketForBuilder.useDefaultResponseMessages(swaggerProperties.isApplyDefaultResponseMessages())
|
||||
.globalResponseMessage(RequestMethod.POST, postResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.GET, getResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.PUT, putResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.PATCH, patchResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.DELETE, deleteResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.HEAD, headResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.OPTIONS, optionsResponseMessages)
|
||||
.globalResponseMessage(RequestMethod.TRACE, trackResponseMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回消息体列表
|
||||
*
|
||||
* @param globalResponseMessageBodyList 全局 Code 消息返回集合
|
||||
* @return response message list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@NotNull
|
||||
private List<ResponseMessage> getResponseMessageList(
|
||||
@NotNull List<SwaggerDocProperties.GlobalResponseMessageBody> globalResponseMessageBodyList) {
|
||||
List<ResponseMessage> responseMessages = new ArrayList<>();
|
||||
for (SwaggerDocProperties.GlobalResponseMessageBody globalResponseMessageBody : globalResponseMessageBodyList) {
|
||||
ResponseMessageBuilder responseMessageBuilder = new ResponseMessageBuilder();
|
||||
responseMessageBuilder.code(globalResponseMessageBody.getCode()).message(globalResponseMessageBody.getMessage());
|
||||
|
||||
if (!StringUtils.isEmpty(globalResponseMessageBody.getModelRef())) {
|
||||
responseMessageBuilder.responseModel(new ModelRef(globalResponseMessageBody.getModelRef()));
|
||||
}
|
||||
responseMessages.add(responseMessageBuilder.build());
|
||||
}
|
||||
|
||||
return responseMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bean factory *
|
||||
*
|
||||
* @param beanFactory bean factory
|
||||
* @throws BeansException beans exception
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Override
|
||||
public void setBeanFactory(@NotNull BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
}
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
package com.aivfo.doc.autoconfigure.swagger;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import springfox.documentation.swagger.web.DocExpansion;
|
||||
import springfox.documentation.swagger.web.ModelRendering;
|
||||
import springfox.documentation.swagger.web.OperationsSorter;
|
||||
import springfox.documentation.swagger.web.TagsSorter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.2.3
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 14:54
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = SwaggerDocProperties.PREFIX)
|
||||
public class SwaggerDocProperties {
|
||||
/**
|
||||
* PREFIX
|
||||
*/
|
||||
public static final String PREFIX = "aivfo.doc.swagger";
|
||||
/**
|
||||
* 是否开启 swagger
|
||||
*/
|
||||
private boolean enabled;
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title = "";
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description = "";
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version = "";
|
||||
/**
|
||||
* 许可证
|
||||
*/
|
||||
private String license = "";
|
||||
/**
|
||||
* 许可证 URL
|
||||
*/
|
||||
private String licenseUrl = "";
|
||||
/**
|
||||
* 服务条款 URL
|
||||
*/
|
||||
private String termsOfServiceUrl = "";
|
||||
/**
|
||||
* 忽略的参数类型
|
||||
*/
|
||||
private List<Class<?>> ignoredParameterTypes = new ArrayList<>();
|
||||
/**
|
||||
* swagger 会解析的包路径
|
||||
*/
|
||||
private String basePackage = "com.aivfo.doc";
|
||||
/**
|
||||
* swagger 会解析的 url 规则
|
||||
*/
|
||||
private List<String> basePath = new ArrayList<>();
|
||||
/**
|
||||
* 在 basePath 基础上需要排除的 url 规则
|
||||
*/
|
||||
private List<String> excludePath = new ArrayList<>();
|
||||
/**
|
||||
* 分组文档
|
||||
*/
|
||||
private Map<String, DocketInfo> docket = new LinkedHashMap<>();
|
||||
/**
|
||||
* host 信息
|
||||
*/
|
||||
private String host = "";
|
||||
/**
|
||||
* 全局参数配置
|
||||
*/
|
||||
private List<GlobalOperationParameter> globalOperationParameters;
|
||||
/**
|
||||
* Contact
|
||||
*/
|
||||
private Contact contact = new Contact();
|
||||
/**
|
||||
* 页面功能配置
|
||||
*/
|
||||
private UiConfig uiConfig = new UiConfig();
|
||||
/**
|
||||
* 是否使用默认预定义的响应消息 ,默认 true
|
||||
*/
|
||||
private boolean applyDefaultResponseMessages = Boolean.TRUE;
|
||||
/**
|
||||
* 全局响应消息
|
||||
*/
|
||||
private GlobalResponseMessage globalResponseMessage;
|
||||
/**
|
||||
* 全局统一鉴权配置
|
||||
*/
|
||||
private Authorization authorization = new Authorization();
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class GlobalOperationParameter {
|
||||
/**
|
||||
* 参数名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 指定参数类型
|
||||
*/
|
||||
private String modelRef;
|
||||
/**
|
||||
* 参数放在哪个地方:header,query,path,body.form
|
||||
*/
|
||||
private String parameterType;
|
||||
/**
|
||||
* 参数是否必须传
|
||||
*/
|
||||
private String required;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@AllArgsConstructor
|
||||
public static class DocketInfo {
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title = "";
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description = "";
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version = "";
|
||||
/**
|
||||
* 许可证
|
||||
*/
|
||||
private String license = "";
|
||||
/**
|
||||
* 许可证 URL
|
||||
*/
|
||||
private String licenseUrl = "";
|
||||
/**
|
||||
* 服务条款 URL
|
||||
*/
|
||||
private String termsOfServiceUrl = "";
|
||||
/**
|
||||
* Contact
|
||||
*/
|
||||
private Contact contact = new Contact();
|
||||
/**
|
||||
* swagger 会解析的包路径
|
||||
*/
|
||||
private String basePackage = "";
|
||||
/**
|
||||
* swagger 会解析的 url 规则
|
||||
*/
|
||||
private List<String> basePath = new ArrayList<>();
|
||||
/**
|
||||
* 在 basePath 基础上需要排除的 url 规则
|
||||
*/
|
||||
private List<String> excludePath = new ArrayList<>();
|
||||
/**
|
||||
* Global operation parameters
|
||||
*/
|
||||
private List<GlobalOperationParameter> globalOperationParameters;
|
||||
/**
|
||||
* 忽略的参数类型
|
||||
*/
|
||||
private List<Class<?>> ignoredParameterTypes = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Contact.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Contact {
|
||||
/**
|
||||
* 联系人
|
||||
*/
|
||||
private String name = "";
|
||||
/**
|
||||
* 联系人 url
|
||||
*/
|
||||
private String url = "";
|
||||
/**
|
||||
* 联系人 email
|
||||
*/
|
||||
private String email = "";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Global response message.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class GlobalResponseMessage {
|
||||
/**
|
||||
* POST 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> post = new ArrayList<>();
|
||||
/**
|
||||
* GET 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> get = new ArrayList<>();
|
||||
/**
|
||||
* PUT 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> put = new ArrayList<>();
|
||||
/**
|
||||
* PATCH 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> patch = new ArrayList<>();
|
||||
/**
|
||||
* DELETE 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> delete = new ArrayList<>();
|
||||
/**
|
||||
* HEAD 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> head = new ArrayList<>();
|
||||
/**
|
||||
* OPTIONS 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> options = new ArrayList<>();
|
||||
/**
|
||||
* TRACE 响应消息体
|
||||
*/
|
||||
List<GlobalResponseMessageBody> trace = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Global response message body.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class GlobalResponseMessageBody {
|
||||
/**
|
||||
* 响应码
|
||||
*/
|
||||
private int code;
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
private String message;
|
||||
/**
|
||||
* 响应体
|
||||
*/
|
||||
private String modelRef;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Ui config.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class UiConfig {
|
||||
/**
|
||||
* Api sorter
|
||||
*/
|
||||
private String apiSorter = "alpha";
|
||||
/**
|
||||
* 是否启用 json 编辑器
|
||||
*/
|
||||
private Boolean jsonEditor = false;
|
||||
/**
|
||||
* 是否显示请求头信息
|
||||
*/
|
||||
private Boolean showRequestHeaders = true;
|
||||
/**
|
||||
* 支持页面提交的请求类型
|
||||
*/
|
||||
private String submitMethods = "get,post,put,delete,patch";
|
||||
/**
|
||||
* 请求超时时间
|
||||
*/
|
||||
private Long requestTimeout = 10000L;
|
||||
/**
|
||||
* Deep linking
|
||||
*/
|
||||
private Boolean deepLinking;
|
||||
/**
|
||||
* Display operation id
|
||||
*/
|
||||
private Boolean displayOperationId;
|
||||
/**
|
||||
* Default models expand depth
|
||||
*/
|
||||
private Integer defaultModelsExpandDepth;
|
||||
/**
|
||||
* Default model expand depth
|
||||
*/
|
||||
private Integer defaultModelExpandDepth;
|
||||
/**
|
||||
* Default model rendering
|
||||
*/
|
||||
private ModelRendering defaultModelRendering;
|
||||
/**
|
||||
* 是否显示请求耗时,默认 false
|
||||
*/
|
||||
private Boolean displayRequestDuration = true;
|
||||
/**
|
||||
* 可选 none | list
|
||||
*/
|
||||
private DocExpansion docExpansion;
|
||||
/**
|
||||
* Boolean=false OR String
|
||||
*/
|
||||
private Object filter;
|
||||
/**
|
||||
* Max displayed tags
|
||||
*/
|
||||
private Integer maxDisplayedTags;
|
||||
/**
|
||||
* Operations sorter
|
||||
*/
|
||||
private OperationsSorter operationsSorter;
|
||||
/**
|
||||
* Show extensions
|
||||
*/
|
||||
private Boolean showExtensions;
|
||||
/**
|
||||
* Tags sorter
|
||||
*/
|
||||
private TagsSorter tagsSorter;
|
||||
/**
|
||||
* Network
|
||||
*/
|
||||
private String validatorUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* securitySchemes 支持方式之一 ApiKey
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Authorization {
|
||||
/**
|
||||
* 鉴权策略 ID,对应 SecurityReferences ID
|
||||
*/
|
||||
private String name = "Authorization";
|
||||
/**
|
||||
* 鉴权策略,可选 ApiKey | BasicAuth | None,默认 ApiKey
|
||||
*/
|
||||
private String type = "BasicAuth";
|
||||
/**
|
||||
* 鉴权传递的 Header 参数
|
||||
*/
|
||||
private String keyName = "X-Client-Token";
|
||||
/**
|
||||
* 需要开启鉴权 URL 的正则
|
||||
*/
|
||||
private String authRegex = "^.*$";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.aivfo.doc.autoconfigure.knife4j.Knife4jAutoConfiguration,\
|
||||
com.aivfo.doc.autoconfigure.swagger.SwaggerDocAutoConfiguration
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>aivfo-doc-spring-boot</artifactId>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>aivfo-doc-spring-boot-core</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-ui</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package com.aivfo.doc.core.common.schema;
|
||||
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
import com.fasterxml.classmate.ResolvedType;
|
||||
import com.fasterxml.classmate.TypeResolver;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import springfox.documentation.schema.Types;
|
||||
import springfox.documentation.service.ResolvedMethodParameter;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.OperationModelsProviderPlugin;
|
||||
import springfox.documentation.spi.service.contexts.RequestMappingContext;
|
||||
import springfox.documentation.swagger.common.SwaggerPluginSupport;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* 包装返回值为 Result
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
|
||||
public class AivfoOperationModelsProviderPlugin implements OperationModelsProviderPlugin {
|
||||
/**
|
||||
* Annotation
|
||||
*/
|
||||
private final Class<? extends Annotation> filterAnnotationClass;
|
||||
/**
|
||||
* Type resolver
|
||||
*/
|
||||
@Resource
|
||||
private TypeResolver typeResolver;
|
||||
|
||||
/**
|
||||
* Fkh operation models provider plugin
|
||||
*
|
||||
* @param filterAnnotationClass filterAnnotationClass
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public AivfoOperationModelsProviderPlugin(Class<? extends Annotation> filterAnnotationClass) {
|
||||
this.filterAnnotationClass = filterAnnotationClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply
|
||||
*
|
||||
* @param context context
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public void apply(RequestMappingContext context) {
|
||||
if (this.findAnnotationIfNotNull(context)) {
|
||||
this.addInputParams(context);
|
||||
this.addReturnType(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find annotation if not null
|
||||
*
|
||||
* @param context context
|
||||
* @return the boolean
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public boolean findAnnotationIfNotNull(RequestMappingContext context) {
|
||||
if (null == this.filterAnnotationClass) {
|
||||
return true;
|
||||
}
|
||||
return context.findAnnotation(filterAnnotationClass).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add return type
|
||||
*
|
||||
* @param context context
|
||||
* @since 1.7.0
|
||||
*/
|
||||
private void addReturnType(RequestMappingContext context) {
|
||||
ResolvedType returnType = context.getReturnType();
|
||||
// 兼容,如果方法返回值本身写的 Result 就不包装了
|
||||
if (Result.class.equals(returnType.getErasedType())) {
|
||||
return;
|
||||
}
|
||||
if (Types.isVoid(returnType)) {
|
||||
returnType = this.typeResolver.resolve(Void.class);
|
||||
}
|
||||
ResolvedType packageType = context.alternateFor(this.typeResolver.resolve(Result.class, returnType));
|
||||
context.operationModelsBuilder().addReturn(packageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add input params
|
||||
*
|
||||
* @param context context
|
||||
* @since 1.7.0
|
||||
*/
|
||||
private void addInputParams(RequestMappingContext context) {
|
||||
for (ResolvedMethodParameter parameter : context.getParameters()) {
|
||||
ResolvedType modelType = context.alternateFor(parameter.getParameterType());
|
||||
context.operationModelsBuilder().addInputParam(modelType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports
|
||||
*
|
||||
* @param delimiter delimiter
|
||||
* @return the boolean
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(DocumentationType delimiter) {
|
||||
return SwaggerPluginSupport.pluginDoesApply(delimiter);
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.aivfo.doc.core.common.schema;
|
||||
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
import com.aivfo.el.starter.base.utils.StringPool;
|
||||
import com.fasterxml.classmate.ResolvedType;
|
||||
import com.fasterxml.classmate.TypeResolver;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import springfox.documentation.builders.ResponseMessageBuilder;
|
||||
import springfox.documentation.schema.*;
|
||||
import springfox.documentation.service.ResponseMessage;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.schema.contexts.ModelContext;
|
||||
import springfox.documentation.spi.service.OperationBuilderPlugin;
|
||||
import springfox.documentation.spi.service.contexts.OperationContext;
|
||||
import springfox.documentation.spring.web.readers.operation.ResponseMessagesReader;
|
||||
import springfox.documentation.swagger.common.SwaggerPluginSupport;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* <p>Description: 最后一位执行,修改200状态码显示的 schema </p>
|
||||
*/
|
||||
@Order
|
||||
public class AivfoOperationResponseMessagePlugin implements OperationBuilderPlugin {
|
||||
/**
|
||||
* Annotation
|
||||
*/
|
||||
private final Class<? extends Annotation> filterAnnotationClass;
|
||||
/**
|
||||
* Name extractor
|
||||
*/
|
||||
@Resource
|
||||
private TypeNameExtractor nameExtractor;
|
||||
/**
|
||||
* Type resolver
|
||||
*/
|
||||
@Resource
|
||||
private TypeResolver typeResolver;
|
||||
|
||||
/**
|
||||
* Fkh operation models provider plugin
|
||||
*
|
||||
* @param filterAnnotationClass filterAnnotationClass
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public AivfoOperationResponseMessagePlugin(Class<? extends Annotation> filterAnnotationClass) {
|
||||
this.filterAnnotationClass = filterAnnotationClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply
|
||||
*
|
||||
* @param operationContext operation context
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public void apply(OperationContext operationContext) {
|
||||
if (this.findAnnotationIfNotNull(operationContext)) {
|
||||
ResolvedType returnType = operationContext.getReturnType();
|
||||
// 兼容,如果方法返回值本身写的 Result 就不包装了
|
||||
if (Result.class.equals(returnType.getErasedType())) {
|
||||
return;
|
||||
}
|
||||
if (Types.isVoid(returnType)) {
|
||||
returnType = this.typeResolver.resolve(Void.class);
|
||||
}
|
||||
ResolvedType packageType = operationContext.alternateFor(this.typeResolver.resolve(Result.class, returnType));
|
||||
ModelContext modelContext = ModelContext.returnValue(StringPool.EMPTY,
|
||||
operationContext.getGroupName(),
|
||||
packageType,
|
||||
Optional.empty(),
|
||||
operationContext.getDocumentationType(),
|
||||
operationContext.getAlternateTypeProvider(),
|
||||
operationContext.getGenericsNamingStrategy(),
|
||||
operationContext.getIgnorableParameterTypes());
|
||||
|
||||
int httpStatusCode = ResponseMessagesReader.httpStatusCode(operationContext);
|
||||
String message = ResponseMessagesReader.message(operationContext);
|
||||
ModelReference modelRef =
|
||||
ResolvedTypes.modelRefFactory(modelContext, new JacksonEnumTypeDeterminer(), this.nameExtractor).apply(packageType);
|
||||
ResponseMessage built = new ResponseMessageBuilder()
|
||||
.code(httpStatusCode)
|
||||
.message(message)
|
||||
.responseModel(modelRef)
|
||||
.build();
|
||||
|
||||
operationContext.operationBuilder().responseMessages(Sets.newHashSet(built));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find annotation if not null
|
||||
*
|
||||
* @param context context
|
||||
* @return the boolean
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public boolean findAnnotationIfNotNull(OperationContext context) {
|
||||
if (null == this.filterAnnotationClass) {
|
||||
return true;
|
||||
}
|
||||
return context.findAnnotation(this.filterAnnotationClass).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports
|
||||
*
|
||||
* @param delimiter delimiter
|
||||
* @return the boolean
|
||||
* @since 1.7.0
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(DocumentationType delimiter) {
|
||||
return SwaggerPluginSupport.pluginDoesApply(delimiter);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.aivfo.doc.core.knife4j;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/***
|
||||
* Enable Knife4j enhanced annotation and use @EnableSwagger2 annotation together.
|
||||
*
|
||||
* inlude:
|
||||
* <ul>
|
||||
* <li>Interface sorting </li>
|
||||
* <li>Interface document download (word)</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.TYPE})
|
||||
@Documented
|
||||
public @interface EnableKnife4j {
|
||||
}
|
||||
@@ -15,5 +15,20 @@
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.aivfo</groupId>
|
||||
<artifactId>aivfo-doc-spring-boot-autoconfigure</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.aivfo.doc;
|
||||
|
||||
import com.aivfo.doc.core.knife4j.EnableKnife4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.4.0
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.05.08 16:16
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@EnableKnife4j
|
||||
@SpringBootApplication
|
||||
public class SampleKnife4jApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleKnife4jApplication.class, args);
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.aivfo.doc;
|
||||
|
||||
import com.aivfo.el.start.core.api.R;
|
||||
import com.aivfo.el.start.core.exception.BaseException;
|
||||
import com.aivfo.el.starter.base.Result;
|
||||
import io.swagger.annotations.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司</p>
|
||||
* <p>Description: </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.0.0
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.01.27 18:19
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Api(tags = "swagger test api")
|
||||
@RestController
|
||||
public class TestController {
|
||||
/**
|
||||
* Find by id result.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the result
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@GetMapping("/user/{id}")
|
||||
@ApiOperation(value = "获取用户详情", notes = "xxx", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ApiResponses( {
|
||||
@ApiResponse(code = 2000, message = "操作成功", response = R.class),
|
||||
@ApiResponse(code = 6000, message = "参数校验失败", response = R.class),
|
||||
@ApiResponse(code = 4000, message = "用户不存在", response = R.class)
|
||||
})
|
||||
public Result<Void> findById(@Valid @NotNull @Size(min = 1) @PathVariable("id") Long id) {
|
||||
return R.succeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save result.
|
||||
*
|
||||
* @param user the user
|
||||
* @return the result
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@PostMapping("/user")
|
||||
@ApiOperation(value = "创建新用户", notes = "传入一个 User 实体", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Result<Void> save(@Valid @RequestBody User user) {
|
||||
return R.succeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update result.
|
||||
*
|
||||
* @param user the user
|
||||
* @return the result
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@PutMapping("/user")
|
||||
@ApiOperation(value = "修改用户信息", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Result<String> update(@Valid @RequestBody User user) {
|
||||
return R.succeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete by id result.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the result
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@DeleteMapping("/user/{id}")
|
||||
@ApiOperation(value = "删除用户", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Result<String> deleteById(@Valid @NotNull @Size(min = 1) @PathVariable("id") Long id) {
|
||||
if(id>10L)
|
||||
{
|
||||
throw new BaseException("123");
|
||||
}
|
||||
return R.succeed("delete user : " + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* List.
|
||||
*
|
||||
* @param pageIndex the page index
|
||||
* @param pageSize the page size
|
||||
* @return the list
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@GetMapping("/user")
|
||||
@ApiOperation(value = "用户列表", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Result<Void> list(@ApiParam("查看第几页") @RequestParam(value = "pageIndex", required = false) Integer pageIndex,
|
||||
@ApiParam("每页多少条") @RequestParam(value = "pageSize", required = false) Integer pageSize) {
|
||||
return R.succeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Company: 成都返空汇网络技术有限公司 </p>
|
||||
* <p>Description: </p>
|
||||
*
|
||||
* @author dong4j
|
||||
* @version 1.4.0
|
||||
* @email "mailto:dongshijie@fkhwl.com"
|
||||
* @date 2020.05.08 17:27
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
private static class User {
|
||||
/** Username */
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
/** Version */
|
||||
private String version;
|
||||
/** Date */
|
||||
private Date date;
|
||||
/** Age */
|
||||
@Max(150)
|
||||
@Min(1)
|
||||
private Integer age;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
aivfo:
|
||||
doc:
|
||||
swagger:
|
||||
title: swagger-plus test
|
||||
description: 描述信息
|
||||
version: 2022.1.1-SNAPSHOT
|
||||
license: xxxx
|
||||
license-url: xxxxx
|
||||
base-package: com.aivfo.doc
|
||||
@@ -14,5 +14,6 @@
|
||||
<modules>
|
||||
<module>aivfo-doc-spring-boot-autoconfigure</module>
|
||||
<module>aivfo-doc-spring-boot-start</module>
|
||||
<module>aivfo-doc-spring-boot-core</module>
|
||||
</modules>
|
||||
</project>
|
||||
Reference in New Issue
Block a user