From 000018826ade94a53fc9fa296b7c6a5c33ad4f46 Mon Sep 17 00:00:00 2001
From: jerrywei <2073825933@qq.com>
Date: Tue, 10 Sep 2024 02:18:12 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91SWAGGER?=
=?UTF-8?q?:=20=E4=BF=AE=E5=A4=8Dswagger=E6=96=87=E6=A1=A3A=E5=B1=9E?=
=?UTF-8?q?=E6=80=A7=E5=BC=95=E7=94=A8B=E5=B1=9E=E6=80=A7=E6=97=B6=20A?=
=?UTF-8?q?=E5=B1=9E=E6=80=A7=E4=B8=AD=E5=AE=9A=E4=B9=89B=E5=AD=97?=
=?UTF-8?q?=E6=AE=B5=E4=B8=8A=E7=9A=84=20@Schema=20=E6=B3=A8=E8=A7=A3?=
=?UTF-8?q?=E4=B8=8D=E7=94=9F=E6=95=88=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../config/YudaoSwaggerAutoConfiguration.java | 9 ++
.../SchemaPropertyFixModelConverter.java | 96 +++++++++++++++++++
.../src/main/resources/application.yaml | 1 +
3 files changed, 106 insertions(+)
create mode 100644 yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
index a131d1f2d8..5b7456961f 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.swagger.config;
+import cn.iocoder.yudao.framework.swagger.core.converter.SchemaPropertyFixModelConverter;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
@@ -100,6 +101,14 @@ public class YudaoSwaggerAutoConfiguration {
propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
}
+ /**
+ * 修复swagger文档A属性引用B属性时 A属性中定义B字段上的 @Schema 注解不生效问题
+ */
+ @Bean
+ public SchemaPropertyFixModelConverter schemaPropertyFixModelConverter(){
+ return new SchemaPropertyFixModelConverter();
+ }
+
// ========== 分组 OpenAPI 配置 ==========
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java
new file mode 100644
index 0000000000..ea7ba4fa81
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java
@@ -0,0 +1,96 @@
+package cn.iocoder.yudao.framework.swagger.core.converter;
+
+import cn.hutool.core.util.StrUtil;
+import io.swagger.v3.core.converter.AnnotatedType;
+import io.swagger.v3.core.converter.ModelConverter;
+import io.swagger.v3.core.converter.ModelConverterContext;
+import io.swagger.v3.core.util.RefUtils;
+import io.swagger.v3.oas.models.media.Schema;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ *
+ * swagger3的规范?
+ * 如果属性中的字段是引用另一个类时, 那么底层会被包装成只有$ref的Schema, 导致属性上的@Schema注解信息直接丢失了
+ * 那么就会导致前端不显示属性上的注解信息了, 这里手动将信息和$ref字段放在一起, 保证前端可以正常显现引用属性上@Schema注解的信息
+ * 且 springdoc.api-docs.version 需配置 3.1.0 版本, 3.0.1会在序列化时判断$ref有值则直接过滤除了$ref字段以外的信息
+ *
+ *
+ * @author jerryskyr
+ */
+public class SchemaPropertyFixModelConverter implements ModelConverter {
+
+
+
+ /**
+ * Instantiates a new Polymorphic model converter.
+ *
+ */
+ public SchemaPropertyFixModelConverter() {
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) {
+ Schema> resolvedSchema = (chain.hasNext()) ? chain.next().resolve(type, context, chain) : null;
+ if (resolvedSchema == null || resolvedSchema.get$ref() == null) {
+ return resolvedSchema;
+ }
+ return supplementarySchema(type, resolvedSchema, context.getDefinedModels().values());
+ }
+
+ /**
+ * 补充 schema 信息.
+ *
+ * @param type the type
+ * @param schema the schema
+ * @param schemas the schemas
+ * @return the schema
+ */
+ @SuppressWarnings("rawtypes")
+ private Schema supplementarySchema(AnnotatedType type, Schema schema, Collection schemas) {
+ // 拿到真实的类型定义
+ Optional schemaDefine = schemas.stream()
+ .filter(s -> schema.get$ref().equals(RefUtils.constructRef(s.getName())))
+ .findAny();
+ if (!schemaDefine.isPresent()) {
+ return schema;
+ }
+
+ // 拿到真实定义下的类型下的属性定义
+ Map properties = schemaDefine.get().getProperties();
+ if (properties == null) {
+ return schema;
+ }
+
+ for (Object propertie : properties.values()) {
+ if (propertie instanceof Schema) {
+ // 拿到属性的真实定义
+ Schema propertieSchema = (Schema) propertie;
+
+ String propertieRef = propertieSchema.get$ref();
+ // 如果是引用类型, 则将真实定义的属性, 赋值到引用定义中
+ if (propertieRef != null) {
+ schemas.stream()
+ .filter(s -> propertieRef.equals(RefUtils.constructRef(s.getName())))
+ .findAny()
+ .ifPresent(propertieSchemaDefine -> {
+ if (StrUtil.isEmpty(propertieSchema.getTitle())) {
+ propertieSchema.setTitle(propertieSchemaDefine.getTitle());
+ }
+ if (StrUtil.isEmpty(propertieSchema.getDescription())) {
+ propertieSchema.setDescription(propertieSchemaDefine.getDescription());
+ }
+ });
+ }
+
+ }
+ }
+
+ return schema;
+ }
+}
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index baf68657e0..0d38db8fbc 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -40,6 +40,7 @@ server:
springdoc:
api-docs:
+ version: OPENAPI_3_1
enabled: true
path: /v3/api-docs
swagger-ui:
From 16328e64c8f4d19001674903a2c29c614cd58925 Mon Sep 17 00:00:00 2001
From: jerrywei <2073825933@qq.com>
Date: Wed, 11 Sep 2024 23:35:26 +0800
Subject: [PATCH 2/2] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91SWAGGER?=
=?UTF-8?q?:=20=E4=BF=AE=E5=A4=8Dswagger=E6=96=87=E6=A1=A3A=E5=B1=9E?=
=?UTF-8?q?=E6=80=A7=E5=BC=95=E7=94=A8B=E5=B1=9E=E6=80=A7=E6=97=B6=20A?=
=?UTF-8?q?=E5=B1=9E=E6=80=A7=E4=B8=AD=E5=AE=9A=E4=B9=89B=E5=AD=97?=
=?UTF-8?q?=E6=AE=B5=E4=B8=8A=E7=9A=84=20@Schema=20=E6=B3=A8=E8=A7=A3?=
=?UTF-8?q?=E4=B8=8D=E7=94=9F=E6=95=88=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../config/YudaoSwaggerAutoConfiguration.java | 5 +-
.../SchemaPropertyFixModelConverter.java | 94 +++++++++++++++----
2 files changed, 80 insertions(+), 19 deletions(-)
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
index 5b7456961f..994ce93555 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
@@ -15,6 +15,7 @@ import org.springdoc.core.*;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.providers.JavadocProvider;
+import org.springdoc.core.providers.ObjectMapperProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -105,8 +106,8 @@ public class YudaoSwaggerAutoConfiguration {
* 修复swagger文档A属性引用B属性时 A属性中定义B字段上的 @Schema 注解不生效问题
*/
@Bean
- public SchemaPropertyFixModelConverter schemaPropertyFixModelConverter(){
- return new SchemaPropertyFixModelConverter();
+ public SchemaPropertyFixModelConverter schemaPropertyFixModelConverter(ObjectMapperProvider springDocObjectMapper){
+ return new SchemaPropertyFixModelConverter(springDocObjectMapper);
}
// ========== 分组 OpenAPI 配置 ==========
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java
index ea7ba4fa81..18410adb65 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/core/converter/SchemaPropertyFixModelConverter.java
@@ -1,16 +1,22 @@
package cn.iocoder.yudao.framework.swagger.core.converter;
import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
+import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.core.util.RefUtils;
import io.swagger.v3.oas.models.media.Schema;
+import org.apache.commons.lang3.StringUtils;
+import org.springdoc.core.providers.ObjectMapperProvider;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Optional;
+import java.lang.annotation.Annotation;
+import java.util.*;
/**
*
@@ -24,13 +30,18 @@ import java.util.Optional;
*/
public class SchemaPropertyFixModelConverter implements ModelConverter {
+ /**
+ * The Spring doc object mapper.
+ */
+ private final ObjectMapperProvider springDocObjectMapper;
/**
* Instantiates a new Polymorphic model converter.
*
*/
- public SchemaPropertyFixModelConverter() {
+ public SchemaPropertyFixModelConverter(ObjectMapperProvider springDocObjectMapper) {
+ this.springDocObjectMapper = springDocObjectMapper;
}
@Override
@@ -51,7 +62,7 @@ public class SchemaPropertyFixModelConverter implements ModelConverter {
* @param schemas the schemas
* @return the schema
*/
- @SuppressWarnings("rawtypes")
+ @SuppressWarnings("all")
private Schema supplementarySchema(AnnotatedType type, Schema schema, Collection schemas) {
// 拿到真实的类型定义
Optional schemaDefine = schemas.stream()
@@ -67,6 +78,56 @@ public class SchemaPropertyFixModelConverter implements ModelConverter {
return schema;
}
+ // 获取类型
+ ObjectMapper _mapper = springDocObjectMapper.jsonMapper();
+ final JavaType javaType;
+ if (type.getType() instanceof JavaType) {
+ javaType = (JavaType) type.getType();
+ } else {
+ javaType = _mapper.constructType(type.getType());
+ }
+
+ // 解析类属性描述
+ final BeanDescription beanDesc;
+ {
+ BeanDescription recurBeanDesc = _mapper.getSerializationConfig().introspect(javaType);
+
+ HashSet visited = new HashSet<>();
+ JsonSerialize jsonSerialize = recurBeanDesc.getClassAnnotations().get(JsonSerialize.class);
+ while (jsonSerialize != null && !Void.class.equals(jsonSerialize.as())) {
+ String asName = jsonSerialize.as().getName();
+ if (visited.contains(asName)) break;
+ visited.add(asName);
+
+ recurBeanDesc = _mapper.getSerializationConfig().introspect(
+ _mapper.constructType(jsonSerialize.as())
+ );
+ jsonSerialize = recurBeanDesc.getClassAnnotations().get(JsonSerialize.class);
+ }
+ beanDesc = recurBeanDesc;
+ }
+
+ // 解析属性上的注解
+ List propertiesDefine = beanDesc.findProperties();
+ Map defineMap = new HashMap<>();
+ for (BeanPropertyDefinition propDef : propertiesDefine) {
+ List annotationList = new ArrayList<>();
+ for (Annotation a : propDef.getPrimaryMember().annotations()) {
+ annotationList.add(a);
+ }
+ Annotation[] annotations = annotationList.toArray(new Annotation[annotationList.size()]);
+ io.swagger.v3.oas.annotations.media.Schema ctxSchema = AnnotationsUtils.getSchemaAnnotation(annotations);
+
+ String propSchemaName = propDef.getName();
+ if (AnnotationsUtils.hasSchemaAnnotation(ctxSchema)) {
+ if (!StringUtils.isBlank(ctxSchema.name())) {
+ propSchemaName = ctxSchema.name();
+ }
+ }
+ defineMap.put(propSchemaName, ctxSchema);
+ }
+
+ // 重新补充属性描述
for (Object propertie : properties.values()) {
if (propertie instanceof Schema) {
// 拿到属性的真实定义
@@ -75,17 +136,16 @@ public class SchemaPropertyFixModelConverter implements ModelConverter {
String propertieRef = propertieSchema.get$ref();
// 如果是引用类型, 则将真实定义的属性, 赋值到引用定义中
if (propertieRef != null) {
- schemas.stream()
- .filter(s -> propertieRef.equals(RefUtils.constructRef(s.getName())))
- .findAny()
- .ifPresent(propertieSchemaDefine -> {
- if (StrUtil.isEmpty(propertieSchema.getTitle())) {
- propertieSchema.setTitle(propertieSchemaDefine.getTitle());
- }
- if (StrUtil.isEmpty(propertieSchema.getDescription())) {
- propertieSchema.setDescription(propertieSchemaDefine.getDescription());
- }
- });
+ io.swagger.v3.oas.annotations.media.Schema propertieSchemaDefine = defineMap.get(propertieSchema.getName());
+
+ if (AnnotationsUtils.hasSchemaAnnotation(propertieSchemaDefine)) {
+ if (!StrUtil.isBlank(propertieSchemaDefine.title())) {
+ propertieSchema.setTitle(propertieSchemaDefine.title());
+ }
+ if (!StrUtil.isBlank(propertieSchemaDefine.description())) {
+ propertieSchema.setDescription(propertieSchemaDefine.description());
+ }
+ }
}
}