pull/3/head
tanghc 4 years ago
parent 160e57df0e
commit 7dab8da699
  1. 5
      changelog.md
  2. 6
      doc/docs/files/10041_编写文档.md
  3. 2
      sop-admin/sop-admin-server/pom.xml
  4. 2
      sop-auth/pom.xml
  5. 4
      sop-common/sop-bridge-eureka/pom.xml
  6. 4
      sop-common/sop-bridge-nacos/pom.xml
  7. 2
      sop-common/sop-gateway-common/pom.xml
  8. 2
      sop-common/sop-service-common/pom.xml
  9. 37
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/BizCode.java
  10. 5
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/Open.java
  11. 15
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/ServiceConfiguration.java
  12. 19
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/CustomSwaggerParameterBuilder.java
  13. 87
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/DocumentationPluginsManagerExt.java
  14. 2
      sop-example/sop-springmvc/pom.xml
  15. 2
      sop-example/sop-story/pom.xml
  16. 13
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
  17. 2
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/param/StoryParam.java
  18. 5
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/result/StoryResult.java
  19. 2
      sop-gateway/pom.xml
  20. 2
      sop-website/pom.xml
  21. 13
      sop-website/src/main/java/com/gitee/sop/websiteserver/bean/BizCode.java
  22. 1
      sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java
  23. 20
      sop-website/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java
  24. 23
      sop-website/src/main/resources/public/pages/doc/doc.html
  25. 19
      sop-website/src/main/resources/public/pages/doc/docEvent.js

@ -1,5 +1,10 @@
# changelog
## 4.0.3
- 可定义业务错误码(见`@Open`注解中的`bizCode`属性)
- 文档参数可指定最大长度(使用`@Length(max = xx)`)
## 4.0.2
- 支持swagger排序(position属性)

