Merge branch 'master' into SpringCloudGateway

tanghc 6 years ago
commit 004b4b84ec
  1. 2
      README.md
  2. 1
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
  3. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
  4. 8
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java
  5. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
  6. 11
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java
  7. 12
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java
  8. 21
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java
  9. 28
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java
  10. 42
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java
  11. 21
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java
  12. 37
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/DownloadController.java
  13. 4
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/vo/FileUploadVO.java
  14. 93
      sop-sdk/sdk-csharp/SDKCSharp/Client/OpenClient.cs
  15. 52
      sop-test/src/main/java/com/gitee/sop/test/HttpTool.java
  16. 67
      sop-test/src/test/java/com/gitee/sop/test/DownloadTest.java
  17. 15
      sop-test/src/test/java/com/gitee/sop/test/TestBase.java
  18. 1
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java
  19. 140
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java
  20. 470
      sop-website/website-server/src/main/resources/api.json

@ -1,5 +1,7 @@
# SOP(Simple Open Platform)
> 2.0版本正在开发中...
一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台。
SOP提供了两种接口调用方式,分别是:[支付宝开放平台](https://docs.open.alipay.com/api)的调用方式和[淘宝开放平台](http://open.taobao.com/api.htm?docId=285&docType=2)的调用方式。

@ -56,5 +56,6 @@ public class SopConstants {
public static final String UNKNOWN_SERVICE= "_sop_unknown_service_";
public static final String UNKNOWN_METHOD = "_sop_unknown_method_";
public static final String UNKNOWN_VERSION = "_sop_unknown_version_";
public static final String UNKNOWN_APPID = "_sop_unknown_appId_";
}

@ -2,6 +2,7 @@ package com.gitee.sop.gatewaycommon.gateway.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.LimitFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.LoadBalancerClientExtFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.ParameterFormatterFilter;
import com.gitee.sop.gatewaycommon.gateway.filter.ValidateFilter;
@ -90,6 +91,11 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
return new ValidateFilter();
}
@Bean
LimitFilter limitFilter() {
return new LimitFilter();
}
@Bean
LoadBalancerClientExtFilter loadBalancerClientExtFilter() {
return new LoadBalancerClientExtFilter();

@ -17,7 +17,6 @@ import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@ -41,7 +40,7 @@ public class LimitFilter implements GlobalFilter, Ordered {
}
// 单个限流功能未开启
if (configLimitDto.getLimitStatus() == ConfigLimitDto.LIMIT_STATUS_CLOSE) {
return null;
return chain.filter(exchange);
}
byte limitType = configLimitDto.getLimitType().byteValue();
LimitManager limitManager = ApiConfig.getInstance().getLimitManager();
@ -82,12 +81,15 @@ public class LimitFilter implements GlobalFilter, Ordered {
List<ConfigLimitDto> limitConfigList = new ArrayList<>();
for (String limitKey : limitKeys) {
ConfigLimitDto configLimitDto = limitConfigManager.get(limitKey);
if (configLimitDto == null) {
continue;
}
limitConfigList.add(configLimitDto);
}
if (limitConfigList.isEmpty()) {
return null;
}
Collections.sort(limitConfigList, Comparator.comparing(ConfigLimitDto::getOrderIndex));
limitConfigList.sort(Comparator.comparing(ConfigLimitDto::getOrderIndex));
return limitConfigList.get(0);
}
}

@ -96,6 +96,11 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
responseData.put(GATEWAY_CODE_NAME, ISV_MISSING_METHOD_META.getCode());
responseData.put(GATEWAY_MSG_NAME, ISV_MISSING_METHOD_META.getError().getCode());
} else {
Map<String, Object> params = this.getApiParam(request);
String name = this.getParamValue(params, ParamNames.API_NAME, SopConstants.UNKNOWN_METHOD);
String version = this.getParamValue(params, ParamNames.VERSION_NAME, SopConstants.UNKNOWN_VERSION);
String appId = this.getParamValue(params, ParamNames.APP_KEY_NAME, SopConstants.UNKNOWN_APPID);
log.error("微服务端报错,方法名:{}, 版本号:{}, appId:{}, 微服务返回结果:{}",name, version, appId, serviceResult);
this.storeError(request, ErrorType.UNKNOWN);
// 微服务端有可能返回500错误
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}

