From 635d762aef37b5de6cd2e34f4a076ab56d9a239d Mon Sep 17 00:00:00 2001 From: feiyu02 <risaku@163.com> Date: 星期五, 11 四月 2025 17:35:11 +0800 Subject: [PATCH] 1. 添加自动输出接口API文档功能 --- src/main/kotlin/com/flightfeather/uav/common/api2word/utils/MenuUtils.java | 25 src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ModelAttrUtils.java | 72 ++ src/main/kotlin/com/flightfeather/uav/lightshare/web/OpenApiWordController.java | 106 +++ src/main/kotlin/com/flightfeather/uav/common/api2word/JavaConfig.java | 47 + src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt | 1 src/main/kotlin/com/flightfeather/uav/common/api2word/model/Response.java | 48 + src/main/kotlin/com/flightfeather/uav/lightshare/service/OpenApiWordService.java | 18 src/main/kotlin/com/flightfeather/uav/common/api2word/model/ModelAttr.java | 107 +++ pom.xml | 22 src/main/kotlin/com/flightfeather/uav/common/api2word/utils/RequestUtils.java | 29 src/main/kotlin/com/flightfeather/uav/common/api2word/model/Request.java | 87 ++ src/main/resources/templates/word.html | 212 ++++++ src/main/kotlin/com/flightfeather/uav/common/api2word/model/Table.java | 163 ++++ src/main/kotlin/com/flightfeather/uav/config/Swagger2Configuration.kt | 12 src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt | 1 src/main/kotlin/com/flightfeather/uav/common/api2word/utils/JsonUtils.java | 66 ++ src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ResponseUtils.java | 44 + src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/OpenApiWordServiceImpl.java | 884 ++++++++++++++++++++++++++ src/main/resources/application.yml | 8 19 files changed, 1,945 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index b8e37db..5655f62 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ <properties> <java.version>1.8</java.version> - <kotlin.version>1.5.0</kotlin.version> + <kotlin.version>2.1.0</kotlin.version> <itext.version>7.2.2</itext.version> <!-- tk.mybatis --> <mapper.plugin>tk.mybatis.mapper.generator.MapperPlugin</mapper.plugin> @@ -35,6 +35,10 @@ <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> @@ -165,6 +169,17 @@ <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency> + <dependency> + <groupId>org.springframework.plugin</groupId> + <artifactId>spring-plugin-core</artifactId> + <version>2.0.0.RELEASE</version> + </dependency> +<!-- <dependency>--> +<!-- <groupId>org.springframework.plugin</groupId>--> +<!-- <artifactId>spring-plugin-metadata</artifactId>--> +<!-- <version>2.0.0.RELEASE</version>--> +<!-- </dependency>--> + <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker --> <dependency> @@ -247,6 +262,11 @@ <artifactId>axis2-kernel</artifactId> <version>1.8.2</version> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.17.0</version> + </dependency> </dependencies> diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/JavaConfig.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/JavaConfig.java new file mode 100644 index 0000000..3586ec6 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/JavaConfig.java @@ -0,0 +1,47 @@ +package com.flightfeather.uav.common.api2word; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import javax.net.ssl.SSLContext; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; + +/** + * Created by XiuYin.Cui on 2018/6/21. + */ +@Configuration +public class JavaConfig { + + @Bean + public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; + SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() + .loadTrustMaterial(null, acceptingTrustStrategy) + .build(); + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(csf) + .build(); + HttpComponentsClientHttpRequestFactory requestFactory = + new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(httpClient); + + //60s + requestFactory.setConnectTimeout(60 * 1000); + requestFactory.setReadTimeout(60 * 1000); + RestTemplate restTemplate = new RestTemplate(requestFactory); + restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); + return restTemplate; + } +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/model/ModelAttr.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/ModelAttr.java new file mode 100644 index 0000000..38958e2 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/ModelAttr.java @@ -0,0 +1,107 @@ +package com.flightfeather.uav.common.api2word.model; + +import com.github.jknack.handlebars.internal.lang3.StringUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 杩斿洖灞炴�� + * + * @author kevin + */ +public class ModelAttr implements Serializable { + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getRequire() { + return require; + } + + public void setRequire(Boolean require) { + this.require = require; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List<ModelAttr> getProperties() { + return properties; + } + + public void setProperties(List<ModelAttr> properties) { + this.properties = properties; + } + + public boolean isCompleted() { + return isCompleted; + } + + public void setCompleted(boolean isCompleted) { + this.isCompleted = isCompleted; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + private static final long serialVersionUID = -4074067438450613643L; + + /** + * 绫诲悕 + */ + private String className = StringUtils.EMPTY; + /** + * 灞炴�у悕 + */ + private String name = StringUtils.EMPTY; + /** + * 绫诲瀷 + */ + private String type = StringUtils.EMPTY; + /** + * 鏄惁蹇呭~ + */ + private Boolean require = false; + /** + * 灞炴�ф弿杩� + */ + private String description; + /** + * 宓屽灞炴�у垪琛� + */ + private List<ModelAttr> properties = new ArrayList<>(); + + /** + * 鏄惁鍔犺浇瀹屾垚锛岄伩鍏嶅惊鐜紩鐢� + */ + private boolean isCompleted = false; +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Request.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Request.java new file mode 100644 index 0000000..44b4494 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Request.java @@ -0,0 +1,87 @@ +package com.flightfeather.uav.common.api2word.model; + +import java.io.Serializable; + +/** + * Created by XiuYin.Cui on 2018/1/11. + */ +public class Request implements Serializable{ + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getParamType() { + return paramType; + } + + public void setParamType(String paramType) { + this.paramType = paramType; + } + + public Boolean getRequire() { + return require; + } + + public void setRequire(Boolean require) { + this.require = require; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public ModelAttr getModelAttr() { + return modelAttr; + } + + public void setModelAttr(ModelAttr modelAttr) { + this.modelAttr = modelAttr; + } + + /** + * 鍙傛暟鍚� + */ + private String name; + + /** + * 鏁版嵁绫诲瀷 + */ + private String type; + + /** + * 鍙傛暟绫诲瀷 + */ + private String paramType; + + /** + * 鏄惁蹇呭~ + */ + private Boolean require; + + /** + * 璇存槑 + */ + private String remark; + + /** + * 澶嶆潅瀵硅薄寮曠敤 + */ + private ModelAttr modelAttr; +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Response.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Response.java new file mode 100644 index 0000000..f5209c6 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Response.java @@ -0,0 +1,48 @@ +package com.flightfeather.uav.common.api2word.model; + +import java.io.Serializable; + +/** + * Created by XiuYin.Cui on 2018/1/11. + */ +public class Response implements Serializable{ + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + /** + * 杩斿洖鍙傛暟 + */ + private String description; + + /** + * 鍙傛暟鍚� + */ + private String name; + + /** + * 澶囨敞 + */ + private String remark; +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Table.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Table.java new file mode 100644 index 0000000..b7a2e12 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/model/Table.java @@ -0,0 +1,163 @@ +package com.flightfeather.uav.common.api2word.model; + +import java.util.List; + +/** + * Created by XiuYin.Cui on 2018/1/11. + */ +public class Table { + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getRequestForm() { + return requestForm; + } + + public void setRequestForm(String requestForm) { + this.requestForm = requestForm; + } + + public String getResponseForm() { + return responseForm; + } + + public void setResponseForm(String responseForm) { + this.responseForm = responseForm; + } + + public String getRequestType() { + return requestType; + } + + public void setRequestType(String requestType) { + this.requestType = requestType; + } + + public List<Request> getRequestList() { + return requestList; + } + + public void setRequestList(List<Request> requestList) { + this.requestList = requestList; + } + + public List<Response> getResponseList() { + return responseList; + } + + public void setResponseList(List<Response> responseList) { + this.responseList = responseList; + } + + public String getRequestParam() { + return requestParam; + } + + public void setRequestParam(String requestParam) { + this.requestParam = requestParam; + } + + public String getResponseParam() { + return responseParam; + } + + public void setResponseParam(String responseParam) { + this.responseParam = responseParam; + } + + public ModelAttr getModelAttr() { + return modelAttr; + } + + public void setModelAttr(ModelAttr modelAttr) { + this.modelAttr = modelAttr; + } + + /** + * 澶ф爣棰� + */ + private String title; + /** + * 灏忔爣棰� + */ + private String tag; + /** + * url + */ + private String url; + + /** + * 鎻忚堪 + */ + private String description; + + /** + * 璇锋眰鍙傛暟鏍煎紡 + */ + private String requestForm; + + /** + * 鍝嶅簲鍙傛暟鏍煎紡 + */ + private String responseForm; + + /** + * 璇锋眰鏂瑰紡 + */ + private String requestType; + + /** + * 璇锋眰浣� + */ + private List<Request> requestList; + + /** + * 杩斿洖浣� + */ + private List<Response> responseList; + + /** + * 璇锋眰鍙傛暟 + */ + private String requestParam; + + /** + * 杩斿洖鍙傛暟 + */ + private String responseParam; + + /** + * 杩斿洖灞炴�у垪琛� + */ + private ModelAttr modelAttr = new ModelAttr(); +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/JsonUtils.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/JsonUtils.java new file mode 100644 index 0000000..deca1a7 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/JsonUtils.java @@ -0,0 +1,66 @@ +package com.flightfeather.uav.common.api2word.utils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.io.IOException; +import java.util.List; + +/** + * @author cuixiuyin + * @Date: 2018/11/05 + */ + +public class JsonUtils { + + private static ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); + objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + } + + public static <T> T readValue(String jsonStr, Class<T> clazz) throws IOException { + return objectMapper.readValue(jsonStr, clazz); + } + + public static <T> List<T> readListValue(String jsonStr, Class<T> clazz) throws IOException { + JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, clazz); + return objectMapper.readValue(jsonStr, javaType); + } + + public static ArrayNode readArray(String jsonStr) throws IOException { + JsonNode node = objectMapper.readTree(jsonStr); + if (node.isArray()) { + return (ArrayNode) node; + } + return null; + } + + public static JsonNode readNode(String jsonStr) throws IOException { + return objectMapper.readTree(jsonStr); + } + + public static String writeJsonStr(Object obj) throws JsonProcessingException { + return objectMapper.writeValueAsString(obj); + } + + public static ObjectNode createObjectNode() { + return objectMapper.createObjectNode(); + } + + public static ArrayNode createArrayNode() { + return objectMapper.createArrayNode(); + } + +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/MenuUtils.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/MenuUtils.java new file mode 100644 index 0000000..8e70ff2 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/MenuUtils.java @@ -0,0 +1,25 @@ +package com.flightfeather.uav.common.api2word.utils; + +/** + * @author : cuixiuyin + * @date : 2019/8/31 + */ +public class MenuUtils { + + public static Integer count = 0; + public static String menuStr = "null"; + + public static boolean isMenu(String tags) { + if (menuStr.equals(tags)) { + count++; + } else { + menuStr = tags; + count = 0; + } + if (count == 0) { + return true; + } else { + return false; + } + } +} diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ModelAttrUtils.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ModelAttrUtils.java new file mode 100644 index 0000000..6c94ec2 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ModelAttrUtils.java @@ -0,0 +1,72 @@ +package com.flightfeather.uav.common.api2word.utils; + + +import com.flightfeather.uav.common.api2word.model.ModelAttr; +import com.github.jknack.handlebars.internal.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * @author ivenhan + * @Date: 2020/10/15 + */ + +public class ModelAttrUtils { + + // 灏佽schema - properties涓嬫煇涓叿浣損roperty瀵硅薄 + public static ModelAttr propertyModelAttr(Map<String, Map<String, Object>> property) { + ModelAttr modeAttr = new ModelAttr(); + + Map<String, Object> modeProperties = (Map<String, Object>) property.get("properties"); + ArrayList modeRequired = (ArrayList) property.get("required"); + List<ModelAttr> attrList = new ArrayList<>(); + + if (modeProperties != null) { + Iterator<Entry<String, Object>> mIt = modeProperties.entrySet().iterator(); + + //瑙f瀽灞炴�� + while (mIt.hasNext()) { + Entry<String, Object> mEntry = mIt.next(); + Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue(); + ModelAttr child = new ModelAttr(); + child.setName(mEntry.getKey()); + child.setType((String) attrInfoMap.get("type")); + if (attrInfoMap.get("format") != null) { + child.setType(child.getType() + "(" + attrInfoMap.get("format") + ")"); + } + child.setType(StringUtils.defaultIfBlank(child.getType(), "object")); + + Object ref = attrInfoMap.get("$ref"); + Object items = attrInfoMap.get("items"); + + if (items != null && ((Map) items).get("$ref") == null) { + ModelAttr refModel = propertyModelAttr((Map<String, Map<String, Object>>)items); + if (refModel != null) { + child.setProperties(refModel.getProperties()); + } + child.setType(child.getType()); + } + + child.setDescription((String) attrInfoMap.get("description")); + + child.setRequire(false); + if (modeRequired != null && modeRequired.contains(mEntry.getKey())) { + child.setRequire(true); + } + + attrList.add(child); + } + } + + Object title = property.get("title"); + Object description = property.get("description"); + modeAttr.setClassName(title == null ? "" : title.toString()); + modeAttr.setDescription(description == null ? "" : description.toString()); + modeAttr.setProperties(attrList); + return modeAttr; + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/RequestUtils.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/RequestUtils.java new file mode 100644 index 0000000..51b0c60 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/RequestUtils.java @@ -0,0 +1,29 @@ +package com.flightfeather.uav.common.api2word.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.util.Map; + + +/** + * @author : ivenhan + * @date : 2020/10/16 + */ +public class RequestUtils { + + public static void validateRequestKey(Map<String, Map> content) throws JsonProcessingException { + Map<String, Map> applicationJSON = content.get("application/json"); + if (applicationJSON == null) { + throw new JsonProcessingException("content瀛楁 缂哄皯 application/json 瀛楁") {}; + } + + Map<String, Map> schema = applicationJSON.get("schema"); + if (schema == null) { + throw new JsonProcessingException("content瀛楁 application/json 缂哄皯 schema 瀛楁") {}; + } + + if (schema.get("type") == null) { + throw new JsonProcessingException("content瀛楁 application/json 瀛楁 schema 瀛楁 缂哄皯 type瀛楁") {}; + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ResponseUtils.java b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ResponseUtils.java new file mode 100644 index 0000000..dca0ef0 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/common/api2word/utils/ResponseUtils.java @@ -0,0 +1,44 @@ +package com.flightfeather.uav.common.api2word.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author : ivenhan + * @date : 2020/10/16 + */ +public class ResponseUtils { + + public static void validateResponseKey(Map<String, Map> content) throws JsonProcessingException { + Map<String, Map> applicationJSON = content.get("application/json"); + if (applicationJSON == null) { + throw new JsonProcessingException("content 瀛楁缂哄皯 application/json 瀛楁") {}; + } + + Map<String, Map> schema = applicationJSON.get("schema"); + if (schema == null) { + throw new JsonProcessingException("content 瀛楁 application/json 瀛楁 缂哄皯 schema 瀛楁") {}; + } + + if (schema.get("type") == null) { + throw new JsonProcessingException("content 瀛楁 application/json 瀛楁 schema 瀛楁 缂哄皯 type瀛楁") {}; + } + + Map items = schema.get("items"); + Map properties = schema.get("properties"); + if (items == null && properties == null) { + throw new JsonProcessingException("content 瀛楁 application/json 瀛楁 schema 瀛楁 缂哄皯 properties 鎴栬�� items 瀛楁") {}; + } + + + Set<Entry<String, Map>> contentValues = content.entrySet(); + + int size = contentValues.size(); + if ((applicationJSON.get("examples") != null && size > 2) || (applicationJSON.get("examples") == null && size > 1) ) { + throw new JsonProcessingException("content 瀛楁瀛樺湪闄� application/json 鎴栬�� examples 瀛楁涔嬪鐨勫叾浠栧瓧娈�") {}; + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/config/Swagger2Configuration.kt b/src/main/kotlin/com/flightfeather/uav/config/Swagger2Configuration.kt index d485339..b91354e 100644 --- a/src/main/kotlin/com/flightfeather/uav/config/Swagger2Configuration.kt +++ b/src/main/kotlin/com/flightfeather/uav/config/Swagger2Configuration.kt @@ -16,20 +16,22 @@ */ @Configuration @EnableSwagger2 -class Swagger2Configuration { +class Swagger2Configuration( + @Value("\${springfox.documentation.swagger.v2.enabled}") var swagger2Enable: Boolean, +) { companion object { - const val SWAGGER_SCAN_BASE_PACKAGE = "cn.flightfeather.uav" + const val SWAGGER_SCAN_BASE_PACKAGE = "com.flightfeather.uav" const val VERSION = "1.0.0" } - @Value("\${springfox.documentation.swagger.v2.enabled}") - private val swagger2Enable: Boolean = true +// @Value("\${springfox.documentation.swagger.v2.enabled}") +// private val swagger2Enable: Boolean = true @Bean fun createRestApi(): Docket = - Docket(DocumentationType.SWAGGER_2) + Docket(DocumentationType.OAS_30) .enable(swagger2Enable) .apiInfo(apiInfo()) .select() diff --git a/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt b/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt index c0bf033..15b6b78 100644 --- a/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt +++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt @@ -142,6 +142,7 @@ VELOCITY -> TODO("no such factor") TIME -> TODO("no such factor") HEIGHT -> TODO("no such factor") + else -> TODO("no such factor") } } diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/OpenApiWordService.java b/src/main/kotlin/com/flightfeather/uav/lightshare/service/OpenApiWordService.java new file mode 100644 index 0000000..1886d2f --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/OpenApiWordService.java @@ -0,0 +1,18 @@ +package com.flightfeather.uav.lightshare.service; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Map; + +/** + * Created by XiuYin.Cui on 2018/1/12. + */ +public interface OpenApiWordService { + + Map<String,Object> tableList(String swaggerUrl); + + Map<String, Object> tableListFromString(String jsonStr) throws IOException; + + Map<String, Object> tableList(MultipartFile jsonFile) throws IOException; +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/OpenApiWordServiceImpl.java b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/OpenApiWordServiceImpl.java new file mode 100644 index 0000000..d120472 --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/OpenApiWordServiceImpl.java @@ -0,0 +1,884 @@ +package com.flightfeather.uav.lightshare.service.impl; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.flightfeather.uav.common.api2word.model.ModelAttr; +import com.flightfeather.uav.common.api2word.model.Request; +import com.flightfeather.uav.common.api2word.model.Response; +import com.flightfeather.uav.common.api2word.model.Table; +import com.flightfeather.uav.common.api2word.utils.JsonUtils; +import com.flightfeather.uav.lightshare.service.OpenApiWordService; +import com.github.jknack.handlebars.internal.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +/** + * @Author XiuYin.Cui + * @Date 2018/1/12 + **/ +@SuppressWarnings({"unchecked", "rawtypes"}) +@Service +public class OpenApiWordServiceImpl implements OpenApiWordService { + + @Autowired + private RestTemplate restTemplate; + + @Override + public Map<String, Object> tableList(String swaggerUrl) { + Map<String, Object> resultMap = new HashMap<>(); + try { + String jsonStr = restTemplate.getForObject(swaggerUrl, String.class); + resultMap = tableListFromString(jsonStr); + // log.debug(JsonUtils.writeJsonStr(resultMap)); + } catch (Exception e) { + } + return resultMap; + } + + @Override + public Map<String, Object> tableListFromString(String jsonStr) throws IOException { + Map<String, Object> resultMap = new HashMap<>(); + List<Table> result = new ArrayList<>(); + try { + Map<String, Object> map = getResultFromString(result, jsonStr); + Map<String, List<Table>> tableMap = result.stream().parallel().collect(Collectors.groupingBy(Table::getTitle)); + resultMap.put("tableMap", new TreeMap<>(tableMap)); + resultMap.put("info", map.get("info")); + + // log.debug(JsonUtils.writeJsonStr(resultMap)); + } catch (Exception e) { + throw e; + } + return resultMap; + } + + @Override + public Map<String, Object> tableList(MultipartFile jsonFile) throws IOException { + Map<String, Object> resultMap = new HashMap<>(); + try { + String jsonStr = new String(jsonFile.getBytes()); + resultMap = tableListFromString(jsonStr); + // log.debug(JsonUtils.writeJsonStr(resultMap)); + } catch (Exception e) { + throw e; + } + return resultMap; + } + + private Map<String, Object> getResultFromString(List<Table> result, String jsonStr) throws IOException { + // convert JSON string to Map + Map<String, Object> map = JsonUtils.readValue(jsonStr, HashMap.class); + + //瑙f瀽model + Map<String, ModelAttr> definitinMap = parseComponents(map); + + //瑙f瀽paths + Map<String, Map<String, Object>> paths = (Map<String, Map<String, Object>>) map.get("paths"); + + //鑾峰彇鍏ㄥ眬璇锋眰鍙傛暟鏍煎紡浣滀负榛樿璇锋眰鍙傛暟鏍煎紡 + List<String> defaultConsumes = (List) map.get("consumes"); + + //鑾峰彇鍏ㄥ眬鍝嶅簲鍙傛暟鏍煎紡浣滀负榛樿鍝嶅簲鍙傛暟鏍煎紡 + List<String> defaultProduces = (List) map.get("produces"); + + if (paths != null) { + + Iterator<Entry<String, Map<String, Object>>> it = paths.entrySet().iterator(); + while (it.hasNext()) { + Entry<String, Map<String, Object>> path = it.next(); + + // 0. 鑾峰彇璇ヨ矾鐢变笅鎵�鏈夎姹傛柟寮忕殑鍏叡鍙傛暟 + Map<String, Object> methods = (Map<String, Object>) path.getValue(); + List<LinkedHashMap> commonParameters = (ArrayList) methods.get("parameters"); + + Iterator<Entry<String, Object>> it2 = path.getValue().entrySet().iterator(); + // 1.璇锋眰璺緞 + String url = path.getKey(); + String requestType = null; + while (it2.hasNext()) { + try { + Entry<String, Object> request = it2.next(); + + // 2.璇锋眰鏂瑰紡锛岀被浼间负 get,post,delete,put 杩欐牱 + requestType = request.getKey(); + + if ("parameters".equals(requestType)) { + continue; + } + + Map<String, Object> content = (Map<String, Object>) request.getValue(); + + // 4. 澶ф爣棰橈紙绫昏鏄庯級 + String title = String.valueOf(((List) content.get("tags")).get(0)); + + // 5.灏忔爣棰� 锛堟柟娉曡鏄庯級 + String tag = String.valueOf(content.get("summary")); + + // 6.鎺ュ彛鎻忚堪 + Object descObj = content.get("description"); + String description = descObj == null ? tag : descObj.toString(); + + // 7. 璇锋眰浣� + List<LinkedHashMap> parameters = (ArrayList) content.get("parameters"); + + if (!CollectionUtils.isEmpty(parameters)) { + if (commonParameters != null) { + parameters.addAll(commonParameters); + } + } else { + if (commonParameters != null) { + parameters = commonParameters; + } + } + + // 8.杩斿洖浣� + Map<String, Object> responses = (LinkedHashMap) content.get("responses"); + + // 9.璇锋眰鍙傛暟鏍煎紡锛岀被浼间簬 multipart/form-data + List<String> requestParamsFormates = getRequestParamsFormate(content); + String requestForm = StringUtils.join(requestParamsFormates, ","); + + // 鍙栧嚭鏉ョ姸鎬佹槸200鏃剁殑杩斿洖鍊� + Map<String, Object> obj = (Map<String, Object>) responses.get("200"); + Map<String, Object> requestBody = (Map<String, Object>) content.get("requestBody"); + + // 10.杩斿洖鍙傛暟鏍煎紡锛岀被浼间簬 application/json + List<String> responseParamsFormates = getResponseParamsFormate(obj); + String responseForm = StringUtils.join(responseParamsFormates, ","); + + //灏佽Table + Table table = new Table(); + + table.setTitle(title); + table.setUrl(url); + table.setTag(tag); + table.setDescription(description); + table.setRequestForm(requestForm); + table.setResponseForm(responseForm); + table.setRequestType(requestType); + table.setRequestList(processRequestList(parameters, requestBody, definitinMap)); + + table.setResponseList(processResponseCodeList(responses, definitinMap)); + if (obj != null && obj.get("content") != null) { + table.setModelAttr(processResponseModelAttrs(obj, definitinMap)); + } + + //绀轰緥 + table.setRequestParam(processRequestParam(table.getRequestList())); + table.setResponseParam(processResponseParam1(obj, definitinMap)); + + result.add(table); + } catch (Exception e) { + e.printStackTrace(); +// StringWriter sw = new StringWriter(); +// PrintWriter pw = new PrintWriter(sw); +// e.printStackTrace(pw); +// throw new JsonProcessingException(url + "鎺ュ彛鏍煎紡涓嶆纭�: " + requestType + "璇锋眰 " + e.getMessage()) {}; + } + } + } + } + return map; + } + + /** + * 璇锋眰鍙傛暟鏍煎紡锛� 绫讳技浜� multipart/form-data + */ + private List<String> getRequestParamsFormate(Map<String, Object> obj) { + Map<String, Object> requestBody = (LinkedHashMap) obj.get("requestBody"); + List<String> requestTypes = new ArrayList(); + if (requestBody != null) { + Map<String, Map> content = (LinkedHashMap) requestBody.get("content"); + Set keys = content.keySet(); + return new ArrayList<String>(keys); + } + return requestTypes; + } + + /** + * 杩斿洖鍙傛暟鏍煎紡锛岀被浼间簬 application/json + * @throws Exception + */ + private List<String> getResponseParamsFormate(Map<String, Object> responseObj) { + Map<String, Map> content = (LinkedHashMap) responseObj.get("content"); + List<String> responseTypes = new ArrayList(); + if (content != null) { + Set keys = content.keySet(); + return new ArrayList<String>(keys); + } + return responseTypes; + } + + /** + * 澶勭悊璇锋眰鍙傛暟鍒楄〃 + * + * @param parameters + * @param definitinMap + * @return + * @throws JsonProcessingException + */ + private List<Request> processRequestList(List<LinkedHashMap> parameters, Map<String, Object> requestBody, Map<String, ModelAttr> definitinMap) { + List<Request> requestList = new ArrayList<>(); + if (!CollectionUtils.isEmpty(parameters)) { + for (Map<String, Object> param : parameters) { + Object in = param.get("in"); + Request request = new Request(); + request.setName(String.valueOf(param.get("name"))); + + Map<String, String> schema1 = (Map) param.get("schema"); + + request.setType(schema1 == null ? " " : schema1.get("type").toString()); + // request.setType(param.get("type") == null ? "object" : param.get("type").toString()); + if (param.get("format") != null) { + request.setType(request.getType() + "(" + param.get("format") + ")"); + } + request.setParamType(String.valueOf(in)); + // 鑰冭檻瀵硅薄鍙傛暟绫诲瀷 + if (in != null && "body".equals(in)) { + Map<String, Object> schema = (Map) param.get("schema"); + Object ref = schema.get("$ref"); + // 鏁扮粍鎯呭喌鍙﹀澶勭悊 + if (schema.get("type") != null && "array".equals(schema.get("type"))) { + ref = ((Map) schema.get("items")).get("$ref"); + request.setType("array"); + } + if (ref != null) { + request.setType(request.getType() + ":" + ref.toString().replaceAll("#/definitions/", "")); + request.setModelAttr(definitinMap.get(ref)); + } + } + // 鏄惁蹇呭~ + request.setRequire(false); + if (param.get("required") != null) { + request.setRequire((Boolean) param.get("required")); + } + // 鍙傛暟璇存槑 + request.setRemark(String.valueOf(param.get("description"))); + requestList.add(request); + } + } + + if (requestBody != null) { + Map<String, Map> content = (LinkedHashMap) requestBody.get("content"); + +// try { +// RequestUtils.validateRequestKey(content); +// } catch(Exception e) { +// throw new JsonProcessingException("requestybody 瀛楁 " + e.getMessage()) {}; +// } + + Iterator<Entry<String, Map>> applications = content.entrySet().iterator(); + while (applications.hasNext()) { + Entry<String, Map> application = applications.next(); + + if (application.getValue() != null) { + Request request = new Request(); + + Map<String, Object> schema = (Map<String, Object>) application.getValue().get("schema"); + request.setName(" "); + request.setType(schema == null ? " " : (schema.get("type") == null ? " " : schema.get("type").toString())); + request.setParamType("body"); + + Object ref = schema.get("$ref"); + + if (schema.get("type") != null && "array".equals(schema.get("type"))) { + ref = ((Map) schema.get("items")).get("$ref"); + request.setType("array"); + } + if (ref != null) { + // request.setType(request.getType() + ":" + ref.toString().replaceAll("#/definitions/", "")); + request.setType("object"); + request.setModelAttr(definitinMap.get(ref)); + } + if (schema.get("properties") != null) { + ArrayList<String> requiredArr = new ArrayList<String>(); + if (schema.get("required") != null) { + requiredArr = (ArrayList<String>) schema.get("required"); + } + request.setModelAttr(getRequestSchemaModelAttr(schema, requiredArr)); + } + + // 鏄惁蹇呭~ + request.setRequire(true); + + // 鍙傛暟璇存槑 + requestList.add(request); + } + } + } + return requestList; + } + + + /** + * 澶勭悊杩斿洖鐮佸垪琛� + * + * @param responses 鍏ㄩ儴鐘舵�佺爜杩斿洖瀵硅薄 + * @return + */ + private List<Response> processResponseCodeList(Map<String, Object> responses, Map<String, ModelAttr> definitinMap ) { + List<Response> responseList = new ArrayList<>(); + Iterator<Entry<String, Object>> resIt = responses.entrySet().iterator(); + while (resIt.hasNext()) { + Entry<String, Object> entry = resIt.next(); + Response response = new Response(); + // 鐘舵�佺爜 200 201 401 403 404 杩欐牱 + response.setName(entry.getKey()); + LinkedHashMap<String, Object> statusCodeInfo = (LinkedHashMap) entry.getValue(); + response.setDescription(String.valueOf(statusCodeInfo.get("description"))); + + Map<String, Map> content = (Map) statusCodeInfo.get("content"); + + if (content != null) { +// try { +// ResponseUtils.validateResponseKey(content); +// } catch(Exception e) { +// throw new JsonProcessingException("response瀛楁 " + entry.getKey() + "瀛楁 " + e.getMessage()) {}; +// } + // responses鍐呭application澶氫釜閬嶅巻澶勭悊 + Iterator<Entry<String, Map>> applications = content.entrySet().iterator(); + + while (applications.hasNext()) { + Entry<String, Map> application = applications.next(); + + if (application.getValue() != null) { + Object schema = application.getValue().get("schema"); + if (schema != null) { + Object originalRef = ((LinkedHashMap) schema).get("originalRef"); + response.setRemark(originalRef == null ? "" : originalRef.toString()); + } + responseList.add(response); + } + } + } else { + String ref = String.valueOf(statusCodeInfo.get("$ref")); + + if (ref != "null" && ref != "") { + ModelAttr modelAttr = definitinMap.get(ref); + response.setDescription(modelAttr.getDescription()); + } + + responseList.add(response); + } + } + return responseList; + } + + /** + * 澶勭悊杩斿洖灞炴�у垪琛� + * + * @param responseObj + * @param definitinMap + * @return + */ + private ModelAttr processResponseModelAttrs(Map<String, Object> responseObj, Map<String, ModelAttr> definitinMap) { + Map<String, Map> content = (Map) responseObj.get("content"); + //鍏朵粬绫诲瀷 + ModelAttr modelAttr = new ModelAttr(); + + Iterator<Entry<String, Map>> applications = content.entrySet().iterator(); + + while (applications.hasNext()) { + Entry<String, Map> application = applications.next(); + + if (application.getValue() != null) { + + Map<String, Object> schema = (Map<String, Object>) application.getValue().get("schema"); + String type = (String) schema.get("type"); + String ref = null; + //鏁扮粍 + if ("array".equals(type)) { + Map<String, Object> items = (Map<String, Object>) schema.get("items"); + if (items != null && items.get("$ref") != null) { + ref = (String) items.get("$ref"); + } + } + //瀵硅薄 + if (schema.get("$ref") != null) { + ref = (String) schema.get("$ref"); + } + + //鍏朵粬绫诲瀷 + modelAttr.setType(StringUtils.defaultIfBlank(type, StringUtils.EMPTY)); + + if (StringUtils.isNotBlank(ref) && definitinMap.get(ref) != null) { + modelAttr = definitinMap.get(ref); + } + + // 鏈娇鐢╮ef鏂瑰紡 浣跨敤properties鏂瑰紡 + if (schema.get("properties") != null) { + modelAttr = getSchemaModelAttr(schema); + } + } + } + return modelAttr; + } + + /** + * 瑙f瀽components + * + * @param map + * @return + */ + private Map<String, ModelAttr> parseComponents(Map<String, Object> map) { + Map<String, Object> definitions = (Map<String, Object>) map.get("components"); + Map<String, ModelAttr> definitinMap = new HashMap<>(256); + if (definitions != null) { + Iterator<String> modelNameIt = definitions.keySet().iterator(); + /** + "components": { + "requestBodies": {}, + "schemas": {} + } + */ + while (modelNameIt.hasNext()) { + String modeName = modelNameIt.next(); + /** + "schemas": { + "cat":{}, + "dog":{}, + } + */ + Map<String, Map<String, Object>> modeContent = (Map<String, Map<String, Object>>) definitions.get(modeName); + + if (modeContent != null) { + Iterator<String> modeContentIt = modeContent.keySet().iterator(); + + while (modeContentIt.hasNext()) { + String componentsGrandChildName = modeContentIt.next(); + + getAndPutModelAttr(modeContent, definitinMap, modeName, componentsGrandChildName); + } + } + } + } + return definitinMap; + } + + /** + * 閫掑綊鐢熸垚ModelAttr + * 瀵�$ref绫诲瀷璁剧疆鍏蜂綋灞炴�� + */ + private ModelAttr getAndPutModelAttr(Map<String, Map<String, Object>> swaggerMap, Map<String, ModelAttr> resMap, String parentName, String modeName) { + ModelAttr modeAttr; + if ((modeAttr = resMap.get("#/components/" + parentName + "/" + modeName)) == null) { + modeAttr = new ModelAttr(); + resMap.put("#/components/" + parentName + "/" + modeName, modeAttr); + } else if (resMap.get("#/components/" + parentName + "/" + modeName) != null) { + return resMap.get("#/components/" + parentName + "/" + modeName); + } + Map<String, Object> modeProperties = (Map<String, Object>) swaggerMap.get(modeName).get("properties"); + List<ModelAttr> attrList = new ArrayList<>(); + // 鑾峰彇required瀛楁锛岄亶鍘唒roperties娣诲姞鏄惁蹇呭~灞炴�� + ArrayList modeRequired = (ArrayList) swaggerMap.get(modeName).get("required"); + + if (modeProperties != null) { + Iterator<Entry<String, Object>> mIt = modeProperties.entrySet().iterator(); + + //瑙f瀽灞炴�� + while (mIt.hasNext()) { + Entry<String, Object> mEntry = mIt.next(); + Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue(); + ModelAttr child = new ModelAttr(); + child.setName(mEntry.getKey()); + child.setType((String) attrInfoMap.get("type")); + if (attrInfoMap.get("format") != null) { + child.setType(child.getType() + "(" + attrInfoMap.get("format") + ")"); + } + child.setType(StringUtils.defaultIfBlank(child.getType(), "object")); + + Object ref = attrInfoMap.get("$ref"); + Object items = attrInfoMap.get("items"); + if (ref != null || (items != null && (ref = ((Map) items).get("$ref")) != null)) { + String refName = ref.toString(); + //鎴彇 #/components/ 鍚庨潰鐨� + String clsName = refName.substring(21); + ModelAttr refModel = getAndPutModelAttr(swaggerMap, resMap, parentName, clsName); + if (refModel != null) { + child.setProperties(refModel.getProperties()); + } + child.setType(child.getType() + ":" + clsName); + } + child.setDescription((String) attrInfoMap.get("description")); + + child.setRequire(false); + if (modeRequired != null && modeRequired.contains(mEntry.getKey())) { + child.setRequire(true); + } + + attrList.add(child); + } + } + + Object title = swaggerMap.get(modeName).get("title"); + Object description = swaggerMap.get(modeName).get("description"); + modeAttr.setClassName(title == null ? "" : title.toString()); + modeAttr.setDescription(description == null ? "" : description.toString()); + modeAttr.setProperties(attrList); + return modeAttr; + } + + /** + * 閫掑綊鐢熸垚ModelAttr + * 澶勭悊schema瀵硅薄 + * 澶勭悊requestBody鐩存帴杩斿洖灞炴�у�兼儏鍐� + */ + private ModelAttr getRequestSchemaModelAttr(Map<String, Object> schemaMap, ArrayList requiredArr) { + ModelAttr modeAttr = new ModelAttr(); + Map<String, Object> modeProperties = (Map<String, Object>) schemaMap.get("properties"); + + if ("array".equals(schemaMap.get("type"))) { + Map items = (Map<String, Object>) schemaMap.get("items"); + + if (items != null) { + modeProperties = (Map<String, Object>) items.get("properties"); + } + } + + if (modeProperties == null) { + return null; + } + Iterator<Entry<String, Object>> mIt = modeProperties.entrySet().iterator(); + + List<ModelAttr> attrList = new ArrayList<>(); + //瑙f瀽灞炴�� + while (mIt.hasNext()) { + Entry<String, Object> mEntry = mIt.next(); + Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue(); + ModelAttr child = new ModelAttr(); + child.setName(mEntry.getKey()); + child.setType((String) attrInfoMap.get("type")); + if (attrInfoMap.get("format") != null) { + child.setType(child.getType() + "(" + attrInfoMap.get("format") + ")"); + } + child.setType(StringUtils.defaultIfBlank(child.getType(), "object")); + + Object properties = attrInfoMap.get("properties"); + Object ref = attrInfoMap.get("$ref"); + Object items = attrInfoMap.get("items"); + if (properties != null || (items != null)) { + ArrayList<String> childRequiredArr = new ArrayList<String>(); + if (attrInfoMap.get("required") != null) { + childRequiredArr = (ArrayList<String>) attrInfoMap.get("required"); + } + ModelAttr refModel = getRequestSchemaModelAttr(attrInfoMap, childRequiredArr); + if (refModel != null) { + child.setProperties(refModel.getProperties()); + } + child.setType((String) attrInfoMap.get("type")); + } + child.setRequire(true); + if (!requiredArr.contains(mEntry.getKey())) { + child.setRequire(false); + } + child.setDescription((String) attrInfoMap.get("description")); + attrList.add(child); + } + modeAttr.setClassName(""); + modeAttr.setDescription(""); + modeAttr.setProperties(attrList); + return modeAttr; + } + + /** + * 閫掑綊鐢熸垚ModelAttr + * 澶勭悊schema瀵硅薄 + * 澶勭悊responseData鐩存帴杩斿洖灞炴�у�兼儏鍐� + */ + private ModelAttr getSchemaModelAttr(Map<String, Object> schemaMap) { + ModelAttr modeAttr = new ModelAttr(); + Map<String, Object> modeProperties = (Map<String, Object>) schemaMap.get("properties"); + + if ("array".equals(schemaMap.get("type"))) { + Map items = (Map<String, Object>) schemaMap.get("items"); + + if (items != null) { + modeProperties = (Map<String, Object>) items.get("properties"); + } + } + + if (modeProperties == null) { + return null; + } + Iterator<Entry<String, Object>> mIt = modeProperties.entrySet().iterator(); + + List<ModelAttr> attrList = new ArrayList<>(); + //瑙f瀽灞炴�� + while (mIt.hasNext()) { + Entry<String, Object> mEntry = mIt.next(); + Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue(); + ModelAttr child = new ModelAttr(); + child.setName(mEntry.getKey()); + + child.setType((String) attrInfoMap.get("type")); + if (attrInfoMap.get("format") != null) { + child.setType(child.getType() + "(" + attrInfoMap.get("format") + ")"); + } + child.setType(StringUtils.defaultIfBlank(child.getType(), "object")); + + Object properties = attrInfoMap.get("properties"); + Object ref = attrInfoMap.get("$ref"); + Object items = attrInfoMap.get("items"); + if (properties != null || (items != null)) { + ModelAttr refModel = getSchemaModelAttr(attrInfoMap); + if (refModel != null) { + child.setProperties(refModel.getProperties()); + } + child.setType((String) attrInfoMap.get("type")); + } + child.setDescription((String) attrInfoMap.get("description")); + attrList.add(child); + } + modeAttr.setClassName(""); + modeAttr.setDescription(""); + modeAttr.setProperties(attrList); + return modeAttr; + } + + /** + * 澶勭悊杩斿洖鍊� + * + * @param responseObj + * @return + */ + private String processResponseParam(Map<String, Object> responseObj, Map<String, ModelAttr> definitinMap) throws JsonProcessingException { + Map<String, Map> content = (Map) responseObj.get("content"); + if (content != null) { + Iterator<Entry<String, Map>> applications = content.entrySet().iterator(); + while (applications.hasNext()) { + Entry<String, Map> application = applications.next(); + + if (application.getValue() != null) { + Map<String, Object> applicationContent = (Map<String, Object>) application.getValue(); + if (applicationContent != null) { + Map<String, Object> schema = (Map<String, Object>) applicationContent.get("schema"); + String type = (String) schema.get("type"); + String ref = null; + // 鏁扮粍 + if ("array".equals(type)) { + Map<String, Object> items = (Map<String, Object>) schema.get("items"); + if (items != null && items.get("$ref") != null) { + ref = (String) items.get("$ref"); + } + } + // 瀵硅薄ref + if (schema.get("$ref") != null) { + ref = (String) schema.get("$ref"); + } + if (StringUtils.isNotEmpty(ref)) { + ModelAttr modelAttr = definitinMap.get(ref); + if (modelAttr != null && !CollectionUtils.isEmpty(modelAttr.getProperties())) { + Map<String, Object> responseMap = new HashMap<>(8); + for (ModelAttr subModelAttr : modelAttr.getProperties()) { + responseMap.put(subModelAttr.getName(), getValue(subModelAttr.getType(), subModelAttr)); + } + return JsonUtils.writeJsonStr(responseMap); + } + } + if (schema.get("properties") != null) { + ModelAttr modelAttr = getSchemaModelAttr(schema); + if (modelAttr != null) { + Map<String, Object> responseMap = new HashMap<>(8); + for (ModelAttr subModelAttr : modelAttr.getProperties()) { + responseMap.put(subModelAttr.getName(), getValue(subModelAttr.getType(), subModelAttr)); + } + return JsonUtils.writeJsonStr(responseMap); + } + } + } + } + } + } + return StringUtils.EMPTY; + } + + private String processResponseParam1(Map<String, Object> responseObj, Map<String, ModelAttr> definitinMap) { + Map<String, Map> content = (Map) responseObj.get("content"); + // if (responseObj != null && content.get("application/json") != null) { + if (content != null) { + Iterator<Entry<String, Map>> applications = content.entrySet().iterator(); + while (applications.hasNext()) { + Entry<String, Map> application = applications.next(); + + if (application.getValue() != null) { + Map<String, Map<String, Map>> applicationContent = (Map<String, Map<String, Map>>) application.getValue(); + if (applicationContent != null) { + Map<String, Map> examples = (Map<String, Map>) applicationContent.get("examples"); + + if (examples != null) { + Map<String, Object> responseData = examples.get("response"); + + if (responseData != null) { + Object value = responseData.get("value"); + try { + return JsonUtils.writeJsonStr(value); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + } else { + return ""; + } + + } + } + } + } + return StringUtils.EMPTY; + } + + /** + * 灏佽璇锋眰浣� + * + * @param list + * @return + */ + private String processRequestParam(List<Request> list) { + Map<String, Object> headerMap = new LinkedHashMap<>(); + Map<String, Object> queryMap = new LinkedHashMap<>(); + Map<String, Object> jsonMap = new LinkedHashMap<>(); + if (list != null && list.size() > 0) { + for (Request request : list) { + String name = request.getName(); + String paramType = request.getParamType(); + Object value = getValue(request.getType(), request.getModelAttr()); + switch (paramType) { + case "header": + headerMap.put(name, value); + break; + case "query": + queryMap.put(name, value); + break; + case "path": + queryMap.put(name, value); + break; + case "body": + //TODO 鏍规嵁content-type搴忓垪鍖栨垚涓嶅悓鏍煎紡锛岀洰鍓嶅彧鐢ㄤ簡json + jsonMap.put(name, value); + break; + default: + break; + + } + } + } + String res = ""; + if (!queryMap.isEmpty()) { + res += getUrlParamsByMap(queryMap); + } + if (!headerMap.isEmpty()) { + res += " " + getHeaderByMap(headerMap); + } + if (!jsonMap.isEmpty()) { + if (jsonMap.size() == 1) { + for (Entry<String, Object> entry : jsonMap.entrySet()) { + try { + res += " '" + JsonUtils.writeJsonStr(entry.getValue()) + "'"; + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + } else { + try { + res += " '" + JsonUtils.writeJsonStr(jsonMap) + "'"; + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + } + return res; + } + + /** + * 渚嬪瓙涓紝瀛楁鐨勯粯璁ゅ�� + * + * @param type 绫诲瀷 + * @param modelAttr 寮曠敤鐨勭被鍨� + * @return + */ + private Object getValue(String type, ModelAttr modelAttr) { + int pos; + if ((pos = type.indexOf(":")) != -1) { + type = type.substring(0, pos); + } + switch (type) { + case "string": + return "string"; + case "string(date-time)": + return "2020/01/01 00:00:00"; + case "integer": + case "integer(int64)": + case "integer(int32)": + return 0; + case "number": + return 0.0; + case "boolean": + return true; + case "file": + return "(binary)"; + case "array": + List list = new ArrayList(); + Map<String, Object> map = new LinkedHashMap<>(); + if (modelAttr != null && !CollectionUtils.isEmpty(modelAttr.getProperties())) { + for (ModelAttr subModelAttr : modelAttr.getProperties()) { + map.put(subModelAttr.getName(), getValue(subModelAttr.getType(), subModelAttr)); + } + } + list.add(map); + return list; + case "object": + map = new LinkedHashMap<>(); + if (modelAttr != null && !CollectionUtils.isEmpty(modelAttr.getProperties())) { + for (ModelAttr subModelAttr : modelAttr.getProperties()) { + map.put(subModelAttr.getName(), getValue(subModelAttr.getType(), subModelAttr)); + } + } + return map; + default: + return null; + } + } + + /** + * 灏唌ap杞崲鎴恥rl + */ + public static String getUrlParamsByMap(Map<String, Object> map) { + if (map == null || map.isEmpty()) { + return ""; + } + StringBuffer sb = new StringBuffer(); + for (Entry<String, Object> entry : map.entrySet()) { + sb.append(entry.getKey() + "=" + entry.getValue()); + sb.append("&"); + } + String s = sb.toString(); + if (s.endsWith("&")) { + s = StringUtils.substringBeforeLast(s, "&"); + } + return s; + } + + /** + * 灏唌ap杞崲鎴恏eader + */ + public static String getHeaderByMap(Map<String, Object> map) { + if (map == null || map.isEmpty()) { + return ""; + } + StringBuffer sb = new StringBuffer(); + for (Entry<String, Object> entry : map.entrySet()) { + sb.append("--header '"); + sb.append(entry.getKey() + ":" + entry.getValue()); + sb.append("'"); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/web/OpenApiWordController.java b/src/main/kotlin/com/flightfeather/uav/lightshare/web/OpenApiWordController.java new file mode 100644 index 0000000..15e6eab --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/web/OpenApiWordController.java @@ -0,0 +1,106 @@ +package com.flightfeather.uav.lightshare.web; + +import com.flightfeather.uav.lightshare.service.OpenApiWordService; +import com.github.jknack.handlebars.internal.lang3.StringUtils; +import io.swagger.annotations.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring5.SpringTemplateEngine; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.Map; + +@Controller +@Api(tags = "OpenAPI") +public class OpenApiWordController { + + @Value("${swagger.url}") + private String swaggerUrl; + + @Autowired + private OpenApiWordService openApiWordService; + @Autowired + private SpringTemplateEngine springTemplateEngine; + + private String fileName; + + /** + * 灏� swagger json鏂囦欢杞崲鎴� word鏂囨。骞朵笅杞� + * + * @param model + * @param jsonFile 闇�瑕佽浆鎹㈡垚 word 鏂囨。鐨剆wagger json鏂囦欢 + * @param response + * @return + * @throws Exception + */ + @ApiOperation(value = "灏� swagger json鏂囦欢杞崲鎴� word鏂囨。骞朵笅杞�", notes = "") + @ApiResponses(value = {@ApiResponse(code = 200, message = "璇锋眰鎴愬姛銆�")}) + @RequestMapping(value = "/OpenApiFileToWord", method = {RequestMethod.POST}) + public void getWord(Model model, @ApiParam("swagger json file") @Valid @RequestPart("jsonFile") MultipartFile jsonFile, HttpServletResponse response) throws Exception { + generateModelData(model, jsonFile); + writeContentToResponse(model, response); + } + + @ApiOperation(value = "灏� swagger json鏂囦欢杞崲鎴� word鏂囨。骞朵笅杞�", notes = "") + @ApiResponses(value = {@ApiResponse(code = 200, message = "璇锋眰鎴愬姛銆�")}) + @RequestMapping(value = "/OpenApiFileToWord2", method = {RequestMethod.POST}) + public void getWord2( + Model model, + @ApiParam("swagger json url") @RequestParam("url") String url, + HttpServletResponse response + ) throws Exception { + generateModelData(model, url); + writeContentToResponse(model, response); + } + + private void generateModelData(Model model, MultipartFile jsonFile) throws IOException { + Map<String, Object> result = openApiWordService.tableList(jsonFile); + fileName = jsonFile.getOriginalFilename(); + + if (fileName != null) { + fileName = fileName.replaceAll(".json", ""); + } else { + fileName = "toWord"; + } + + model.addAttribute("url", "http://"); + model.addAttribute("download", 0); + model.addAllAttributes(result); + } + + private void generateModelData(Model model, String url) { + url = StringUtils.defaultIfBlank(url, swaggerUrl); + Map<String, Object> result = openApiWordService.tableList(url); + model.addAttribute("url", url); + model.addAttribute("download", 0); + model.addAllAttributes(result); + } + + private void writeContentToResponse(Model model, HttpServletResponse response) { + Context context = new Context(); + context.setVariables(model.asMap()); + String content = springTemplateEngine.process("word", context); + response.setContentType("application/octet-stream;charset=utf-8"); + response.setCharacterEncoding("utf-8"); + try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())) { + response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".doc", "utf-8")); + byte[] bytes = content.getBytes(); + bos.write(bytes, 0, bytes.length); + bos.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt b/src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt index 27954b2..61a2d8c 100644 --- a/src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt +++ b/src/main/kotlin/com/flightfeather/uav/socket/ServerHandler.kt @@ -77,6 +77,7 @@ super.channelInactive(ctx) } + @Deprecated("Deprecated in Java") override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) { cause?.printStackTrace() ctx?.close() diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 89e2e89..f94f027 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -35,4 +35,10 @@ helperDialect: mysql reasonable: true supportMethodsArguments: true - params: count=countSql \ No newline at end of file + params: count=countSql + +# Swagger json url address +# etc. https://petstore.swagger.io/ +swagger.url: http://localhost:8080/v3/api-docs +# api璁块棶缃戝潃 +# api url : http://localhost:8080/swagger-ui/index.html \ No newline at end of file diff --git a/src/main/resources/templates/word.html b/src/main/resources/templates/word.html new file mode 100644 index 0000000..0fd7619 --- /dev/null +++ b/src/main/resources/templates/word.html @@ -0,0 +1,212 @@ +<!DOCTYPE html> +<html xmlns:th="http://www.thymeleaf.org"> +<head> + <meta http-equiv="Content-Type" content="application/msword; charset=utf-8"/> + <title>toWord</title> + <style type="text/css"> + .bg { + font-size: 14.5px; + font-weight: bold; + color: #000; + background-color: #D7D7D7; + } + + table { + border-width: 1px; + border-style: solid; + border-color: black; + table-layout: fixed; + } + + tr { + height: 32px; + font-size: 12px; + } + + td { + padding-left: 10px; + border-width: 1px; + border-style: solid; + border-color: black; + height: 32px; + overflow: hidden; + word-break: break-all; + word-wrap: break-word; + font-size: 14.5px; + } + + .bg td { + font-size: 14.5px; + } + + tr td { + font-size: 14.5px; + } + + .specialHeight { + height: 40px; + } + + .first_title { + height: 60px; + line-height: 60px; + margin: 0; + font-weight: bold; + font-size: 21px; + } + + .second_title { + height: 40px; + line-height: 40px; + margin: 0; + font-size: 18.5px; + } + + .doc_title { + font-size: 42.5px; + text-align: center; + } + + .download_btn { + float: right; + } + + body { + font-family: 鎬濇簮榛戜綋 Normal; + } + </style> +</head> + +<body> +<div style="width:800px; margin: 0 auto"> + <div> + <p class="doc_title" th:text="${info.title +'锛�'+ info.version +'锛�'}"></p> + <a class="download_btn" th:if="${download == 1}" th:href="${'/downloadWord?url='+ url}">涓嬭浇鏂囨。</a> + <br> + </div> + <div th:each="tableMap, tableMapStat:${tableMap}" style="margin-bottom:20px;"> + <!--杩欎釜鏄被鐨勮鏄�--> + <h4 class="first_title" th:text="${tableMapStat.count} + '. ' + ${tableMap.key}"></h4> + <div th:each="table,tableStat:${tableMap.value}"> + + <!--杩欎釜鏄瘡涓姹傜殑璇存槑锛屾柟渚跨敓鎴愭枃妗e悗杩涜鏁寸悊--> + <h5 class="second_title" th:text="${tableStat.count} + '锛�' + ${table.tag}"></h5> + + <table border="1" cellspacing="0" cellpadding="0" width="100%"> + <tr class="bg"> + <td colspan="5" th:text="${table.tag}"></td> + </tr> + <tr> + <td width="25%">鎺ュ彛鎻忚堪</td> + <td colspan="4" th:text="${table.description}"></td> + </tr> + <tr> + <td>URL</td> + <td colspan="4" th:text="${table.url}"></td> + </tr> + <tr> + <td>璇锋眰鏂瑰紡</td> + <td colspan="4" th:text="${#strings.toUpperCase(table.requestType)}"></td> + </tr> + <tr> + <td>璇锋眰绫诲瀷</td> + <td colspan="4" th:text="${table.requestForm}"></td> + </tr> + <tr> + <td>杩斿洖绫诲瀷</td> + <td colspan="4" th:text="${table.responseForm}"></td> + </tr> + + <tr class="bg"> + <td>鍙傛暟鍚�</td> + <td width="15%">鏁版嵁绫诲瀷</td> + <td width="15%">鍙傛暟绫诲瀷</td> + <td width="15%">鏄惁蹇呭~</td> + <td width="29%">璇存槑</td> + </tr> + + <th:block th:each="request, c:${table.requestList}"> + <tr> + <td align="left" th:text="${c.count} + '.' + ${request.name}"></td> + <td th:text="${request.type}"></td> + <td th:text="${request.paramType}"></td> + <td th:if="${request.require}" th:text="Y"></td> + <td th:if="${!request.require}" th:text="N"></td> + <td th:text="${request.remark}"></td> + <!-- <td th:if="${request.modelAttr}" th:text="asdfagadfg"></td>--> + </tr> + <th:block th:if="${request.modelAttr}"> + <tbody th:include="this::request(${request.modelAttr.properties},${c.count} + '.', 1)"/> + </th:block> + + + </th:block> + + <tr class="bg"> + <td>鐘舵�佺爜</td> + <td colspan="2">鎻忚堪</td> + <td colspan="2">璇存槑</td> + </tr> + + <tr th:each="response:${table.responseList}"> + <td th:text="${response.name}"></td> + <td colspan="2" th:text="${response.description}"></td> + <td colspan="2" th:text="${response.remark}"></td> + </tr> + + <tr class="bg"> + <td>杩斿洖灞炴�у悕</td> + <td colspan="2">绫诲瀷</td> + <td colspan="2">璇存槑</td> + </tr> + + <!-- 瀵硅繑鍥炲弬鏁� 閫掑綊鐢熸垚琛�--> + <tbody th:include="this::response(${table.modelAttr.properties},'', 1)"/> + +<!-- <tr class="bg">--> +<!-- <td colspan="5">绀轰緥</td>--> +<!-- </tr>--> +<!-- <tr class="specialHeight">--> +<!-- <td class="bg">璇锋眰鍙傛暟</td>--> +<!-- <td colspan="4" th:text="${table.requestParam}"></td>--> +<!-- </tr>--> +<!-- <tr class="specialHeight">--> +<!-- <td class="bg">杩斿洖鍊�</td>--> +<!-- <td colspan="4" th:text="${table.responseParam}"></td>--> +<!-- </tr>--> + + </table> + </div> + </div> +</div> + +<th:block th:fragment="request(properties,count, lv)"> + <th:block th:each="p,c : ${properties}"> + <tr> + <td align="left" th:text="${count} + '' + ${c.count} + '.' + ${p.name}" + th:style="|padding-left:${10*lv}px|"></td> + <td th:text="${p.type}"></td> + <td></td> + <td th:if="${p.require}" th:text="Y"></td> + <td th:if="${!p.require}" th:text="N"></td> + <td th:text="${p.description}"></td> + </tr> + <th:block th:unless="${#lists.isEmpty(p.properties)}" + th:include="this::request(${p.properties},${count} + '' + ${c.count} + '.',${lv+1})"/> + </th:block> +</th:block> + +<th:block th:fragment="response(properties,count, lv)"> + <th:block th:each="p,c : ${properties}"> + <tr> + <td align="left" th:text="${count} + '' + ${c.count} + '.' + ${p.name}" + th:style="|padding-left:${10*lv}px|"></td> + <td colspan="2" th:text="${p.type}"></td> + <td colspan="2" th:text="${p.description}"></td> + </tr> + <th:block th:unless="${#lists.isEmpty(p.properties)}" + th:include="this::response(${p.properties},${count} + '' + ${c.count} + '.',${lv+1})"/> + </th:block> +</th:block> +</body> +</html> -- Gitblit v1.9.3