diff --git a/README.md b/README.md index 4f2e4e9c..271b7bd7 100644 --- a/README.md +++ b/README.md @@ -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)的调用方式。 diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java index 649f1a7f..0ed1ab9a 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java @@ -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_"; } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java index badbd26f..52e3d1d2 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java @@ -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(); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java index c24ef5d3..efb2c379 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java @@ -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 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); } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java index fff6b09a..23a023d8 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java @@ -96,6 +96,11 @@ public abstract class BaseExecutorAdapter implements ResultExecutor responseData.put(GATEWAY_CODE_NAME, ISV_MISSING_METHOD_META.getCode()); responseData.put(GATEWAY_MSG_NAME, ISV_MISSING_METHOD_META.getError().getCode()); } else { + Map 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} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java index a1a82b65..9d64b428 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java @@ -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); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java index bafb3767..76f07921 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PostResultFilter.java @@ -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> 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 resultExecutor = apiConfig.getZuulResultExecutor(); diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java index dcc20ae6..515b66e3 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java @@ -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 implements OpenContext { 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 implements OpenContext { 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); } } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java index 7ff9495f..37b51fb6 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/interceptor/ServiceContextInterceptor.java @@ -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(); diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java index cc0660e1..d3bae967 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java @@ -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 resolveNativeRequest(NativeWebRequest webRequest, Class 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 resolveNativeResponse(NativeWebRequest webRequest, Class 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 参数 diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java index b5e1ea49..58158a45 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java @@ -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 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 diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/DownloadController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/DownloadController.java new file mode 100644 index 00000000..88e2091b --- /dev/null +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/DownloadController.java @@ -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 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); + } +} diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/vo/FileUploadVO.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/vo/FileUploadVO.java index 6367cb08..fcfafad7 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/vo/FileUploadVO.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/vo/FileUploadVO.java @@ -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; } } diff --git a/sop-sdk/sdk-csharp/SDKCSharp/Client/OpenClient.cs b/sop-sdk/sdk-csharp/SDKCSharp/Client/OpenClient.cs index 427db732..ae011f1e 100644 --- a/sop-sdk/sdk-csharp/SDKCSharp/Client/OpenClient.cs +++ b/sop-sdk/sdk-csharp/SDKCSharp/Client/OpenClient.cs @@ -20,46 +20,108 @@ namespace SDKCSharp.Client /// public class OpenClient { - + /// + /// 默认配置 + /// private static OpenConfig DEFAULT_CONFIG = new OpenConfig(); private Dictionary header = new Dictionary(); - + /// + /// 接口请求url + /// private string url; + + /// + /// 平台提供的appId + /// private string appId; + + /// + /// 开放平台提供的私钥 + /// private string privateKey; + + /// + /// 开放平台提供的公钥 + /// private string publicKeyPlatform; + /// + /// 配置项 + /// private OpenConfig openConfig; + + /// + /// 请求对象 + /// private OpenRequest openRequest; - private DataNameBuilder dataNameBuilder; + /// + /// 节点处理 + /// + private DataNameBuilder dataNameBuilder; + /// + /// 构建请求客户端 + /// + /// 接口url + /// 平台分配的appId + /// 平台分配的私钥 public OpenClient(string url, string appId, string privateKey) : this(url, appId, privateKey,false, DEFAULT_CONFIG) { } + /// + /// 构建请求客户端 + /// + /// 接口url + /// 平台分配的appId + /// 平台分配的私钥 + /// 平台分配的公钥 public OpenClient(string url, string appId, string privateKey, string publicKeyPlatform) : this(url, appId, privateKey) { this.publicKeyPlatform = publicKeyPlatform; } + /// + /// 构建请求客户端 + /// + /// 接口url + /// 平台分配的appId + /// 平台分配的私钥 + /// 如果设置 true 从文件中加载私钥 public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile) : this(url, appId, privateKey, priKeyFromFile, DEFAULT_CONFIG) { } + /// + /// 构建请求客户端 + /// + /// 接口url + /// 平台分配的appId + /// 平台分配的私钥 + /// 如果设置 true 从文件中加载私钥 + /// 平台分配的公钥 public OpenClient(string url, string appId, string privateKey, bool priKeyFromFile, string publicKeyPlatform) : this(url, appId, privateKey, priKeyFromFile) { this.publicKeyPlatform = publicKeyPlatform; } + /// + /// 构建请求客户端 + /// + /// 接口url + /// 平台分配的appId + /// 平台分配的私钥 + /// 如果设置 true 从文件中加载私钥 + /// 配置项 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; } + /// + /// 构建请求客户端 + /// + /// 接口url + /// 平台分配的appId + /// 平台分配的私钥 + /// 如果设置 true 从文件中加载私钥 + /// 平台分配的公钥 + /// 配置项 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 } } + /// + /// 构建业务json内容。 + /// 假设返回的结果是:{"alipay_story_get_response":{"msg":"Success","code":"10000","name":"海底小纵队","id":1},"sign":"xxx"} + /// 将解析得到:{"msg":"Success","code":"10000","name":"海底小纵队","id":1} + /// + /// The biz json. + /// 根节点名称. + /// 返回内容. protected virtual string BuildBizJson(string rootNodeName, string body) { int indexOfRootNode = body.IndexOf(rootNodeName); @@ -219,6 +298,14 @@ namespace SDKCSharp.Client return result; } + /// + /// 获取业务结果,如下结果:{"alipay_story_get_response":{"msg":"Success","code":"10000","name":"海底小纵队","id":1},"sign":"xxx"} + /// 将返回:{"msg":"Success","code":"10000","name":"海底小纵队","id":1} + /// + /// The json node data. + /// Body. + /// Root node. + /// Index of root node. protected virtual string BuildJsonNodeData(string body, string rootNode, int indexOfRootNode) { int signDataStartIndex = indexOfRootNode + rootNode.Length + 2; diff --git a/sop-test/src/main/java/com/gitee/sop/test/HttpTool.java b/sop-test/src/main/java/com/gitee/sop/test/HttpTool.java index 39025dc0..671347dc 100644 --- a/sop-test/src/main/java/com/gitee/sop/test/HttpTool.java +++ b/sop-test/src/main/java/com/gitee/sop/test/HttpTool.java @@ -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 list) { cookieStore.put(httpUrl.host(), list); } + @Override public List loadForRequest(HttpUrl httpUrl) { List cookies = cookieStore.get(httpUrl.host()); return cookies != null ? cookies : new ArrayList(); @@ -204,22 +208,24 @@ public class HttpTool { * @return * @throws IOException */ - public String requestFile(String url, Map form, Map header, List files) + public String requestFile(String url, Map form, Map header, List 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> entrySet = form.entrySet(); - for (Map.Entry entry : entrySet) { - bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue()); + for (Map.Entry 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 form, Map 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 header) { if (header != null) { Set> 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() { diff --git a/sop-test/src/test/java/com/gitee/sop/test/DownloadTest.java b/sop-test/src/test/java/com/gitee/sop/test/DownloadTest.java new file mode 100644 index 00000000..84ebf9bb --- /dev/null +++ b/sop-test/src/test/java/com/gitee/sop/test/DownloadTest.java @@ -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 params = new HashMap(); + 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 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")); + } + +} diff --git a/sop-test/src/test/java/com/gitee/sop/test/TestBase.java b/sop-test/src/test/java/com/gitee/sop/test/TestBase.java index bc34209c..adbb3538 100644 --- a/sop-test/src/test/java/com/gitee/sop/test/TestBase.java +++ b/sop-test/src/test/java/com/gitee/sop/test/TestBase.java @@ -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 params) { + try { + return httpTool.downloadFile(url, params, null); + } catch (IOException e) { + throw new RuntimeException("网络请求异常", e); + } + } + protected static String buildParamQuery(Map params) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : params.entrySet()) { diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java index 794770d4..25a20024 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java @@ -119,6 +119,7 @@ public class SandboxController { result.apiResult = responseData; return result; } catch (Exception e) { + log.error("请求失败", e); throw new RuntimeException("请求失败"); } } diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java index 7435df42..6bf8a1cc 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java @@ -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 ret = docParameterList.stream() + return docParameterList.stream() .filter(docParameter -> !docParameter.getName().contains(".")) .collect(Collectors.toList()); - - return ret; } protected List buildResponseParameterList(JSONObject docInfo, JSONObject docRoot) { - String responseRef = getResponseRef(docInfo); + RefInfo refInfo = getResponseRefInfo(docInfo); List 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 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 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 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; } } diff --git a/sop-website/website-server/src/main/resources/api.json b/sop-website/website-server/src/main/resources/api.json index 1c5ad911..9a298be3 100644 --- a/sop-website/website-server/src/main/resources/api.json +++ b/sop-website/website-server/src/main/resources/api.json @@ -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" } } },