diff --git a/common/pom.xml b/common/pom.xml index 216d535e..3f29940d 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -65,6 +65,13 @@ + + + org.springframework.boot + spring-boot-starter-freemarker + + + \ No newline at end of file diff --git a/common/src/main/java/ink/wgink/common/base/ApiInfoController.java b/common/src/main/java/ink/wgink/common/base/ApiInfoController.java index a97ac747..80844a8b 100644 --- a/common/src/main/java/ink/wgink/common/base/ApiInfoController.java +++ b/common/src/main/java/ink/wgink/common/base/ApiInfoController.java @@ -1,30 +1,14 @@ package ink.wgink.common.base; -import ink.wgink.exceptions.ParamsException; -import ink.wgink.exceptions.SearchException; -import ink.wgink.interfaces.menu.IMenuBaseService; -import ink.wgink.interfaces.user.IUserCheckService; -import ink.wgink.pojo.dtos.menu.MenuDTO; +import freemarker.template.TemplateException; +import ink.wgink.common.service.api.IApiInfoService; import ink.wgink.pojo.dtos.permission.SystemApiDTO; -import ink.wgink.util.RegexUtil; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Swagger; -import io.swagger.models.Tag; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.servlet.ModelAndView; -import springfox.documentation.service.Documentation; -import springfox.documentation.spring.web.DocumentationCache; -import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import java.util.ArrayList; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.List; import java.util.Map; @@ -38,67 +22,26 @@ import java.util.Map; * @Date: 2021/2/24 20:17 * @Version: 1.0 **/ -@Controller +@RestController public class ApiInfoController { - private static String API_TYPE_GET = "get"; - private static String API_TYPE_POST = "post"; - private static String API_TYPE_PUT = "put"; - private static String API_TYPE_DELETE = "delete"; - @Autowired - private DocumentationCache documentationCache; - @Autowired - private ServiceModelToSwagger2Mapper serviceModelToSwagger2Mapper; + private IApiInfoService apiInfoService; @GetMapping("system-api/{group}/{apiType}") @ResponseBody public SystemApiDTO getSystemApi(@PathVariable("group") String group, @PathVariable("apiType") String apiType, HttpServletRequest request) { - boolean apiTypeFlag = StringUtils.equalsIgnoreCase(API_TYPE_POST, apiType) || - StringUtils.equalsIgnoreCase(API_TYPE_DELETE, apiType) || - StringUtils.equalsIgnoreCase(API_TYPE_PUT, apiType) || - StringUtils.equalsIgnoreCase(API_TYPE_GET, apiType); - if (!apiTypeFlag) { - throw new ParamsException("api类型错误,只能是 post、delete、put、get 其中一种"); - } - Documentation documentation = documentationCache.documentationByGroup(group); - if (documentation == null) { - throw new SearchException("无任何接口"); - } - Swagger swagger = serviceModelToSwagger2Mapper.mapDocumentation(documentation); - // 标签 - List tags = swagger.getTags(); - List apis = new ArrayList<>(); - for (Map.Entry kv : swagger.getPaths().entrySet()) { - String keyPath = kv.getKey(); - Path valuePath = kv.getValue(); - Operation operation = null; - if (StringUtils.equals(API_TYPE_POST, apiType)) { - operation = valuePath.getPost(); - } else if (StringUtils.equals(API_TYPE_DELETE, apiType)) { - operation = valuePath.getDelete(); - } else if (StringUtils.equals(API_TYPE_PUT, apiType)) { - operation = valuePath.getPut(); - } else if (StringUtils.equals(API_TYPE_GET, apiType)) { - operation = valuePath.getGet(); - } - if (operation == null) { - continue; - } - String apiTag = null; - for (Tag tag : tags) { - if (StringUtils.equals(tag.getName(), operation.getTags().get(0))) { - apiTag = tag.getDescription().replaceAll("\\s", ""); - } - } - if (apiTag == null) { - continue; - } - apis.add(new SystemApiDTO.ApiDTO(RegexUtil.replacePathParams(keyPath, "*"), operation.getSummary(), apiTag)); - } - SystemApiDTO systemApiDTO = new SystemApiDTO(); - systemApiDTO.setSystemTag(request.getContextPath()); - systemApiDTO.setApis(apis); - return systemApiDTO; + return apiInfoService.getSystemApi(group, apiType, request); + + } + + @GetMapping("api-info/get-api") + public Map getApi(@RequestParam(name = "apiTypes", required = false) List apiTypes) { + return apiInfoService.getApi(apiTypes); + } + + @GetMapping("api-info/get") + public void get(HttpServletRequest request, HttpServletResponse response) throws IOException, TemplateException { + apiInfoService.get(request, response); } } diff --git a/common/src/main/java/ink/wgink/common/service/api/IApiInfoService.java b/common/src/main/java/ink/wgink/common/service/api/IApiInfoService.java new file mode 100644 index 00000000..cc372ed8 --- /dev/null +++ b/common/src/main/java/ink/wgink/common/service/api/IApiInfoService.java @@ -0,0 +1,47 @@ +package ink.wgink.common.service.api; + +import freemarker.template.TemplateException; +import ink.wgink.pojo.dtos.permission.SystemApiDTO; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * @ClassName: IApiInfoService + * @Description: API + * @Author: wanggeng + * @Date: 2021/8/15 4:05 下午 + * @Version: 1.0 + */ +public interface IApiInfoService { + /** + * 获取系统API + * + * @param group + * @param apiType + * @param request + * @return + */ + SystemApiDTO getSystemApi(String group, String apiType, HttpServletRequest request); + + /** + * 获取API + * + * @param apiTypes + * @return + */ + Map getApi(List apiTypes); + + /** + * 下载文档 + * + * @param request + * @param response + * @throws IOException + * @throws TemplateException + */ + void get(HttpServletRequest request, HttpServletResponse response) throws IOException, TemplateException; +} diff --git a/common/src/main/java/ink/wgink/common/service/api/impl/ApiInfoServiceImpl.java b/common/src/main/java/ink/wgink/common/service/api/impl/ApiInfoServiceImpl.java new file mode 100644 index 00000000..a0249b0e --- /dev/null +++ b/common/src/main/java/ink/wgink/common/service/api/impl/ApiInfoServiceImpl.java @@ -0,0 +1,480 @@ +package ink.wgink.common.service.api.impl; + +import com.alibaba.fastjson.JSONObject; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.common.service.api.IApiInfoService; +import ink.wgink.exceptions.ParamsException; +import ink.wgink.exceptions.SearchException; +import ink.wgink.interfaces.consts.ISystemConstant; +import ink.wgink.pojo.dtos.permission.SystemApiDTO; +import ink.wgink.properties.ServerProperties; +import ink.wgink.util.RegexUtil; +import ink.wgink.util.date.DateUtil; +import io.swagger.models.*; +import io.swagger.models.parameters.BodyParameter; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.RefProperty; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; +import springfox.documentation.service.Documentation; +import springfox.documentation.spring.web.DocumentationCache; +import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.util.*; + +/** + * @ClassName: ApiInfoServiceImpl + * @Description: API + * @Author: wanggeng + * @Date: 2021/8/15 4:06 下午 + * @Version: 1.0 + */ +@Service +public class ApiInfoServiceImpl extends DefaultBaseService implements IApiInfoService { + + private static String API_TYPE_GET = "get"; + private static String API_TYPE_POST = "post"; + private static String API_TYPE_PUT = "put"; + private static String API_TYPE_DELETE = "delete"; + + @Autowired + private DocumentationCache documentationCache; + @Autowired + private ServiceModelToSwagger2Mapper serviceModelToSwagger2Mapper; + @Autowired + private FreeMarkerConfigurer freeMarkerConfigurer; + @Autowired + private ServerProperties serverProperties; + + @PostConstruct + public void init() { + freeMarkerConfigurer.getConfiguration().setClassForTemplateLoading(ApiInfoServiceImpl.class, "/templates"); + } + + @Override + public SystemApiDTO getSystemApi(String group, String apiType, HttpServletRequest request) { + boolean apiTypeFlag = StringUtils.equalsIgnoreCase(API_TYPE_POST, apiType) || + StringUtils.equalsIgnoreCase(API_TYPE_DELETE, apiType) || + StringUtils.equalsIgnoreCase(API_TYPE_PUT, apiType) || + StringUtils.equalsIgnoreCase(API_TYPE_GET, apiType); + if (!apiTypeFlag) { + throw new ParamsException("api类型错误,只能是 post、delete、put、get 其中一种"); + } + Documentation documentation = documentationCache.documentationByGroup(group); + if (documentation == null) { + throw new SearchException("无任何接口"); + } + Swagger swagger = serviceModelToSwagger2Mapper.mapDocumentation(documentation); + // 标签 + List tags = swagger.getTags(); + List apis = new ArrayList<>(); + for (Map.Entry kv : swagger.getPaths().entrySet()) { + String keyPath = kv.getKey(); + Path valuePath = kv.getValue(); + Operation operation = null; + if (StringUtils.equals(API_TYPE_POST, apiType)) { + operation = valuePath.getPost(); + } else if (StringUtils.equals(API_TYPE_DELETE, apiType)) { + operation = valuePath.getDelete(); + } else if (StringUtils.equals(API_TYPE_PUT, apiType)) { + operation = valuePath.getPut(); + } else if (StringUtils.equals(API_TYPE_GET, apiType)) { + operation = valuePath.getGet(); + } + if (operation == null) { + continue; + } + String apiTag = null; + for (Tag tag : tags) { + if (StringUtils.equals(tag.getName(), operation.getTags().get(0))) { + apiTag = tag.getDescription().replaceAll("\\s", ""); + } + } + if (apiTag == null) { + continue; + } + apis.add(new SystemApiDTO.ApiDTO(RegexUtil.replacePathParams(keyPath, "*"), operation.getSummary(), apiTag)); + } + SystemApiDTO systemApiDTO = new SystemApiDTO(); + systemApiDTO.setSystemTag(request.getContextPath()); + systemApiDTO.setApis(apis); + return systemApiDTO; + } + + @Override + public Map getApi(List apiTypes) { + Map apiDocMap = new HashMap<>(); + List> apiGroups = new ArrayList<>(); + if (apiTypes != null) { + for (String apiType : apiTypes) { + String name = null; + if (StringUtils.equalsIgnoreCase(apiType, "SYSTEM")) { + name = "系统接口"; + } else if (StringUtils.equalsIgnoreCase(apiType, "APP")) { + name = "APP接口"; + } else if (StringUtils.equalsIgnoreCase(apiType, "RESOURCE")) { + name = "资源接口"; + } else if (StringUtils.equalsIgnoreCase(apiType, "WECHAT")) { + name = "微信接口"; + } + if (name == null) { + continue; + } + Map systemDocMap = new HashMap<>(); + systemDocMap.put("name", name); + systemDocMap.put("api", getApi(apiType.toUpperCase())); + apiGroups.add(systemDocMap); + } + } + apiDocMap.put("title", serverProperties.getSystemTitle()); + apiDocMap.put("apiGroups", apiGroups); + apiDocMap.put("author", "XXXXXX"); + apiDocMap.put("date", DateUtil.getDay()); + apiDocMap.put("version", "v1.0"); + return apiDocMap; + } + + @Override + public void get(HttpServletRequest request, HttpServletResponse response) throws IOException, TemplateException { + String apiTypes = request.getParameter("apiTypes"); + Template template = freeMarkerConfigurer.getConfiguration().getTemplate("ftl/api-template.ftl"); + response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("接口文档.doc", "UTF-8")); + Map apiDocMap = getApi(StringUtils.isBlank(apiTypes) ? null : Arrays.asList(apiTypes.split(","))); + Writer writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), ISystemConstant.CHARSET_UTF8)); + template.process(apiDocMap, writer); + } + + /** + * 获取API + * + * @param groupName + * @return + */ + private Map getApi(String groupName) { + Documentation documentation = documentationCache.documentationByGroup(groupName); + if (documentation == null) { + return null; + } + Swagger swagger = serviceModelToSwagger2Mapper.mapDocumentation(documentation); + Info info = swagger.getInfo(); + String host = swagger.getHost(); + String basePath = swagger.getBasePath(); + List tags = swagger.getTags(); + Map paths = swagger.getPaths(); + Map definitions = swagger.getDefinitions(); + + List> apiControllers = new ArrayList<>(); + Map apiMap = new HashMap<>(); + apiMap.put("info", info); + apiMap.put("host", host); + apiMap.put("basePath", basePath); + apiMap.put("apiControllers", apiControllers); + apiMap.put("definitions", definitions); + for (Tag tag : tags) { + Map apiController = new HashMap<>(); + apiController.put("name", tag.getName().indexOf("\\-") > 0 ? tag.getName().split("\\-")[1] : tag.getName()); + apiController.put("description", tag.getDescription()); + apiController.put("apis", listApiMap(tag.getName(), paths, definitions)); + apiControllers.add(apiController); + } + return apiMap; + } + + /** + * api列表 + * + * @param apiController + * @param paths + * @return + */ + private List> listApiMap(String apiController, Map paths, Map definitions) { + List> pathMaps = new ArrayList<>(); + for (Map.Entry kv : paths.entrySet()) { + String apiUrl = kv.getKey(); + Path path = kv.getValue(); + + Map operationMap = path.getOperationMap(); + for (Map.Entry operationEntry : operationMap.entrySet()) { + HttpMethod httpMethod = operationEntry.getKey(); + Operation operation = operationEntry.getValue(); + if (StringUtils.equalsIgnoreCase(apiController, operation.getTags().get(0))) { + Map methodOperationMap = new HashMap<>(); + methodOperationMap.put("apiUrl", apiUrl); + methodOperationMap.put("method", httpMethod.name()); + methodOperationMap.put("summary", operation.getSummary()); + methodOperationMap.put("description", operation.getDescription() == null ? "无" : operation.getDescription()); + methodOperationMap.put("consumes", operation.getConsumes()); + methodOperationMap.put("produces", operation.getProduces()); + // 请求参数 + List> parameters = listRequestParameter(operation); + methodOperationMap.put("parameters", parameters); + methodOperationMap.put("parameterRefs", listRef(parameters, definitions)); + // 请求示例 + List> parameterExamples = listExample(parameters, definitions); + methodOperationMap.put("parameterExamples", parameterExamples); + // 响应参数 + List> responses = listResponseMap(operation); + methodOperationMap.put("responses", responses); + methodOperationMap.put("responseRefs", listRef(responses, definitions)); + // 响应示例 + List> responseExamples = listExample(responses, definitions); + methodOperationMap.put("responseExamples", responseExamples); + pathMaps.add(methodOperationMap); + } + } + } + return pathMaps; + } + + /** + * 请求参数 + * + * @param operation + * @return + */ + private List> listRequestParameter(Operation operation) { + List> requestParameterList = new ArrayList<>(); + List parameters = operation.getParameters(); + for (Parameter parameter : parameters) { + Map requestParameter = new HashMap<>(); + requestParameter.put("class", parameter.getClass()); + requestParameter.put("name", parameter.getName()); + requestParameter.put("description", parameter.getDescription()); + String type = StringUtils.isBlank(parameter.getPattern()) ? "string" : parameter.getPattern(); + requestParameter.put("in", parameter.getIn()); + if (StringUtils.equals(parameter.getIn(), "body")) { + type = "object"; + } + requestParameter.put("type", type); + requestParameter.put("source", parameter); + requestParameter.put("access", parameter.getAccess()); + String ref = "无"; + String simpleRef = "无"; + if (parameter instanceof BodyParameter) { + BodyParameter bodyParameter = (BodyParameter) parameter; + Model schema = bodyParameter.getSchema(); + requestParameter.put("schemaClass", schema.getClass()); + if (schema instanceof RefModel) { + RefModel refModel = (RefModel) schema; + ref = refModel.get$ref(); + simpleRef = refModel.getSimpleRef(); + } + } + requestParameter.put("ref", ref); + requestParameter.put("simpleRef", simpleRef); + requestParameterList.add(requestParameter); + } + return requestParameterList; + } + + /** + * 响应结果 + * + * @param operation + * @return + */ + private List> listResponseMap(Operation operation) { + Map responses = operation.getResponses(); + List> responseMaps = new ArrayList<>(); + for (Map.Entry responseEntry : responses.entrySet()) { + String code = responseEntry.getKey(); + Response response = responseEntry.getValue(); + Map responseMap = new HashMap<>(); + responseMap.put("code", code); + responseMap.put("description", response.getDescription()); + Property schema = response.getSchema(); + String ref = "无"; + String simpleRef = "无"; + String type = "无"; + if (schema != null) { + responseMap.put("schemaClass", schema.getClass()); + type = schema.getType(); + if (schema instanceof ArrayProperty) { + ArrayProperty arrayProperty = (ArrayProperty) schema; + Property items = arrayProperty.getItems(); + if (items instanceof RefProperty) { + RefProperty refProperty = (RefProperty) items; + ref = refProperty.get$ref(); + simpleRef = refProperty.getSimpleRef(); + } + } else if (schema instanceof RefProperty) { + RefProperty refProperty = (RefProperty) schema; + ref = refProperty.get$ref(); + simpleRef = refProperty.getSimpleRef(); + } + } + responseMap.put("ref", ref); + responseMap.put("simpleRef", simpleRef); + responseMap.put("type", StringUtils.equals(type, "ref") ? "object" : type); + responseMaps.add(responseMap); + } + return responseMaps; + } + + /** + * 引用对象列表 + * + * @param listParameter + * @param definitions + * @return + */ + private List> listRef(List> listParameter, Map definitions) { + List> refs = new ArrayList<>(); + for (Map parameter : listParameter) { + if (StringUtils.equals(parameter.get("ref").toString(), "无")) { + continue; + } + String simpleRef = parameter.get("simpleRef").toString(); + setRef(refs, simpleRef, definitions); + } + return refs; + } + + /** + * 设置引用 + * + * @param refs + * @param simpleRef + * @param definitions + */ + private void setRef(List> refs, String simpleRef, Map definitions) { + for (Map.Entry definitionKV : definitions.entrySet()) { + if (StringUtils.equals(definitionKV.getKey(), simpleRef)) { + Model model = definitionKV.getValue(); + if (model instanceof ModelImpl) { + ModelImpl modelImpl = (ModelImpl) model; + Map modelProperties = model.getProperties(); + if (modelProperties == null) { + return; + } + + Map requestParameterRef = new HashMap<>(); + // 这里提前添加为了保证递归时的顺序 + refs.add(requestParameterRef); + requestParameterRef.put("class", model.getClass()); + requestParameterRef.put("name", definitionKV.getKey()); + requestParameterRef.put("type", modelImpl.getType()); + requestParameterRef.put("modelProperties", modelProperties); + List> modelPropertiesList = new ArrayList<>(); + // 这里提前添加为了保证递归时的顺序 + requestParameterRef.put("modelPropertiesList", modelPropertiesList); + for (Map.Entry modelPropertiesKV : modelProperties.entrySet()) { + Property modelPropertiesValue = modelPropertiesKV.getValue(); + Map modelPropertiesMap = new HashMap<>(); + modelPropertiesMap.put("name", modelPropertiesKV.getKey()); + modelPropertiesMap.put("description", StringUtils.isBlank(modelPropertiesValue.getDescription()) ? "无" : modelPropertiesValue.getDescription()); + modelPropertiesMap.put("type", modelPropertiesValue.getType()); + modelPropertiesMap.put("example", modelPropertiesValue.getExample() == null ? "无" : modelPropertiesValue); + modelPropertiesMap.put("class", modelPropertiesValue.getClass()); + modelPropertiesList.add(modelPropertiesMap); + // 递归完成引用类的构建 + RefProperty subRefProperty = getRefProperty(modelPropertiesValue); + String subSimpleRef = "无"; + if (subRefProperty != null) { + subSimpleRef = subRefProperty.getSimpleRef(); + // 相同引用不递归处理 + if (!StringUtils.equals(simpleRef, subSimpleRef)) { + setRef(refs, subSimpleRef, definitions); + } + } + modelPropertiesMap.put("simpleRef", subSimpleRef); + } + } + return; + } + } + } + + /** + * 获取对象示例 + * + * @param listParameter + * @param definitions + * @return + */ + private List> listExample(List> listParameter, Map definitions) { + List> exampleList = new ArrayList<>(); + for (Map parameter : listParameter) { + if (StringUtils.equals(parameter.get("ref").toString(), "无")) { + continue; + } + String simpleRef = parameter.get("simpleRef").toString(); + Map exampleMap = new HashMap<>(); + exampleMap.put("simpleRef", simpleRef); + // 得到对象示例 + Map example = new HashMap<>(); + setExample(example, simpleRef, definitions); + // 带格式的JSON输出 + exampleMap.put("example", JSONObject.toJSONString(example)); + exampleList.add(exampleMap); + } + return exampleList; + } + + private void setExample(Map exampleMap, String simpleRef, Map definitions) { + for (Map.Entry definitionKV : definitions.entrySet()) { + if (StringUtils.equals(definitionKV.getKey(), simpleRef)) { + Model model = definitionKV.getValue(); + if (model instanceof ModelImpl) { + Map modelProperties = model.getProperties(); + if (modelProperties == null) { + return; + } + for (Map.Entry modelPropertiesKV : modelProperties.entrySet()) { + Property modelPropertiesValue = modelPropertiesKV.getValue(); + exampleMap.put(modelPropertiesKV.getKey(), ""); + // 递归完成引用类的构建 + RefProperty subRefProperty = getRefProperty(modelPropertiesValue); + if (subRefProperty != null) { + String subSimpleRef = subRefProperty.getSimpleRef(); + // 相同引用不递归处理 + if (!StringUtils.equals(simpleRef, subSimpleRef)) { + Map subExampleMap = new HashMap<>(); + if (StringUtils.equals(modelPropertiesValue.getType(), "array")) { + List> subExampleList = new ArrayList<>(); + subExampleList.add(subExampleMap); + exampleMap.put(modelPropertiesKV.getKey(), subExampleList); + } else { + exampleMap.put(modelPropertiesKV.getKey(), subExampleMap); + } + setExample(subExampleMap, subSimpleRef, definitions); + } + } + } + } + return; + } + } + } + + /** + * 获取引用属性 + * + * @param modelPropertiesValue + * @return + */ + private RefProperty getRefProperty(Property modelPropertiesValue) { + RefProperty subRefProperty = null; + if (modelPropertiesValue instanceof ArrayProperty) { + ArrayProperty arrayProperty = (ArrayProperty) modelPropertiesValue; + Property items = arrayProperty.getItems(); + if (items instanceof RefProperty) { + subRefProperty = (RefProperty) items; + } + } else if (modelPropertiesValue instanceof RefProperty) { + subRefProperty = (RefProperty) modelPropertiesValue; + } + return subRefProperty; + } +} diff --git a/common/src/main/resources/templates/ftl/api-template.ftl b/common/src/main/resources/templates/ftl/api-template.ftl new file mode 100644 index 00000000..8f742c28 --- /dev/null +++ b/common/src/main/resources/templates/ftl/api-template.ftl @@ -0,0 +1,3250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${title} + + + + + + + + + + + + + + + + + + + + + + 接口设计文档 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 作者:${author} + + + + + + + + + + + + + + + + + + 时间:${date} + + + + + + + + + + + + + + + + + + 版本:${version} + + + + + + + + + + + + + + + + + + + + + +<#list apiGroups as apiGroup> + + + + + + + + + + + + + + + + + + + + + + + ${apiGroup.name} + + + <#list apiGroup.api.apiControllers as apiController> + + + + + + + + + + + + + + + + + + + + + + ${apiController.name} + + + <#list apiController.apis as api> + + + + + + + + + + + + + + + + + + + + + + ${api.summary} + + + + + + + + + + + + + + + + + + + + + + + + 功能描述 + + + + + + + + + + + + + + + + ${api.description} + + + + + + + + + + + + + + + + + + + + + + + + 接口说明 + + + + + + + + + + + + + + + + + + + + + + 请求方式:${api.method} + + + + + + + + + + + + + + + + + + + + + + 请求地址:${apiGroup.api.basePath}${api.apiUrl} + + + + + + + + + + + + + + + + + + + + + + 请求参数 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 名称 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 描述 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 参数类型 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 数据类型 + + + + + <#list api.parameters as parameter> + + + + + + + + + + + + + + + + + + + + + + ${parameter.name} + + + + + + + + + + + + + + + + + + + + + + + + ${parameter.description} + + + + + + + + + + + + + + + + + + + + + + + + ${parameter.in} + + + + + + + + + + + + + + + + + + + + + + + + ${parameter.type} + + + + + + + <#list api.parameterRefs as parameterRef> + + + + + + + + + + + + + + ${parameterRef.name}对象属性 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 属性名 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 描述 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 类型 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 构造对象 + + + + + <#list parameterRef.modelPropertiesList as modelProperties> + + + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.name} + + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.description} + + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.type} + + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.simpleRef} + + + + + + + + <#list api.parameterExamples as parameterExample> + + + + + + + + + + + + + + + + + + + + ${parameterExample.simpleRef} 示例 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${parameterExample.example} + + + + + + + + + + + + + + + + + + + + + + + + + + 响应 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 响应状态码 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 说明 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 返回值 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 返回类型` + + + + + <#list api.responses as response> + + + + + + + + + + + + + + + + + + + + + + + + + ${response.code} + + + + + + + + + + + + + + + + + + + + + + + + ${response.description} + + + + + + + + + + + + + + + + + + + + + + + + ${response.simpleRef} + + + + + + + + + + + + + + + + + + + + + + + + ${response.type} + + + + + + + <#list api.responseRefs as responseRef> + + + + + + + + + + + + + + ${responseRef.name}对象属性 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 名称 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 描述 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 参数类型 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 数据类型 + + + + + <#if (responseRef.modelPropertiesList)??> + <#list responseRef.modelPropertiesList as modelProperties> + + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.name} + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.description} + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.type} + + + + + + + + + + + + + + + + + + + + + + + + ${modelProperties.simpleRef} + + + + + + + + + <#list api.responseExamples as responseExample> + + + + + + + + + + + + + + + + + + + + ${responseExample.simpleRef}示例 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${responseExample.example} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 王 耿 + + + 王 耿 + 2 + 2021-08-13T23:46:00Z + 2021-08-13T23:46:00Z + + + + + + + + 1 + 2 + 60 + 342 + Microsoft Office Word + 0 + 2 + 1 + false + + false + 401 + false + false + 16.0000 + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index a7c93ee2..f51fe1c8 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.2 0.4.8 2.5.0 - 2.0.4 + 2.0.9 2.9.4 1.6 2.6.0 @@ -148,6 +148,11 @@ spring-boot-autoconfigure ${spring-boot.version} + + org.springframework.boot + spring-boot-starter-freemarker + ${spring-boot.version} +