All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pighand.framework.spring.api.springdoc.SpringDocOpenAPI Maven / Gradle / Ivy

package com.pighand.framework.spring.api.springdoc;

import com.pighand.framework.spring.api.springdoc.analysis.AnalysisSchema;
import com.pighand.framework.spring.api.springdoc.analysis.info.DocInfo;
import com.pighand.framework.spring.api.springdoc.analysis.info.FieldInfo;
import com.pighand.framework.spring.api.springdoc.analysis.info.MethodInfo;
import com.pighand.framework.spring.api.springdoc.analysis.info.SpringDocInfo;
import com.pighand.framework.spring.api.springdoc.pageParams.AddPageParams;
import com.pighand.framework.spring.api.springdoc.utils.DocFieldGroupUrl;

import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponses;

import org.springframework.util.StringUtils;

import java.lang.reflect.Type;
import java.util.*;

/**
 * 处理springdoc文档,根据group生成新的schema,重写文档
 *
 * @author wangshuli
 */
public class SpringDocOpenAPI {

    private DocInfo docInfo = SpringDocInfo.docInfo;
    private Map refMapping = SpringDocInfo.refMapping;

    private OpenAPI openApi;

    public SpringDocOpenAPI(OpenAPI openApi) {
        this.openApi = openApi;

        // 添加分页查询参数
        new AddPageParams(openApi);

        // 根据group处理文档
        if (refMapping.size() == 0) {
            return;
        }

        openApi.getPaths()
                .forEach(
                        (url, pathItem) -> {
                            analysisController("POST", url, pathItem.getPost());
                            analysisController("PUT", url, pathItem.getPut());
                            analysisController("DELETE", url, pathItem.getDelete());
                            analysisController("GET", url, pathItem.getGet());
                            analysisController("PATCH", url, pathItem.getPatch());
                        });
    }