@ -84,7 +84,11 @@ public class StoryResult {
* @return
*/
@ApiOperation(value = "获取故事信息", notes = "说明接口的详细信息,介绍,用途,注意事项等。")
@Open(value = "alipay.story.find")
@Open(value = "alipay.story.find", bizCode = {
// 定义业务错误码,用于文档显示
@BizCode(code = "100001", msg = "姓名错误", solution = "填写正确的姓名"),
@BizCode(code = "100002", msg = "备注错误", solution = "填写正确备注"),
})
public StoryResult getStory2(StoryParam story) {
log.info("获取故事信息参数, story: {}", story);
// 获取其它参数

@ -24,7 +24,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-bridge-nacos</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<!-- easyopen starter -->

@ -25,7 +25,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<!-- sop相关配置 end-->

@ -10,7 +10,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
<artifactId>sop-bridge-eureka</artifactId>
@ -18,7 +18,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>

@ -10,7 +10,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
<artifactId>sop-bridge-nacos</artifactId>
@ -18,7 +18,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>

@ -11,7 +11,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>

@ -12,7 +12,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-service-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>

@ -0,0 +1,37 @@
package com.gitee.sop.servercommon.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 错误码
*
* @author tanghc
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BizCode {
/**
* 错误码
*
* @return
*/
String code();
/**
* 错误描述
* @return
*/
String msg();
/**
* 解决方案
* @return
*/
String solution() default "";
}

@ -44,4 +44,9 @@ public @interface Open {
* 是否需要appAuthToken设置为true网关端会校验token是否存在
*/
boolean needToken() default false;
/**
* 定义业务错误码用于文档显示
*/
BizCode[] bizCode() default {};
}

@ -59,6 +59,21 @@ public class ServiceConfiguration implements WebMvcConfigurer {
registry.addInterceptor(new ServiceContextInterceptor());
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty("spring.cloud.nacos.discovery.server-addr")
public NacosWatch nacosWatch(NacosDiscoveryProperties nacosDiscoveryProperties, ObjectProvider<TaskScheduler> taskScheduler, Environment environment) {
Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();
String contextPath = environment.getProperty("server.servlet.context-path");
// 将context-path信息加入到metadata中
if (contextPath != null) {
metadata.put("context-path", contextPath);
}
// 在元数据中新增启动时间,不能修改这个值,不然网关拉取接口会有问题
metadata.put("time.startup", String.valueOf(System.currentTimeMillis()));
return new NacosWatch(nacosDiscoveryProperties, taskScheduler);
}
@Bean
@ConditionalOnMissingBean
GlobalExceptionHandler globalExceptionHandler() {

@ -6,9 +6,12 @@ import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import org.hibernate.validator.constraints.Length;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.service.StringVendorExtension;
import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
@ -18,6 +21,7 @@ import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.readers.parameter.Examples;
import springfox.documentation.swagger.schema.ApiModelProperties;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -76,6 +80,8 @@ public class CustomSwaggerParameterBuilder implements ExpandedParameterBuilderPl
.scalarExample(apiParam.example())
.complexExamples(Examples.examples(apiParam.examples()))
.order(SWAGGER_PLUGIN_ORDER)
// 添加额外属性
.vendorExtensions(this.getVendorExtension(context))
.build();
}
@ -94,9 +100,22 @@ public class CustomSwaggerParameterBuilder implements ExpandedParameterBuilderPl
.scalarExample(apiModelProperty.example())
//源码这里是: SWAGGER_PLUGIN_ORDER,需要修正
.order(apiModelProperty.position())
// 添加额外属性
.vendorExtensions(this.getVendorExtension(context))
.build();
}
private List<VendorExtension> getVendorExtension(ParameterExpansionContext context) {
List<VendorExtension> vendorExtensions = new ArrayList<>(4);
Optional<Length> annotation = context.findAnnotation(Length.class);
if (annotation.isPresent()) {
Length length = annotation.get();
vendorExtensions.add(new StringVendorExtension("maxLength", String.valueOf(length.max())));
vendorExtensions.add(new StringVendorExtension("minLength", String.valueOf(length.min())));
}
return vendorExtensions;
}
private ParameterBuilder maybeSetParameterName(ParameterExpansionContext context, String parameterName) {
if (!Strings.isNullOrEmpty(parameterName)) {
context.getParameterBuilder().name(parameterName);

@ -1,10 +1,19 @@
package com.gitee.sop.servercommon.swagger;
import com.alibaba.fastjson.JSON;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.members.RawField;
import com.gitee.sop.servercommon.annotation.BizCode;
import com.gitee.sop.servercommon.annotation.Open;
import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.google.common.base.Optional;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import springfox.documentation.service.Operation;
import springfox.documentation.service.StringVendorExtension;
@ -12,7 +21,15 @@ import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author tanghc
@ -23,6 +40,7 @@ public class DocumentationPluginsManagerExt extends DocumentationPluginsManager
private static final String SOP_VERSION = "sop_version";
private static final String MODULE_ORDER = "module_order";
private static final String API_ORDER = "api_order";
private static final String BIZ_CODE = "biz_code";
@Override
public Operation operation(OperationContext operationContext) {
@ -40,6 +58,8 @@ public class DocumentationPluginsManagerExt extends DocumentationPluginsManager
String version = buildVersion(open.version());
vendorExtensions.add(new StringVendorExtension(SOP_NAME, name));
vendorExtensions.add(new StringVendorExtension(SOP_VERSION, version));
this.setBizCode(open, vendorExtensions);
this.setResultExtProperties(operationContext);
}
Optional<Api> apiOptional = operationContext.findControllerAnnotation(Api.class);
int order = 0;
@ -60,6 +80,73 @@ public class DocumentationPluginsManagerExt extends DocumentationPluginsManager
vendorExtensions.add(new StringVendorExtension(API_ORDER, String.valueOf(methodOrder)));
}
/**
* 设置返回结果额外属性如最大长度
* @param operationContext
*/
private void setResultExtProperties(OperationContext operationContext) {
List<VendorExtension> vendorExtensions = operationContext.getDocumentationContext().getVendorExtentions();
ResolvedType returnType = operationContext.getReturnType();
Class<?> erasedType = returnType.getErasedType();
String className = erasedType.getSimpleName();
boolean exist = vendorExtensions.stream().anyMatch(p -> Objects.equals(p.getName(), className));
if (!exist) {
List<RawField> memberFields = returnType.getMemberFields();
Map<String, Map<String, Object>> fieldProperties = new HashMap<>(16);
for (RawField memberField : memberFields) {
String key = memberField.getName();
Length length = AnnotationUtils.findAnnotation(memberField.getRawMember(), Length.class);
if (length != null) {
Map<String, Object> properties = fieldProperties.computeIfAbsent(key, k -> new HashMap<>(16));
properties.computeIfAbsent("maxLength", k -> length.max());
properties.computeIfAbsent("minLength", k -> length.max());
}
ApiModelProperty apiModelProperty = AnnotationUtils.findAnnotation(memberField.getRawMember(), ApiModelProperty.class);
if (apiModelProperty != null) {
Map<String, Object> properties = fieldProperties.computeIfAbsent(key, k -> new HashMap<>(16));
boolean required = apiModelProperty.required();
// 只有在必填的情况下设置
if (required) {
properties.put("required", required);
}
}
}
vendorExtensions.add(new StringVendorExtension(className, JSON.toJSONString(fieldProperties)));
}
}
private Class<?> getGenericType(Field curField) {
// 当前集合的泛型类型
Type genericType = curField.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
// 得到泛型里的class类型对象
return (Class<?>) pt.getActualTypeArguments()[0];
}
return null;
}
/**
* 设置业务错误码
* @param open
* @param vendorExtensions
*/
private void setBizCode(Open open, List<VendorExtension> vendorExtensions) {
BizCode[] bizCodes = open.bizCode();
List<BizCodeObj> codeObjList = Stream.of(bizCodes)
.map(bizCode -> new BizCodeObj(bizCode.code(), bizCode.msg(), bizCode.solution()))
.collect(Collectors.toList());
vendorExtensions.add(new StringVendorExtension(BIZ_CODE, JSON.toJSONString(codeObjList)));
}
@Data
@AllArgsConstructor
private static class BizCodeObj {
private String code;
private String msg;
private String solution;
}
private String buildVersion(String version) {
if ("".equals(version)) {
return ServiceConfig.getInstance().getDefaultVersion();

@ -26,7 +26,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<!-- nacos -->
<dependency>

@ -19,7 +19,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>

@ -1,5 +1,6 @@
package com.gitee.sop.storyweb.controller;
import com.gitee.sop.servercommon.annotation.BizCode;
import com.gitee.sop.servercommon.annotation.Open;
import com.gitee.sop.storyweb.controller.param.CategoryParam;
import com.gitee.sop.storyweb.controller.param.LargeTextParam;
@ -42,7 +43,11 @@ public class Example1001_BaseController {
// 基础用法
@ApiOperation(value = "获取故事信息(首位)", notes = "获取故事信息的详细信息", position = -100/* position默认0,值越小越靠前 */)
@Open("story.get")
@Open(value = "story.get", bizCode = {
// 定义业务错误码,用于文档显示
@BizCode(code = "100001", msg = "姓名错误", solution = "填写正确的姓名"),
@BizCode(code = "100002", msg = "备注错误", solution = "填写正确备注"),
})
@RequestMapping("/get/v1")
public StoryResult get_v1(StoryParam param) {
StoryResult story = new StoryResult();
@ -53,7 +58,11 @@ public class Example1001_BaseController {
// 指定版本号
@ApiOperation(value = "获取故事信息", notes = "获取故事信息的详细信息")
@Open(value = "story.get", version = "2.0")
@Open(value = "story.get", version = "2.0", bizCode = {
// 定义业务错误码,用于文档显示
@BizCode(code = "100001", msg = "姓名错误", solution = "填写正确的姓名"),
@BizCode(code = "100002", msg = "备注错误", solution = "填写正确备注"),
})
@RequestMapping("/get/v2")
public StoryResult get_v2(StoryParam param) {
StoryResult story = new StoryResult();

@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
@Data
@ -17,5 +18,6 @@ public class StoryParam {
private String name;
@ApiModelProperty(value = "备注 (第二)", example = "xx", position = 2)
@Length(max = 64, message = "长度不能超过64")
private String remark;
}

@ -12,10 +12,11 @@ import java.util.Date;
*/
@Data
public class StoryResult {
@ApiModelProperty(value = "故事ID", example = "1")
@ApiModelProperty(value = "故事ID", required = true, example = "1")
private Long id;
@ApiModelProperty(value = "故事名称", example = "海底小纵队")
@ApiModelProperty(value = "故事名称", required = true, example = "海底小纵队")
@Length(max = 20)
private String name;
@ApiModelProperty(value = "创建时间", example = "2019-04-14 19:02:12")

@ -24,7 +24,7 @@
<groupId>com.gitee.sop</groupId>
<artifactId>sop-bridge-nacos</artifactId>
<!-- <artifactId>sop-bridge-eureka</artifactId>-->
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>

@ -25,7 +25,7 @@
<groupId>com.gitee.sop</groupId>
<artifactId>sop-bridge-nacos</artifactId>
<!--<artifactId>sop-bridge-eureka</artifactId>-->
<version>4.0.2-SNAPSHOT</version>
<version>4.0.3-SNAPSHOT</version>
</dependency>
<dependency>

@ -0,0 +1,13 @@
package com.gitee.sop.websiteserver.bean;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class BizCode {
private String code;
private String msg;
private String solution;
}

@ -30,6 +30,7 @@ public class DocItem {
List<DocParameter> requestParameters;
List<DocParameter> responseParameters;
List<BizCode> bizCodeList;
public String getNameVersion() {
return name + version;

@ -1,7 +1,9 @@
package com.gitee.sop.websiteserver.manager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.websiteserver.bean.BizCode;
import com.gitee.sop.websiteserver.bean.DocInfo;
import com.gitee.sop.websiteserver.bean.DocItem;
import com.gitee.sop.websiteserver.bean.DocModule;
@ -121,6 +123,10 @@ public class SwaggerDocParser implements DocParser {
docItem.setDescription(docInfo.getString("description"));
docItem.setMultiple(docInfo.getString("multiple") != null);
docItem.setProduces(docInfo.getJSONArray("produces").toJavaList(String.class));
String bizCodeStr = docInfo.getString("biz_code");
if (bizCodeStr != null) {
docItem.setBizCodeList(JSON.parseArray(bizCodeStr, BizCode.class));
}
docItem.setModuleOrder(NumberUtils.toInt(docInfo.getString("module_order"), 0));
docItem.setApiOrder(NumberUtils.toInt(docInfo.getString("api_order"), 0));
String moduleName = this.buildModuleName(docInfo, docRoot);
@ -208,6 +214,8 @@ public class SwaggerDocParser implements DocParser {
protected List<DocParameter> buildDocParameters(String ref, JSONObject docRoot, boolean doSubRef) {
JSONObject responseObject = docRoot.getJSONObject("definitions").getJSONObject(ref);
String className = responseObject.getString("title");
JSONObject extProperties = docRoot.getJSONObject(className);
JSONObject properties = responseObject.getJSONObject("properties");
List<DocParameter> docParameterList = new ArrayList<>();
if (properties == null) {
@ -224,10 +232,20 @@ public class SwaggerDocParser implements DocParser {
JSONObject fieldInfo = properties.getJSONObject(fieldName);
DocParameter docParameter = fieldInfo.toJavaObject(DocParameter.class);
docParameter.setName(fieldName);
if (extProperties != null) {
JSONObject prop = extProperties.getJSONObject(fieldName);
if (prop != null) {
String maxLength = prop.getString("maxLength");
docParameter.setMaxLength(maxLength == null ? "-" : maxLength);
String required = prop.getString("required");
if (required != null) {
docParameter.setRequired(Boolean.parseBoolean(required));
}
}
}
docParameterList.add(docParameter);
RefInfo refInfo = this.getRefInfo(fieldInfo);
if (refInfo != null && doSubRef) {
// 如果是树状菜单的话,这里可能触发死循环
String subRef = refInfo.ref;
boolean nextDoRef = !Objects.equals(ref, subRef);
List<DocParameter> refs = buildDocParameters(subRef, docRoot, nextDoRef);

@ -367,6 +367,29 @@
</pre>
</div>
<div class="site-title">
<fieldset class="layui-elem-field layui-field-title site-title">
<legend>业务错误码</legend>
</fieldset>
</div>
<div class="site-text">
<p>
<a href="code.html" target="_blank">公共错误码</a>
</p>
<div class="site-text">
<table id="bizCode" class="layui-table">
<thead>
<tr>
<th>错误码</th>
<th>错误描述</th>
<th>解决方案</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div><!-- layui-main end~ -->

@ -16,6 +16,7 @@ function selectItem(docItem, layui) {
createRequestParameter(docItem);
createResponseParameter(docItem);
createResponseCode(docItem);
buildBizCode(docItem);
var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]');
$li.addClass('layui-this').siblings().removeClass('layui-this');
@ -124,3 +125,21 @@ function buildExample(parameter) {
return '\"' + parameter.example + '\"';
}
}
function buildBizCode(docItem) {
var html = []
var bizCodeList = docItem.bizCodeList;
if (bizCodeList && bizCodeList.length > 0) {
for (var i = 0; i < bizCodeList.length; i++) {
var bizCode = bizCodeList[i];
html.push('<tr>')
html.push('<td>'+bizCode.code+'</td>')
html.push('<td>'+bizCode.msg+'</td>')
html.push('<td>'+bizCode.solution+'</td>')
html.push('</tr>')
}
$('#bizCode').find('tbody').html(html.join(''));
} else {
$('#bizCode').find('tbody').html('<tr><td colspan="3" style="text-align: center">暂无数据</td></tr>');
}
}
Loading…
Cancel
Save