@ -2,14 +2,15 @@ package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
@ -28,13 +29,17 @@ public class ZuulErrorController implements ErrorController {
*/
@RequestMapping(ERROR_PATH)
@ResponseBody
public Object error(HttpServletResponse response) {
public Object error(HttpServletRequest request, HttpServletResponse response) {
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.getResponse() == null) {
ctx.setResponse(response);
}
ctx.setResponseStatusCode(HttpStatus.OK.value());
Throwable throwable = ctx.getThrowable();
log.error("网关报错,URL:{}, status:{}, params:{}",
request.getRequestURL().toString()
, response.getStatus()
, ZuulContext.getApiParam()
, throwable);
return this.buildResult(throwable);
}

@ -4,12 +4,16 @@ import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
import com.netflix.util.Pair;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.MediaType;
import java.io.InputStream;
import java.util.List;
/**
* 合并微服务结果统一返回格式
@ -33,6 +37,14 @@ public class PostResultFilter extends BaseZuulFilter {
if (requestContext.getResponse().isCommitted()) {
return null;
}
List<Pair<String, String>> zuulResponseHeaders = requestContext.getZuulResponseHeaders();
boolean isDownloadRequest = zuulResponseHeaders
.stream()
.anyMatch(pair -> StringUtils.contains(pair.second(), MediaType.APPLICATION_OCTET_STREAM_VALUE));
// 如果是文件下载直接返回
if (isDownloadRequest) {
return null;
}
InputStream responseDataStream = requestContext.getResponseDataStream();
ApiConfig apiConfig = ApiContext.getApiConfig();
ResultExecutor<RequestContext, String> resultExecutor = apiConfig.getZuulResultExecutor();

@ -1,5 +1,6 @@
package com.gitee.sop.servercommon.bean;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.time.DateUtils;
@ -25,14 +26,20 @@ public class OpenContextImpl<T> implements OpenContext<T> {
private JSONObject rootJsonObject;
private T bizObject;
public OpenContextImpl(JSONObject rootJsonObject) {
this(rootJsonObject, null);
}
public OpenContextImpl(JSONObject rootJsonObject, Class<?> bizClass) {
if (rootJsonObject == null) {
throw new IllegalArgumentException("rootJsonObject can not be null");
}
this.rootJsonObject = rootJsonObject;
if (bizClass != null) {
JSONObject bizJsonObj = this.rootJsonObject.getJSONObject(BIZ_CONTENT_NAME);
if (bizJsonObj == null) {
bizJsonObj = rootJsonObject;
String bizContent = getBizContent();
if (bizContent != null) {
bizObject = (T) JSON.parseObject(bizContent, bizClass);
}
bizObject = (T) bizJsonObj.toJavaObject(bizClass);
}
}
@ -101,10 +108,10 @@ public class OpenContextImpl<T> implements OpenContext<T> {
if (bizObject != null && bizObject.getClass() == clazz) {
return (E) bizObject;
}
JSONObject bizJsonObj = this.rootJsonObject.getJSONObject(BIZ_CONTENT_NAME);
if (bizJsonObj == null) {
String bizContent = getBizContent();
if (bizContent == null) {
return null;
}
return bizJsonObj.toJavaObject(clazz);
return JSON.parseObject(bizContent, clazz);
}
}

@ -1,6 +1,12 @@
package com.gitee.sop.servercommon.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.servercommon.bean.OpenContext;
import com.gitee.sop.servercommon.bean.OpenContextImpl;
import com.gitee.sop.servercommon.bean.ServiceContext;
import com.gitee.sop.servercommon.util.OpenUtil;
import lombok.Setter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
@ -9,15 +15,37 @@ import javax.servlet.http.HttpServletResponse;
/**
* @author tanghc
*/
@Setter
public class ServiceContextInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ServiceContext context = ServiceContext.getCurrentContext();
context.setRequest(request);
context.setResponse(response);
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 修复方法参数个数为0,OpenContext获取null问题
if (handlerMethod.getMethodParameters().length == 0) {
initOpenContext(request);
}
}
return true;
}
/**
* 初始化OpenContext
*
* @param request request
*/
protected void initOpenContext(HttpServletRequest request) {
JSONObject requestParams = OpenUtil.getRequestParams(request);
OpenContext openContext = new OpenContextImpl(requestParams);
ServiceContext.getCurrentContext().setOpenContext(openContext);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ServiceContext.getCurrentContext().unset();

@ -12,13 +12,17 @@ import org.springframework.core.MethodParameter;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@ -95,9 +99,27 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
* @return 没有返回null
*/
protected Object getParamObject(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) {
Class<?> parameterType = methodParameter.getParameterType();
// WebRequest / NativeWebRequest / ServletWebRequest
if (WebRequest.class.isAssignableFrom(parameterType)) {
if (!parameterType.isInstance(nativeWebRequest)) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + nativeWebRequest);
}
return nativeWebRequest;
}
// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
if (ServletRequest.class.isAssignableFrom(parameterType) || MultipartRequest.class.isAssignableFrom(parameterType)) {
return resolveNativeRequest(nativeWebRequest, parameterType);
}
// ServletResponse, HttpServletResponse
if (ServletResponse.class.isAssignableFrom(parameterType)) {
return resolveNativeResponse(nativeWebRequest, parameterType);
}
HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
JSONObject requestParams = OpenUtil.getRequestParams(request);
Class<?> parameterType = methodParameter.getParameterType();
// 方法参数类型
Class<?> bizObjClass = parameterType;
boolean isOpenRequestParam = parameterType == OpenContext.class;
@ -112,6 +134,24 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
return isOpenRequestParam ? openContext : bizObj;
}
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeRequest = webRequest.getNativeRequest(requiredType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeRequest;
}
private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeResponse = webRequest.getNativeResponse(requiredType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeResponse;
}
/**
* 获取泛型参数类型
* @param methodParameter 参数

@ -17,7 +17,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* 支付宝服务端假设签名验证通过后到达这里进行具体的业务处理
@ -109,6 +111,8 @@ public class AlipayController {
@ApiAbility
@GetMapping("getStory2")
public Story getStory2_0() {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
System.out.println(openContext.getAppId());
Story story = new Story();
story.setId(1);
story.setName("海底小纵队(默认版本号)");
@ -180,6 +184,23 @@ public class AlipayController {
return storyVO;
}
@ApiOperation(value = "返回数组结果", notes = "返回数组结果")
@ApiMapping(value = "alipay.story.find2")
public List<StoryVO> getStory3(StoryParam story) {
int index = 0;
StoryVO storyVO = new StoryVO();
storyVO.id = 1L;
storyVO.name = "白雪公主, index:" + index++;
storyVO.gmt_create = new Date();
StoryVO storyVO2 = new StoryVO();
storyVO2.id = 1L;
storyVO2.name = "白雪公主, index:" + index++;
storyVO2.gmt_create = new Date();
return Arrays.asList(storyVO, storyVO2);
}
/**
* 演示文档表格树
* @param story

@ -0,0 +1,37 @@
package com.gitee.sop.storyweb.controller;
import com.gitee.sop.servercommon.annotation.ApiMapping;
import com.gitee.sop.storyweb.controller.param.StoryParam;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import java.io.File;
import java.io.IOException;
/**
* 演示文件下载
*
* @author tanghc
*/
@Controller
public class DownloadController {
@ApiMapping(value = "story.download")
public ResponseEntity<byte[]> export(StoryParam param) throws IOException {
HttpHeaders headers = new HttpHeaders();
// 假设下载classpath下的application.properties文件
ClassPathResource resource = new ClassPathResource("/application.properties");
File file = resource.getFile();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", file.getName());
return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
}
}

@ -1,5 +1,6 @@
package com.gitee.sop.storyweb.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.ArrayList;
@ -25,8 +26,11 @@ public class FileUploadVO {
public FileMeta() {
}
@ApiModelProperty(value = "文件名称", example = "1.txt")
private String filename;
@ApiModelProperty(value = "文件大小", example = "109")
private long size;
@ApiModelProperty(value = "文件内容", example = "啊啊啊")
private String content;
}
}

@ -20,46 +20,108 @@ namespace SDKCSharp.Client
/// </summary>
public class OpenClient
{
/// <summary>
/// 默认配置
/// </summary>
private static OpenConfig DEFAULT_CONFIG = new OpenConfig();
private Dictionary<string, string> header = new Dictionary<string, string>();
/// <summary>
/// 接口请求url
/// </summary>
private string url;
/// <summary>
/// 平台提供的appId
/// </summary>
private string appId;
/// <summary>
/// 开放平台提供的私钥
/// </summary>
private string privateKey;
/// <summary>
/// 开放平台提供的公钥
/// </summary>
private string publicKeyPlatform;
/// <summary>
/// 配置项
/// </summary>
private OpenConfig openConfig;
/// <summary>
/// 请求对象
/// </summary>
private OpenRequest openRequest;
private DataNameBuilder dataNameBuilder;
/// <summary>
/// 节点处理
/// </summary>
private DataNameBuilder dataNameBuilder;
/// <summary>
/// 构建请求客户端
/// </summary>
/// <param name="url">接口url</param>
/// <param name="appId">平台分配的appId</param>
/// <param name="privateKey">平台分配的私钥</param>
public OpenClient(string url, string appId, string privateKey)
: this(url, appId, privateKey,false, DEFAULT_CONFIG)
{
}
/// <summary>
/// 构建请求客户端
/// </summary>
/// <param name="url">接口url</param>
/// <param name="appId">平台分配的appId</param>
/// <param name="privateKey">平台分配的私钥</param>
/// <param name="publicKeyPlatform">平台分配的公钥</param>
public OpenClient(string url, string appId, string privateKey, string publicKeyPlatform)
: this(url, appId, privateKey)
{
this.publicKeyPlatform = publicKeyPlatform;
}
/// <summary>
/// 构建请求客户端
/// </summary>
/// <param name="url">接口url</param>
/// <param name="appId">平台分配的appId</param>
/// <param name="privateKey">平台分配的私钥</param>
/// <param name="priKeyFromFile">如果设置 <c>true</c> 从文件中加载私钥</param>
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile)
: this(url, appId, privateKey, priKeyFromFile, DEFAULT_CONFIG)
{
}
/// <summary>
/// 构建请求客户端
/// </summary>
/// <param name="url">接口url</param>
/// <param name="appId">平台分配的appId</param>
/// <param name="privateKey">平台分配的私钥</param>
/// <param name="priKeyFromFile">如果设置 <c>true</c> 从文件中加载私钥</param>
/// <param name="publicKeyPlatform">平台分配的公钥</param>
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile, string publicKeyPlatform)
: this(url, appId, privateKey, priKeyFromFile)
{
this.publicKeyPlatform = publicKeyPlatform;
}
/// <summary>
/// 构建请求客户端
/// </summary>
/// <param name="url">接口url</param>
/// <param name="appId">平台分配的appId</param>
/// <param name="privateKey">平台分配的私钥</param>
/// <param name="priKeyFromFile">如果设置 <c>true</c> 从文件中加载私钥</param>
/// <param name="openConfig">配置项</param>
public OpenClient(string url, string appId, string privateKey,bool priKeyFromFile, OpenConfig openConfig)
{
this.url = url;
@ -75,6 +137,15 @@ namespace SDKCSharp.Client
this.dataNameBuilder = openConfig.DataNameBuilder;
}
/// <summary>
/// 构建请求客户端
/// </summary>
/// <param name="url">接口url</param>
/// <param name="appId">平台分配的appId</param>
/// <param name="privateKey">平台分配的私钥</param>
/// <param name="priKeyFromFile">如果设置 <c>true</c> 从文件中加载私钥</param>
/// <param name="publicKeyPlatform">平台分配的公钥</param>
/// <param name="openConfig">配置项</param>
public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile, string publicKeyPlatform, OpenConfig openConfig)
: this(url, appId, privateKey, priKeyFromFile, openConfig)
{
@ -203,6 +274,14 @@ namespace SDKCSharp.Client
}
}
/// <summary>
/// 构建业务json内容。
/// 假设返回的结果是:{"alipay_story_get_response":{"msg":"Success","code":"10000","name":"海底小纵队","id":1},"sign":"xxx"}
/// 将解析得到:{"msg":"Success","code":"10000","name":"海底小纵队","id":1}
/// </summary>
/// <returns>The biz json.</returns>
/// <param name="rootNodeName">根节点名称.</param>
/// <param name="body">返回内容.</param>
protected virtual string BuildBizJson(string rootNodeName, string body)
{
int indexOfRootNode = body.IndexOf(rootNodeName);
@ -219,6 +298,14 @@ namespace SDKCSharp.Client
return result;
}
/// <summary>
/// 获取业务结果,如下结果:{"alipay_story_get_response":{"msg":"Success","code":"10000","name":"海底小纵队","id":1},"sign":"xxx"}
/// 将返回:{"msg":"Success","code":"10000","name":"海底小纵队","id":1}
/// </summary>
/// <returns>The json node data.</returns>
/// <param name="body">Body.</param>
/// <param name="rootNode">Root node.</param>
/// <param name="indexOfRootNode">Index of root node.</param>
protected virtual string BuildJsonNodeData(string body, string rootNode, int indexOfRootNode)
{
int signDataStartIndex = indexOfRootNode + rootNode.Length + 2;

@ -13,6 +13,7 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.ByteArrayOutputStream;
@ -51,14 +52,17 @@ public class HttpTool {
protected void initHttpClient(HttpToolConfig httpToolConfig) {
httpClient = new OkHttpClient.Builder()
.connectTimeout(httpToolConfig.connectTimeoutSeconds, TimeUnit.SECONDS) // 设置链接超时时间,默认10秒
// 设置链接超时时间,默认10秒
.connectTimeout(httpToolConfig.connectTimeoutSeconds, TimeUnit.SECONDS)
.readTimeout(httpToolConfig.readTimeoutSeconds, TimeUnit.SECONDS)
.writeTimeout(httpToolConfig.writeTimeoutSeconds, TimeUnit.SECONDS)
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
cookieStore.put(httpUrl.host(), list);
}
@Override
public List<Cookie> loadForRequest(HttpUrl httpUrl) {
List<Cookie> cookies = cookieStore.get(httpUrl.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
@ -204,22 +208,24 @@ public class HttpTool {
* @return
* @throws IOException
*/
public String requestFile(String url, Map<String, String> form, Map<String, String> header, List<UploadFile> files)
public String requestFile(String url, Map<String, ?> form, Map<String, String> header, List<UploadFile> files)
throws IOException {
// 创建MultipartBody.Builder,用于添加请求的数据
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
bodyBuilder.setType(MultipartBody.FORM);
for (UploadFile uploadFile : files) {
bodyBuilder.addFormDataPart(uploadFile.getName(), // 请求的名字
uploadFile.getFileName(), // 文件的文字,服务器端用来解析的
RequestBody.create(null, uploadFile.getFileData()) // 创建RequestBody,把上传的文件放入
// 请求的名字
bodyBuilder.addFormDataPart(uploadFile.getName(),
// 文件的文字,服务器端用来解析的
uploadFile.getFileName(),
// 创建RequestBody,把上传的文件放入
RequestBody.create(null, uploadFile.getFileData())
);
}
Set<Map.Entry<String, String>> entrySet = form.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
for (Map.Entry<String, ?> entry : form.entrySet()) {
bodyBuilder.addFormDataPart(entry.getKey(), String.valueOf(entry.getValue()));
}
RequestBody requestBody = bodyBuilder.build();
@ -238,6 +244,31 @@ public class HttpTool {
}
}
/**
* 下载文件
*
* @param url 请求url
* @param form 请求数据
* @param header header
* @return 返回文件流
* @throws IOException
*/
public InputStream downloadFile(String url, Map<String, ?> form, Map<String, String> header) throws IOException {
Request.Builder requestBuilder = buildRequestBuilder(url, form, HTTPMethod.GET);
// 添加header
addHeader(requestBuilder, header);
Request request = requestBuilder.build();
Response response = httpClient
.newCall(request)
.execute();
if (response.isSuccessful()) {
ResponseBody body = response.body();
return body == null ? null : body.byteStream();
}
return null;
}
private void addHeader(Request.Builder builder, Map<String, String> header) {
if (header != null) {
Set<Map.Entry<String, String>> entrySet = header.entrySet();
@ -256,10 +287,15 @@ public class HttpTool {
}
public enum HTTPMethod {
/** http GET */
GET,
/** http POST */
POST,
/** http PUT */
PUT,
/** http HEAD */
HEAD,
/** http DELETE */
DELETE;
private HTTPMethod() {

@ -0,0 +1,67 @@
package com.gitee.sop.test;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.test.alipay.AlipayApiException;
import com.gitee.sop.test.alipay.AlipaySignature;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 文件下载
* @author tanghc
*/
public class DownloadTest extends TestBase{
String url = "http://localhost:8081/api"; // zuul
String appId = "2019032617262200001";
// 支付宝私钥
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
public void testDownload() throws AlipayApiException, IOException {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "story.download");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, String> bizContent = new HashMap<>();
bizContent.put("id", "1");
bizContent.put("name", "葫芦娃");
params.put("biz_content", JSON.toJSONString(bizContent));
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("----------- 返回结果 -----------");
InputStream fileInputStream = download(url, params);
String fileContent = IOUtils.toString(fileInputStream, StandardCharsets.UTF_8);
// 输出文件内容
System.out.println("文件内容:");
System.out.println(fileContent);
// 写文件到本地
//FileUtils.copyInputStreamToFile(fileInputStream, new File("D:/a.txt"));
}
}

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
@ -50,6 +51,20 @@ public class TestBase extends TestCase {
}
}
/**
* 下载文件
* @param url
* @param params
* @return 返回文件流
*/
public static InputStream download(String url, Map<String, String> params) {
try {
return httpTool.downloadFile(url, params, null);
} catch (IOException e) {
throw new RuntimeException("网络请求异常", e);
}
}
protected static String buildParamQuery(Map<String, String> params) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {

@ -119,6 +119,7 @@ public class SandboxController {
result.apiResult = responseData;
return result;
} catch (Exception e) {
log.error("请求失败", e);
throw new RuntimeException("请求失败");
}
}

@ -8,7 +8,6 @@ import com.gitee.sop.websiteserver.bean.DocModule;
import com.gitee.sop.websiteserver.bean.DocParameter;
import com.gitee.sop.websiteserver.bean.DocParserContext;
import com.google.common.collect.Sets;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
@ -138,27 +137,33 @@ public class SwaggerDocParser implements DocParser {
})
.collect(Collectors.groupingBy(DocParameter::getModule));
collect.entrySet().stream()
.forEach(entry -> {
DocParameter moduleDoc = new DocParameter();
moduleDoc.setName(entry.getKey());
moduleDoc.setType("object");
moduleDoc.setRefs(entry.getValue());
docParameterList.add(moduleDoc);
});
collect.forEach((key, value) -> {
DocParameter moduleDoc = new DocParameter();
moduleDoc.setName(key);
moduleDoc.setType("object");
moduleDoc.setRefs(value);
docParameterList.add(moduleDoc);
});
List<DocParameter> ret = docParameterList.stream()
return docParameterList.stream()
.filter(docParameter -> !docParameter.getName().contains("."))
.collect(Collectors.toList());
return ret;
}
protected List<DocParameter> buildResponseParameterList(JSONObject docInfo, JSONObject docRoot) {
String responseRef = getResponseRef(docInfo);
RefInfo refInfo = getResponseRefInfo(docInfo);
List<DocParameter> respParameterList = Collections.emptyList();
if (StringUtils.isNotBlank(responseRef)) {
if (refInfo != null) {
String responseRef = refInfo.ref;
respParameterList = this.buildDocParameters(responseRef, docRoot);
// 如果返回数组
if (refInfo.isArray) {
DocParameter docParameter = new DocParameter();
docParameter.setName("items");
docParameter.setType("array");
docParameter.setRefs(respParameterList);
respParameterList = Collections.singletonList(docParameter);
}
}
return respParameterList;
}
@ -170,42 +175,101 @@ public class SwaggerDocParser implements DocParser {
List<DocParameter> docParameterList = new ArrayList<>();
for (String fieldName : fieldNames) {
/*
{
"description": "分类故事",
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
{
"description": "分类故事",
"$ref": "#/definitions/StoryVO"
}
*/
JSONObject fieldInfo = properties.getJSONObject(fieldName);
DocParameter respParam = fieldInfo.toJavaObject(DocParameter.class);
respParam.setName(fieldName);
docParameterList.add(respParam);
String originalRef = isArray(fieldInfo) ? getRef(fieldInfo.getJSONObject(fieldName)) : getRef(fieldInfo);
if (StringUtils.isNotBlank(originalRef)) {
List<DocParameter> refs = buildDocParameters(originalRef, docRoot);
respParam.setRefs(refs);
DocParameter docParameter = fieldInfo.toJavaObject(DocParameter.class);
docParameter.setName(fieldName);
docParameterList.add(docParameter);
RefInfo refInfo = this.getRefInfo(fieldInfo);
if (refInfo != null) {
List<DocParameter> refs = buildDocParameters(refInfo.ref, docRoot);
docParameter.setRefs(refs);
}
}
return docParameterList;
}
protected boolean isArray(JSONObject fieldInfo) {
return "array".equalsIgnoreCase(fieldInfo.getString("type"));
}
private String getRef(JSONObject fieldInfo) {
return Optional.ofNullable(fieldInfo)
.map(jsonObject -> jsonObject.getString("originalRef"))
/**
* 简单对象返回
* "responses": {
* "200": {
* "description": "OK",
* "schema": {
* "$ref": "#/definitions/FileUploadVO"
* }
* },
* "401": {
* "description": "Unauthorized"
* },
* "403": {
* "description": "Forbidden"
* },
* "404": {
* "description": "Not Found"
* }
* }
* 纯数组返回
* "responses": {
* "200": {
* "description": "OK",
* "schema": {
* "type": "array",
* "items": {
* "$ref": "#/definitions/StoryVO"
* }
* }
* },
* "401": {
* "description": "Unauthorized"
* },
* "403": {
* "description": "Forbidden"
* },
* "404": {
* "description": "Not Found"
* }
* }
* @param docInfo
* @return
*/
protected RefInfo getResponseRefInfo(JSONObject docInfo) {
return Optional.ofNullable(docInfo.getJSONObject("responses"))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("200")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("schema")))
.map(this::getRefInfo)
.orElse(null);
}
protected String getResponseRef(JSONObject docInfo) {
String ref = Optional.ofNullable(docInfo.getJSONObject("responses"))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("200")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("schema")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getString("originalRef")))
.orElse("");
return ref;
private RefInfo getRefInfo(JSONObject jsonObject) {
String ref;
boolean isArray = "array".equals(jsonObject.getString("type"));
if (isArray) {
ref = jsonObject.getJSONObject("items").getString("$ref");
} else {
// #/definitions/Category
ref = jsonObject.getString("$ref");
}
if (ref == null) {
return null;
}
int index = ref.lastIndexOf("/");
if (index > -1) {
ref = ref.substring(index + 1);
}
RefInfo refInfo = new RefInfo();
refInfo.isArray = isArray;
refInfo.ref = ref;
return refInfo;
}
private static class RefInfo {
private boolean isArray;
private String ref;
}
}

@ -6,7 +6,7 @@
"title": "故事API",
"termsOfService": "文档"
},
"host": "10.1.30.157:2222",
"host": "localhost:2222",
"basePath": "/",
"tags": [
{
@ -24,13 +24,16 @@
],
"paths": {
"/alipay.category.get/": {
"get": {
"post": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingGET",
"operationId": "getCategoryUsingPOST",
"consumes": [
"application/json"
],
"produces": [
"*/*"
],
@ -74,10 +77,12 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/Category"
}
},
"201": {
"description": "Created"
},
"401": {
"description": "Unauthorized"
},
@ -91,62 +96,96 @@
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_version": "1.0"
},
"head": {
}
},
"/alipay.story.find/": {
"get": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingHEAD",
"consumes": [
"application/json"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingGET",
"produces": [
"*/*"
],
"parameters": [
{
"name": "categoryName",
"name": "id",
"in": "query",
"description": "分类名称",
"description": "故事ID",
"required": false,
"type": "string",
"x-example": "娱乐"
"type": "integer",
"format": "int32",
"x-example": 111
},
{
"name": "story.gmt_create",
"name": "name",
"in": "query",
"description": "创建时间",
"required": false,
"description": "故事名称",
"required": true,
"type": "string",
"format": "date-time",
"x-example": "2019-04-14 19:02:12"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO"
}
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Forbidden"
},
"404": {
"description": "Not Found"
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_version": "1.0"
},
"head": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingHEAD",
"consumes": [
"application/json"
],
"produces": [
"*/*"
],
"parameters": [
{
"name": "story.id",
"name": "id",
"in": "query",
"description": "故事ID",
"required": false,
"type": "integer",
"format": "int64",
"x-example": 1
"format": "int32",
"x-example": 111
},
{
"name": "story.name",
"name": "name",
"in": "query",
"description": "故事名称",
"required": false,
"required": true,
"type": "string",
"x-example": "海底小纵队"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/StoryVO"
}
},
"204": {
@ -160,16 +199,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_name": "alipay.story.find",
"sop_version": "1.0"
},
"post": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingPOST",
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingPOST",
"consumes": [
"application/json"
],
@ -178,46 +217,28 @@
],
"parameters": [
{
"name": "categoryName",
"in": "query",
"description": "分类名称",
"required": false,
"type": "string",
"x-example": "娱乐"
},
{
"name": "story.gmt_create",
"in": "query",
"description": "创建时间",
"required": false,
"type": "string",
"format": "date-time",
"x-example": "2019-04-14 19:02:12"
},
{
"name": "story.id",
"name": "id",
"in": "query",
"description": "故事ID",
"required": false,
"type": "integer",
"format": "int64",
"x-example": 1
"format": "int32",
"x-example": 111
},
{
"name": "story.name",
"name": "name",
"in": "query",
"description": "故事名称",
"required": false,
"required": true,
"type": "string",
"x-example": "海底小纵队"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/StoryVO"
}
},
"201": {
@ -234,16 +255,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_name": "alipay.story.find",
"sop_version": "1.0"
},
"put": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingPUT",
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingPUT",
"consumes": [
"application/json"
],
@ -252,46 +273,28 @@
],
"parameters": [
{
"name": "categoryName",
"in": "query",
"description": "分类名称",
"required": false,
"type": "string",
"x-example": "娱乐"
},
{
"name": "story.gmt_create",
"in": "query",
"description": "创建时间",
"required": false,
"type": "string",
"format": "date-time",
"x-example": "2019-04-14 19:02:12"
},
{
"name": "story.id",
"name": "id",
"in": "query",
"description": "故事ID",
"required": false,
"type": "integer",
"format": "int64",
"x-example": 1
"format": "int32",
"x-example": 111
},
{
"name": "story.name",
"name": "name",
"in": "query",
"description": "故事名称",
"required": false,
"required": true,
"type": "string",
"x-example": "海底小纵队"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/StoryVO"
}
},
"201": {
@ -308,61 +311,43 @@
}
},
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_name": "alipay.story.find",
"sop_version": "1.0"
},
"delete": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingDELETE",
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingDELETE",
"produces": [
"*/*"
],
"parameters": [
{
"name": "categoryName",
"in": "query",
"description": "分类名称",
"required": false,
"type": "string",
"x-example": "娱乐"
},
{
"name": "story.gmt_create",
"in": "query",
"description": "创建时间",
"required": false,
"type": "string",
"format": "date-time",
"x-example": "2019-04-14 19:02:12"
},
{
"name": "story.id",
"name": "id",
"in": "query",
"description": "故事ID",
"required": false,
"type": "integer",
"format": "int64",
"x-example": 1
"format": "int32",
"x-example": 111
},
{
"name": "story.name",
"name": "name",
"in": "query",
"description": "故事名称",
"required": false,
"required": true,
"type": "string",
"x-example": "海底小纵队"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/StoryVO"
}
},
"204": {
@ -376,16 +361,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_name": "alipay.story.find",
"sop_version": "1.0"
},
"options": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingOPTIONS",
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingOPTIONS",
"consumes": [
"application/json"
],
@ -394,46 +379,28 @@
],
"parameters": [
{
"name": "categoryName",
"in": "query",
"description": "分类名称",
"required": false,
"type": "string",
"x-example": "娱乐"
},
{
"name": "story.gmt_create",
"in": "query",
"description": "创建时间",
"required": false,
"type": "string",
"format": "date-time",
"x-example": "2019-04-14 19:02:12"
},
{
"name": "story.id",
"name": "id",
"in": "query",
"description": "故事ID",
"required": false,
"type": "integer",
"format": "int64",
"x-example": 1
"format": "int32",
"x-example": 111
},
{
"name": "story.name",
"name": "name",
"in": "query",
"description": "故事名称",
"required": false,
"required": true,
"type": "string",
"x-example": "海底小纵队"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/StoryVO"
}
},
"204": {
@ -447,16 +414,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_name": "alipay.story.find",
"sop_version": "1.0"
},
"patch": {
"tags": [
"故事接口"
],
"summary": "获取分类信息",
"description": "演示表格树",
"operationId": "getCategoryUsingPATCH",
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingPATCH",
"consumes": [
"application/json"
],
@ -465,46 +432,28 @@
],
"parameters": [
{
"name": "categoryName",
"in": "query",
"description": "分类名称",
"required": false,
"type": "string",
"x-example": "娱乐"
},
{
"name": "story.gmt_create",
"in": "query",
"description": "创建时间",
"required": false,
"type": "string",
"format": "date-time",
"x-example": "2019-04-14 19:02:12"
},
{
"name": "story.id",
"name": "id",
"in": "query",
"description": "故事ID",
"required": false,
"type": "integer",
"format": "int64",
"x-example": 1
"format": "int32",
"x-example": 111
},
{
"name": "story.name",
"name": "name",
"in": "query",
"description": "故事名称",
"required": false,
"required": true,
"type": "string",
"x-example": "海底小纵队"
"x-example": "白雪公主"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Category",
"originalRef": "Category"
"$ref": "#/definitions/StoryVO"
}
},
"204": {
@ -518,18 +467,18 @@
}
},
"deprecated": false,
"sop_name": "alipay.category.get",
"sop_name": "alipay.story.find",
"sop_version": "1.0"
}
},
"/alipay.story.find/": {
"/alipay.story.find2/": {
"get": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingGET",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingGET",
"produces": [
"*/*"
],
@ -556,8 +505,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"401": {
@ -571,16 +522,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
},
"head": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingHEAD",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingHEAD",
"consumes": [
"application/json"
],
@ -610,8 +561,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"204": {
@ -625,16 +578,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
},
"post": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingPOST",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingPOST",
"consumes": [
"application/json"
],
@ -664,8 +617,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"201": {
@ -682,16 +637,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
},
"put": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingPUT",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingPUT",
"consumes": [
"application/json"
],
@ -721,8 +676,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"201": {
@ -739,16 +696,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
},
"delete": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingDELETE",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingDELETE",
"produces": [
"*/*"
],
@ -775,8 +732,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"204": {
@ -790,16 +749,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
},
"options": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingOPTIONS",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingOPTIONS",
"consumes": [
"application/json"
],
@ -829,8 +788,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"204": {
@ -844,16 +805,16 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
},
"patch": {
"tags": [
"故事接口"
],
"summary": "获取故事信息",
"description": "说明接口的详细信息,介绍,用途,注意事项等。",
"operationId": "getStory2UsingPATCH",
"summary": "返回数组结果",
"description": "返回数组结果",
"operationId": "getStory3UsingPATCH",
"consumes": [
"application/json"
],
@ -883,8 +844,10 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"type": "array",
"items": {
"$ref": "#/definitions/StoryVO"
}
}
},
"204": {
@ -898,7 +861,7 @@
}
},
"deprecated": false,
"sop_name": "alipay.story.find",
"sop_name": "alipay.story.find2",
"sop_version": "1.0"
}
},
@ -939,8 +902,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"401": {
@ -996,8 +958,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1053,8 +1014,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"201": {
@ -1113,8 +1073,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"201": {
@ -1170,8 +1129,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1227,8 +1185,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1284,8 +1241,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1326,8 +1282,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"401": {
@ -1370,8 +1325,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1414,8 +1368,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"201": {
@ -1461,8 +1414,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"201": {
@ -1505,8 +1457,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1549,8 +1500,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1593,8 +1543,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/FileUploadVO",
"originalRef": "FileUploadVO"
"$ref": "#/definitions/FileUploadVO"
}
},
"204": {
@ -1628,8 +1577,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"401": {
@ -1663,8 +1611,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"204": {
@ -1698,8 +1645,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"201": {
@ -1736,8 +1682,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"201": {
@ -1771,8 +1716,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"204": {
@ -1806,8 +1750,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"204": {
@ -1841,8 +1784,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/Story",
"originalRef": "Story"
"$ref": "#/definitions/Story"
}
},
"204": {
@ -1872,8 +1814,7 @@
},
"story": {
"description": "分类故事",
"$ref": "#/definitions/StoryVO",
"originalRef": "StoryVO"
"$ref": "#/definitions/StoryVO"
}
},
"title": "Category"
@ -1900,8 +1841,7 @@
"files": {
"type": "array",
"items": {
"$ref": "#/definitions/FileMeta",
"originalRef": "FileMeta"
"$ref": "#/definitions/FileMeta"
}
}
},

Loading…
Cancel
Save