    /**
     * 重写文档
     *
     * 

根据方法的fieldGroup,查找request、response中是否设置对应的group字段 * *

如果没设置,返回原文档 * *

如果设置了,生成新的schema,覆盖文档 * * @param method * @param url * @param operation */ private void analysisController(String method, String url, Operation operation) { String apiUrl = DocFieldGroupUrl.url(method, url); MethodInfo methodInfo = docInfo.getUrl2MethodMapping().get(apiUrl); if (null == methodInfo) { return; } // 重写request body this.rewriteRequestBody(operation, methodInfo); // 重写request params this.rewriteRequestParams(operation, methodInfo); // 重写response this.rewriteResponses(operation, methodInfo); } /** * 重写request body * * @param operation * @param methodInfo */ private void rewriteRequestBody(Operation operation, MethodInfo methodInfo) { Content requestBody = Optional.ofNullable(operation.getRequestBody()) .map(RequestBody::getContent) .orElse(new Content()); requestBody.forEach( (contentType, item) -> { Schema oldSchema = item.getSchema(); if (null == oldSchema) { return; } String refName = oldSchema.get$ref(); Schema schema = requestSchema( methodInfo.getMethodFieldGroupNames(), methodInfo.getValidationGroupNames(), refName); if (schema != null) { oldSchema.set$ref(schema.getName()); item.schema(oldSchema); } }); } /** * 重写request params * * @param operation * @param methodInfo */ private void rewriteRequestParams(Operation operation, MethodInfo methodInfo) { List parameters = Optional.ofNullable(operation.getParameters()).orElse(new ArrayList(0)); // request params List requestParameters = new ArrayList<>(parameters.size()); // request schema to params List schemaParameters = new ArrayList<>(parameters.size()); parameters.forEach( parameter -> { Schema oldSchema = parameter.getSchema(); if (null == oldSchema) { requestParameters.add(parameter); return; } String refName = oldSchema.get$ref(); Schema schema = requestSchema( methodInfo.getMethodFieldGroupNames(), methodInfo.getValidationGroupNames(), refName); if (schema != null) { // 更新新的schema oldSchema.set$ref(schema.getName()); parameter.setSchema(oldSchema); // 根据schema生成params schemaParameters.addAll(schema2Parameters(schema)); } }); // 如果存在schema。将schema转为params if (schemaParameters.size() > 0) { schemaParameters.addAll(requestParameters); operation.setParameters(schemaParameters); } } /** * 根据schema生成parameters * * @param schema * @return */ private List schema2Parameters(Schema schema) { Map properties = schema.getProperties(); List parameter = new ArrayList<>(properties.size()); properties .keySet() .forEach( key -> { Schema temSchema = properties.get(key); Parameter newParameter = new Parameter(); newParameter.in("query"); newParameter.name(key); newParameter.description(temSchema.getDescription()); newParameter.required( schema.getRequired() != null && schema.getRequired().contains(key)); newParameter.example(temSchema.getExample()); newParameter.schema(temSchema); parameter.add(newParameter); }); return parameter; } /** * 重写response * * @param operation * @param methodInfo */ private void rewriteResponses(Operation operation, MethodInfo methodInfo) { ApiResponses apiResponses = Optional.ofNullable(operation.getResponses()).orElse(new ApiResponses()); apiResponses.forEach( (key, value) -> { Content responseContent = Optional.ofNullable(value.getContent()).orElse(new Content()); responseContent.forEach( (contentType, item) -> { Schema schema = responseSchema( methodInfo.getMethodFieldGroupNames(), methodInfo.getReturnType()); if (schema != null) { Schema oldSchema = item.getSchema(); oldSchema.set$ref(schema.getName()); item.setSchema(oldSchema); } }); }); } /** * 根据bean class,生成新的schema * * @param type * @return */ private ResolvedSchema createSchema(Type type) { return ModelConverters.getInstance() .resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(false)); } /** * 处理request schema * * @param methodGroupNames * @param validationGroupNames * @param refName * @return 返回设置后的schema;没有对应的group,返回null */ private Schema requestSchema( Set methodGroupNames, Set validationGroupNames, String refName) { if (!StringUtils.hasText(refName)) { return null; } try { String refClassName = refMapping.get(refName); Class clz = Class.forName(refClassName); ResolvedSchema resolvedSchema = createSchema(clz); return this.formatSchema( "request", methodGroupNames, validationGroupNames, resolvedSchema); } catch (Exception e) { return null; } } /** * 处理response schema * * @param methodGroupNames * @param returnType * @return 返回设置后的schema;没有对应的group,返回null */ private Schema responseSchema(Set methodGroupNames, Type returnType) { ResolvedSchema resolvedSchema = createSchema(returnType); return this.formatSchema("response", methodGroupNames, null, resolvedSchema); } /** * 根据group信息,格式化schema * * @param type request、response * @param methodGroupNames * @param validationGroupNames * @param resolvedSchema new schema * @return 返回设置后的schema;没有对应的group,返回null */ private Schema formatSchema( String type, Set methodGroupNames, Set validationGroupNames, ResolvedSchema resolvedSchema) { // 解析的bean对应的schema Schema mainSchema = resolvedSchema.schema; String mainSchemaName = mainSchema.getName(); // bean中字段是其他bean,会生成字段bean对应的schema Map newSchemas = resolvedSchema.referencedSchemas; if (newSchemas == null || newSchemas.size() == 0) { return mainSchema; } for (String schemaName : newSchemas.keySet()) { Schema schema = newSchemas.get(schemaName); String schemaClassName = refMapping.get(schemaName); // 当前bean适配@NotNull的字段 Set notNullFields = this.setSchemaNotNullRequired(schema, schemaClassName, validationGroupNames); // 格式化schema字段信息 Set formatGroupNames = this.formatSchemaField( schema, schemaClassName, type, methodGroupNames, notNullFields); // 跳过:无任何格式化 if (notNullFields == null && formatGroupNames.size() == 0) { continue; } if (formatGroupNames.size() == 0) { formatGroupNames.add("@NotNull"); } // 将新schema添加到文档中,并重命名 schema.setName( type + " ->" + schema.getName() + " -> " + String.join(" ,", formatGroupNames)); openApi.schema(schema.getName(), schema); if (mainSchemaName.equals(schemaClassName)) { mainSchema = schema; } } // 将字段schema合并至主schema return this.mergeMainSchema(mainSchema, newSchemas); } /** * bean中字段是其他bean,会生成字段bean对应的schema。将字段schema合并至主schema * *

从主schema开始读取properties,如果property是schema,从newSchemas取格式化后的schema,回写到properties中。 * * @param mainSchema * @param newSchemas 格式化后的所有schema * @return */ private Schema mergeMainSchema(Schema mainSchema, Map newSchemas) { if (mainSchema == null) { return null; } Map properties = mainSchema.getProperties(); properties.forEach( (propertyName, propertySchema) -> { String propertySchemaRef = propertySchema.get$ref(); // 解析list类型字段: List Schema propertyItems = propertySchema.getItems(); if (!StringUtils.hasText(propertySchemaRef) && propertyItems != null) { propertySchemaRef = propertyItems.get$ref(); } if (StringUtils.hasText(propertySchemaRef)) { String schemaBeanName = AnalysisSchema.refName2BeanName(propertySchemaRef); Schema propertyNewSchema = newSchemas.get(schemaBeanName); if (propertyNewSchema != null && !"json".equals(propertyNewSchema.getFormat())) { propertyNewSchema = this.mergeMainSchema(propertyNewSchema, newSchemas); properties.put(propertyName, propertyNewSchema); } } }); mainSchema.setProperties(properties); return mainSchema; } /** * 设置schema对类中,带有@NotNull注解的字段(包含没有group和group与当前方法一致) * * @param schema * @param schemaClassName * @param validationGroupNames * @return null:类中不带任何@NotNull注解 */ private Set setSchemaNotNullRequired( Schema schema, String schemaClassName, Set validationGroupNames) { Map> notNullGroups = this.docInfo.getClass2NotNullMapping().get(schemaClassName); if (notNullGroups != null && notNullGroups.size() > 0) { Set requiredFieldNames = new HashSet<>(); // 不带group的@NotNull Set allFields = notNullGroups.get(DocInfo.NOT_NULL_GROUP_ALL); if (allFields != null) { requiredFieldNames.addAll(allFields); } // 当前方法group对应@NotNull的group if (validationGroupNames != null) { validationGroupNames.stream() .forEach( validationGroupName -> { Set groupRequiredFieldNames = notNullGroups.get(validationGroupName); if (groupRequiredFieldNames != null) { requiredFieldNames.addAll(groupRequiredFieldNames); } }); } // @NotNull与schema中自带required一致,则不处理 List schemaRequired = schema.getRequired(); if (schemaRequired != null && schemaRequired.equals(requiredFieldNames)) { return null; } schema.setRequired(requiredFieldNames.stream().toList()); return requiredFieldNames; } return null; } /** * 格式化schema字段 * * @param schema * @param schemaClassName * @param type * @param methodGroupNames * @param notNullFields * @return 格式化的field group name。null:未格式化任何信息 */ private Set formatSchemaField( Schema schema, String schemaClassName, String type, Set methodGroupNames, Set notNullFields) { Set formatGroupNames = new HashSet<>(); // 无group的字段 FieldInfo noGroupFieldInfo = Optional.ofNullable(this.docInfo.getClass2FieldMapping().get(schemaClassName)) .orElse(new HashMap<>(0)) .get(DocInfo.NOT_NULL_GROUP_ALL); // schema对应的group字段信息 List groupFieldInfos = this.getGroupFieldInfos(schemaClassName, methodGroupNames); groupFieldInfos.forEach( fieldInfo -> { Set fields = type.equals("request") ? fieldInfo.getRequestFields() : fieldInfo.getResponseFields(); Set requiredFields = type.equals("request") ? fieldInfo.getRequestRequiredFields() : fieldInfo.getResponseRequiredFields(); Set exceptionFields = type.equals("request") ? fieldInfo.getRequestExceptionFields() : fieldInfo.getResponseExceptionFields(); Set noGroupFields = type.equals("request") ? noGroupFieldInfo.getRequestFields() : noGroupFieldInfo.getResponseFields(); Set noGroupRequiredFields = type.equals("request") ? noGroupFieldInfo.getRequestRequiredFields() : noGroupFieldInfo.getResponseRequiredFields(); Set noGroupExceptionFields = type.equals("request") ? noGroupFieldInfo.getRequestExceptionFields() : noGroupFieldInfo.getResponseExceptionFields(); // 根据group,设置schema this.setSchemaFiled( schema, fields, requiredFields, exceptionFields, noGroupFields, noGroupRequiredFields, noGroupExceptionFields, notNullFields); formatGroupNames.add(fieldInfo.getFileGroupName()); }); return formatGroupNames; } /** * 根据group对应的字段,设置schema字段信息 * * @param schema * @param fields 只显示的字段:设置了@Field(group="XXX") * @param requiredFields 必填的字段:设置了@Field(group="XXX", required=true) * @param exceptionFields 不显示的字段: 设置了@FieldException(group="XXX") * @param noGroupFields 全局都可以显示的字段:设置了@Field * @param noGroupRequiredFields 全局必填的字段:设置了@Field(required=true) * @param noGroupExceptionFields 全局不显示的字段: 设置了@FieldException * @param notNullFields notNull的必填字段 */ private void setSchemaFiled( Schema schema, Set fields, Set requiredFields, Set exceptionFields, Set noGroupFields, Set noGroupRequiredFields, Set noGroupExceptionFields, Set notNullFields) { // 没有对应的group,使用原文档schema if (fields.isEmpty() && requiredFields.isEmpty() && exceptionFields.isEmpty() && noGroupFields.isEmpty() && noGroupRequiredFields.isEmpty() && noGroupExceptionFields.isEmpty() && notNullFields.isEmpty()) { return; } Iterator iterator = schema.getProperties().keySet().iterator(); // 移除字段 while (iterator.hasNext()) { String key = iterator.next(); // @notNull。如果设置了@notNull或@notNull(group=当前方法的group),设置成必填项 if (notNullFields != null && notNullFields.size() > 0) { boolean isNotNull = notNullFields.contains(key); if (isNotNull) { requiredFields.add(key); } } // 是否删除字段 = 标明不显示的字段 or 标明全局不显示的字段 or 有只显示的字段,且不在只显示的字段中,也不在全局可现实的字段中 boolean isRemove = exceptionFields.contains(key) || noGroupExceptionFields.contains(key) || (fields.size() > 0 && !fields.contains(key) && !noGroupFields.contains(key)); if (isRemove) { iterator.remove(); requiredFields.remove(key); } } // 设置必填字段 schema.setRequired(requiredFields.stream().toList()); } /** * 获取schema对应的group字段信息 * * @param refClassName schema对应的bean class name * @param methodGroupNames 接口的groupNames * @return */ private List getGroupFieldInfos(String refClassName, Set methodGroupNames) { // {fileGroupName, FieldInfo} Map fileGroupNames = Optional.ofNullable(this.docInfo.getClass2FieldMapping().get(refClassName)) .orElse(new HashMap<>(0)); // 使用交集,取出group对应的字段信息 Set resSet = new HashSet<>(); resSet.addAll(methodGroupNames); resSet.retainAll(fileGroupNames.keySet()); List FieldInfos = new ArrayList<>(resSet.size()); resSet.forEach(fileGroupName -> FieldInfos.add(fileGroupNames.get(fileGroupName))); return FieldInfos; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy