mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
Merge branch 'develop'
# Conflicts: # sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
## 1.4.0
|
||||||
|
|
||||||
|
- 新增文档分组显示
|
||||||
|
- 支持easyopen文档注解
|
||||||
|
- BUG修复
|
||||||
|
|
||||||
## 1.3.0
|
## 1.3.0
|
||||||
|
|
||||||
- 新增接口限流功能 [doc](http://durcframework.gitee.io/sop/#/files/10092_%E6%8E%A5%E5%8F%A3%E9%99%90%E6%B5%81?t=1555378655699)
|
- 新增接口限流功能 [doc](http://durcframework.gitee.io/sop/#/files/10092_%E6%8E%A5%E5%8F%A3%E9%99%90%E6%B5%81?t=1555378655699)
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-common</artifactId>
|
<artifactId>sop-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-common</artifactId>
|
<artifactId>sop-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>sop-gateway-common</artifactId>
|
<artifactId>sop-gateway-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>sop-gateway-common</name>
|
<name>sop-gateway-common</name>
|
||||||
|
@@ -1,20 +1,35 @@
|
|||||||
package com.gitee.sop.gatewaycommon.easyopen;
|
package com.gitee.sop.gatewaycommon.easyopen;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.gitee.sop.gatewaycommon.message.Error;
|
import com.gitee.sop.gatewaycommon.message.Error;
|
||||||
import com.gitee.sop.gatewaycommon.result.ApiResult;
|
import com.gitee.sop.gatewaycommon.result.ApiResult;
|
||||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||||
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
|
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
|
||||||
import com.netflix.zuul.context.RequestContext;
|
import com.netflix.zuul.context.RequestContext;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class EasyopenResultExecutor implements ResultExecutor<RequestContext, String> {
|
public class EasyopenResultExecutor implements ResultExecutor<RequestContext, String> {
|
||||||
|
|
||||||
|
boolean onlyReturnData;
|
||||||
|
|
||||||
|
public EasyopenResultExecutor(boolean onlyReturnData) {
|
||||||
|
this.onlyReturnData = onlyReturnData;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mergeResult(RequestContext request, String serviceResult) {
|
public String mergeResult(RequestContext request, String serviceResult) {
|
||||||
|
if (onlyReturnData) {
|
||||||
|
JSONObject jsonObject = JSON.parseObject(serviceResult);
|
||||||
|
return Optional.ofNullable(jsonObject.getString("data")).orElse("{}");
|
||||||
|
} else {
|
||||||
return serviceResult;
|
return serviceResult;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String buildErrorResult(RequestContext request, Throwable ex) {
|
public String buildErrorResult(RequestContext request, Throwable ex) {
|
||||||
|
@@ -10,15 +10,29 @@ import com.gitee.sop.gatewaycommon.zuul.configuration.BaseZuulConfiguration;
|
|||||||
*/
|
*/
|
||||||
public class EasyopenZuulConfiguration extends BaseZuulConfiguration {
|
public class EasyopenZuulConfiguration extends BaseZuulConfiguration {
|
||||||
|
|
||||||
static {
|
public EasyopenZuulConfiguration() {
|
||||||
|
ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||||
|
if (compatibilityModel()) {
|
||||||
ParamNames.APP_KEY_NAME = "app_key";
|
ParamNames.APP_KEY_NAME = "app_key";
|
||||||
ParamNames.API_NAME = "name";
|
ParamNames.API_NAME = "name";
|
||||||
ParamNames.SIGN_TYPE_NAME = "sign_type";
|
ParamNames.SIGN_TYPE_NAME = "sign_type";
|
||||||
ParamNames.APP_AUTH_TOKEN_NAME = "access_token";
|
ParamNames.APP_AUTH_TOKEN_NAME = "access_token";
|
||||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
|
||||||
apiConfig.setSigner(new EasyopenSigner());
|
apiConfig.setSigner(new EasyopenSigner());
|
||||||
apiConfig.setZuulResultExecutor(new EasyopenResultExecutor());
|
apiConfig.setZuulResultExecutor(new EasyopenResultExecutor(false));
|
||||||
apiConfig.setMergeResult(false);
|
apiConfig.setMergeResult(false);
|
||||||
|
} else {
|
||||||
|
apiConfig.setZuulResultExecutor(new EasyopenResultExecutor(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是兼容模式
|
||||||
|
* @return 返回true,返回true可兼容之前的easyopen接口。
|
||||||
|
*/
|
||||||
|
public boolean compatibilityModel() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,15 +5,10 @@ import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
|
|||||||
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
|
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
|
||||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.curator.framework.CuratorFramework;
|
|
||||||
import org.apache.curator.framework.recipes.cache.ChildData;
|
import org.apache.curator.framework.recipes.cache.ChildData;
|
||||||
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
|
|
||||||
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
|
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
|
||||||
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
|
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 路由管理,采用zookeeper实现,监听路由的增删改,并适时更新到本地。路由的存储格式为:
|
* 路由管理,采用zookeeper实现,监听路由的增删改,并适时更新到本地。路由的存储格式为:
|
||||||
@@ -64,8 +59,10 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
|||||||
|
|
||||||
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
|
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
|
ZookeeperContext.setEnvironment(environment);
|
||||||
this.routeRepository = routeRepository;
|
this.routeRepository = routeRepository;
|
||||||
this.routeRootPath = ZookeeperContext.getRouteRootPath();
|
this.routeRootPath = ZookeeperContext.getRouteRootPath();
|
||||||
|
this.createRouteRootPath(this.routeRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,52 +72,44 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
|||||||
|
|
||||||
protected void refreshRouteInfo() {
|
protected void refreshRouteInfo() {
|
||||||
try {
|
try {
|
||||||
ZookeeperContext.createOrUpdateData(routeRootPath, "");
|
this.watchServiceChange(routeRootPath);
|
||||||
CuratorFramework client = ZookeeperContext.getClient();
|
|
||||||
this.watchServiceChange(client, routeRootPath);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("刷新路由配置失败", e);
|
log.error("刷新路由配置失败", e);
|
||||||
throw new IllegalStateException("刷新路由配置失败");
|
throw new IllegalStateException("刷新路由配置失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void createRouteRootPath(String routeRootPath) {
|
||||||
|
try {
|
||||||
|
ZookeeperContext.createPath(routeRootPath, "");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("创建路由根节点失败", e);
|
||||||
|
throw new IllegalStateException("创建路由根节点失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听微服务更改
|
* 监听微服务更改
|
||||||
*
|
*
|
||||||
* @param client
|
|
||||||
* @param rootPath
|
* @param rootPath
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected void watchServiceChange(CuratorFramework client, String rootPath) throws Exception {
|
protected void watchServiceChange(String rootPath) throws Exception {
|
||||||
// 为子节点添加watcher
|
ZookeeperContext.getChildrenAndListen(rootPath, childDataList -> {
|
||||||
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
|
||||||
PathChildrenCache childrenCache = new PathChildrenCache(client, rootPath, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* StartMode: 初始化方式
|
|
||||||
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
|
|
||||||
* NORMAL:异步初始化
|
|
||||||
* BUILD_INITIAL_CACHE:同步初始化
|
|
||||||
*/
|
|
||||||
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
|
||||||
|
|
||||||
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
|
|
||||||
List<ChildData> childDataList = childrenCache.getCurrentData();
|
|
||||||
log.info("========== 加载路由信息 ==========");
|
log.info("========== 加载路由信息 ==========");
|
||||||
log.info("{} # 根节点", rootPath);
|
log.info("{} # 路由根节点", rootPath);
|
||||||
for (ChildData childData : childDataList) {
|
for (ChildData childData : childDataList) {
|
||||||
String serviceNodeData = new String(childData.getData());
|
String serviceNodeData = new String(childData.getData());
|
||||||
R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass());
|
R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass());
|
||||||
String servicePath = childData.getPath();
|
String servicePath = childData.getPath();
|
||||||
log.info("\t{} # service节点,节点数据:{}", servicePath, serviceNodeData);
|
log.info("\t{} # service节点,节点数据:{}", servicePath, serviceNodeData);
|
||||||
this.loadServiceRouteItem(client, serviceRouteInfo, servicePath);
|
try {
|
||||||
|
this.loadServiceRouteItem(serviceRouteInfo, servicePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("加载路由信息失败,servicePath:{}", servicePath, e);
|
||||||
}
|
}
|
||||||
log.info("监听服务节点增删改,rootPath:{}", rootPath);
|
}
|
||||||
// 监听根节点下面的子节点
|
}, (client, event) -> {
|
||||||
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
|
|
||||||
@Override
|
|
||||||
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
|
|
||||||
PathChildrenCacheEvent.Type type = event.getType();
|
PathChildrenCacheEvent.Type type = event.getType();
|
||||||
synchronized (type) {
|
synchronized (type) {
|
||||||
// 通过判断event type的方式来实现不同事件的触发
|
// 通过判断event type的方式来实现不同事件的触发
|
||||||
@@ -130,7 +119,7 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
|||||||
// 添加子节点时触发
|
// 添加子节点时触发
|
||||||
String servicePath = event.getData().getPath();
|
String servicePath = event.getData().getPath();
|
||||||
log.info("新增serviceId节点:{},节点数据:{}", servicePath, serviceNodeData);
|
log.info("新增serviceId节点:{},节点数据:{}", servicePath, serviceNodeData);
|
||||||
loadServiceRouteItem(client, serviceRouteInfo, servicePath);
|
this.watchRouteItems(serviceRouteInfo, servicePath);
|
||||||
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
|
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
|
||||||
// 修改子节点数据时触发,暂时没有什么操作
|
// 修改子节点数据时触发,暂时没有什么操作
|
||||||
String nodeData = new String(event.getData().getData());
|
String nodeData = new String(event.getData().getData());
|
||||||
@@ -143,7 +132,6 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
|||||||
routeRepository.deleteAll(serviceRouteInfo.getServiceId());
|
routeRepository.deleteAll(serviceRouteInfo.getServiceId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,35 +140,30 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
|||||||
*
|
*
|
||||||
* @param servicePath
|
* @param servicePath
|
||||||
*/
|
*/
|
||||||
protected void loadServiceRouteItem(CuratorFramework client, R serviceRouteInfo, String servicePath) throws Exception {
|
protected void loadServiceRouteItem(R serviceRouteInfo, String servicePath) throws Exception {
|
||||||
// 获取service节点下所有的路由节点,里面保存的是路由名称,前面没有斜杠"/"
|
ZookeeperContext.getChildrenData(servicePath, childDataList -> {
|
||||||
List<String> pathNameList = client.getChildren().forPath(servicePath);
|
for (ChildData childData : childDataList) {
|
||||||
for (String pathName : pathNameList) {
|
String routeItemPath = childData.getPath();
|
||||||
// 完整的路径
|
byte[] routeItemData = childData.getData();
|
||||||
String routeItemPath = servicePath + "/" + pathName;
|
|
||||||
byte[] routeItemData = client.getData().forPath(routeItemPath);
|
|
||||||
String routeDataJson = buildZookeeperData(routeItemData);
|
String routeDataJson = buildZookeeperData(routeItemData);
|
||||||
log.info("\t\t{} # 路由节点,节点数据:{}", routeItemPath, routeDataJson);
|
log.info("\t\t{} # 路由节点,节点数据:{}", routeItemPath, routeDataJson);
|
||||||
this.saveRouteItem(serviceRouteInfo, routeDataJson);
|
this.saveRouteItem(serviceRouteInfo, routeDataJson);
|
||||||
}
|
}
|
||||||
this.watchRouteItems(client, serviceRouteInfo, servicePath);
|
});
|
||||||
|
this.watchRouteItems(serviceRouteInfo, servicePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听serviceId目录下面的子节点
|
* 监听serviceId目录下面的子节点
|
||||||
*
|
*
|
||||||
* @param client
|
|
||||||
* @param serviceRouteInfo
|
* @param serviceRouteInfo
|
||||||
* @param servicePath serviceId节点
|
* @param servicePath serviceId节点
|
||||||
*/
|
*/
|
||||||
protected void watchRouteItems(CuratorFramework client, R serviceRouteInfo, String servicePath) throws Exception {
|
protected void watchRouteItems(R serviceRouteInfo, String servicePath) throws Exception {
|
||||||
log.info("监听路由节点增删改,servicePath:{}", servicePath);
|
log.info("监听{}下子节点增删改", servicePath);
|
||||||
PathChildrenCache childrenCache = new PathChildrenCache(client, servicePath, true);
|
|
||||||
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
|
||||||
// 添加事件监听器
|
// 添加事件监听器
|
||||||
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
|
ZookeeperContext.listenChildren(servicePath, (client, event) -> {
|
||||||
@Override
|
|
||||||
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
|
|
||||||
PathChildrenCacheEvent.Type type = event.getType();
|
PathChildrenCacheEvent.Type type = event.getType();
|
||||||
synchronized (type) {
|
synchronized (type) {
|
||||||
// 通过判断event type的方式来实现不同事件的触发
|
// 通过判断event type的方式来实现不同事件的触发
|
||||||
@@ -201,7 +184,6 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
|||||||
deleteRouteItem(serviceRouteInfo, routeDataJson);
|
deleteRouteItem(serviceRouteInfo, routeDataJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,19 +9,17 @@ import org.apache.curator.framework.recipes.cache.ChildData;
|
|||||||
import org.apache.curator.framework.recipes.cache.NodeCache;
|
import org.apache.curator.framework.recipes.cache.NodeCache;
|
||||||
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
|
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
|
||||||
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
|
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
|
||||||
|
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
|
||||||
import org.apache.curator.retry.ExponentialBackoffRetry;
|
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||||
import org.apache.zookeeper.data.Stat;
|
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_MSG_CHANNEL_PATH;
|
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_MSG_CHANNEL_PATH;
|
||||||
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_ROUTE_PATH;
|
|
||||||
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_ROUTE_PERMISSION_PATH;
|
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_ROUTE_PERMISSION_PATH;
|
||||||
|
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_ROUTE_PATH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
@@ -86,46 +84,9 @@ public class ZookeeperContext {
|
|||||||
ZookeeperContext.client = client;
|
ZookeeperContext.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPathExist(String path) {
|
|
||||||
try {
|
|
||||||
return client.checkExists().forPath(path) != null;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对已存在的path赋值。如果path不存在抛异常
|
|
||||||
*
|
|
||||||
* @param path 已存在的
|
|
||||||
* @param data
|
|
||||||
* @return 返回Stat
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static Stat updatePathData(String path, String data) throws Exception {
|
|
||||||
if (!isPathExist(path)) {
|
|
||||||
throw new IllegalStateException("path " + path + " 不存在");
|
|
||||||
}
|
|
||||||
return getClient().setData().forPath(path, data.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建新的path,并赋值。如果path已存在抛异常
|
|
||||||
* @param path 待创建的path
|
|
||||||
* @param data 值
|
|
||||||
*/
|
|
||||||
public static String createNewData(String path, String data) throws Exception {
|
|
||||||
if (isPathExist(path)) {
|
|
||||||
throw new IllegalStateException("path " + path + " 已存在");
|
|
||||||
}
|
|
||||||
return getClient().create()
|
|
||||||
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
|
||||||
.creatingParentContainersIfNeeded()
|
|
||||||
.forPath(path, data.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新建或保存节点
|
* 新建或保存节点
|
||||||
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* @param data
|
* @param data
|
||||||
* @return 返回path
|
* @return 返回path
|
||||||
@@ -140,8 +101,35 @@ public class ZookeeperContext {
|
|||||||
.forPath(path, data.getBytes());
|
.forPath(path, data.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建path,如果path存在不报错,静默返回path名称
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static String createPath(String path, String data) throws Exception {
|
||||||
|
if (isPathExist(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return getClient().create()
|
||||||
|
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
||||||
|
.creatingParentContainersIfNeeded()
|
||||||
|
.forPath(path, data.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPathExist(String path) {
|
||||||
|
try {
|
||||||
|
return client.checkExists().forPath(path) != null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听一个节点
|
* 监听一个节点
|
||||||
|
*
|
||||||
* @param path
|
* @param path
|
||||||
* @param onChange 节点修改后触发
|
* @param onChange 节点修改后触发
|
||||||
* @return 返回path
|
* @return 返回path
|
||||||
@@ -160,39 +148,82 @@ public class ZookeeperContext {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getData(String path) throws Exception {
|
/**
|
||||||
if (!isPathExist(path)) {
|
* 获取子节点信息并监听子节点
|
||||||
return null;
|
*
|
||||||
}
|
* @param parentPath 父节点路径
|
||||||
byte[] data = getClient().getData().forPath(path);
|
* @param listConsumer 子节点数据
|
||||||
return new String(data);
|
* @param listener 监听事件
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static void getChildrenAndListen(String parentPath, Consumer<List<ChildData>> listConsumer, PathChildrenCacheListener listener) throws Exception {
|
||||||
|
// 为子节点添加watcher
|
||||||
|
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
||||||
|
PathChildrenCache childrenCache = new PathChildrenCache(client, parentPath, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StartMode: 初始化方式
|
||||||
|
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
|
||||||
|
* NORMAL:异步初始化
|
||||||
|
* BUILD_INITIAL_CACHE:同步初始化
|
||||||
|
*/
|
||||||
|
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
||||||
|
|
||||||
|
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
|
||||||
|
List<ChildData> childDataList = childrenCache.getCurrentData();
|
||||||
|
listConsumer.accept(childDataList);
|
||||||
|
log.info("监听子节点增删改,监听路径:{}", parentPath);
|
||||||
|
// 监听根节点下面的子节点
|
||||||
|
childrenCache.getListenable().addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子节点数据
|
* 获取子节点信息
|
||||||
*
|
*
|
||||||
* @param parentPath 父节点
|
* @param parentPath 父节点路径
|
||||||
* @return 返回子节点数据
|
* @param listConsumer 子节点数据
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static List<ChildData> getChildrenData(String parentPath) throws Exception {
|
public static void getChildrenData(String parentPath, Consumer<List<ChildData>> listConsumer) throws Exception {
|
||||||
PathChildrenCache pathChildrenCache = buildPathChildrenCache(parentPath);
|
// 为子节点添加watcher
|
||||||
if (pathChildrenCache == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return pathChildrenCache.getCurrentData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PathChildrenCache buildPathChildrenCache(String path) throws Exception {
|
|
||||||
if (!isPathExist(path)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
||||||
// 且第三个参数要设置为true,不然ChildData对象中的getData返回null
|
PathChildrenCache childrenCache = new PathChildrenCache(client, parentPath, true);
|
||||||
PathChildrenCache childrenCache = new PathChildrenCache(client, path, true);
|
|
||||||
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
|
/**
|
||||||
|
* StartMode: 初始化方式
|
||||||
|
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
|
||||||
|
* NORMAL:异步初始化
|
||||||
|
* BUILD_INITIAL_CACHE:同步初始化
|
||||||
|
*/
|
||||||
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
||||||
return childrenCache;
|
|
||||||
|
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
|
||||||
|
List<ChildData> childDataList = childrenCache.getCurrentData();
|
||||||
|
listConsumer.accept(childDataList);
|
||||||
|
childrenCache.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听子节点的增删改
|
||||||
|
*
|
||||||
|
* @param parentPath 父节点路径
|
||||||
|
* @param listener
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static void listenChildren(String parentPath, PathChildrenCacheListener listener) throws Exception {
|
||||||
|
// 为子节点添加watcher
|
||||||
|
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
||||||
|
PathChildrenCache childrenCache = new PathChildrenCache(client, parentPath, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StartMode: 初始化方式
|
||||||
|
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
|
||||||
|
* NORMAL:异步初始化
|
||||||
|
* BUILD_INITIAL_CACHE:同步初始化
|
||||||
|
*/
|
||||||
|
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
||||||
|
// 监听根节点下面的子节点
|
||||||
|
childrenCache.getListenable().addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,8 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理网关自身异常
|
* 处理网关自身异常
|
||||||
*
|
*
|
||||||
@@ -26,8 +28,11 @@ public class ZuulErrorController implements ErrorController {
|
|||||||
*/
|
*/
|
||||||
@RequestMapping(ERROR_PATH)
|
@RequestMapping(ERROR_PATH)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object error() {
|
public Object error(HttpServletResponse response) {
|
||||||
RequestContext ctx = RequestContext.getCurrentContext();
|
RequestContext ctx = RequestContext.getCurrentContext();
|
||||||
|
if (ctx.getResponse() == null) {
|
||||||
|
ctx.setResponse(response);
|
||||||
|
}
|
||||||
ctx.setResponseStatusCode(HttpStatus.OK.value());
|
ctx.setResponseStatusCode(HttpStatus.OK.value());
|
||||||
Throwable throwable = ctx.getThrowable();
|
Throwable throwable = ctx.getThrowable();
|
||||||
return this.buildResult(throwable);
|
return this.buildResult(throwable);
|
||||||
|
@@ -6,11 +6,11 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-common</artifactId>
|
<artifactId>sop-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>sop-service-common</artifactId>
|
<artifactId>sop-service-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>sop-service-common</name>
|
<name>sop-service-common</name>
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
package com.gitee.sop.servercommon.bean;
|
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author thc
|
|
||||||
*/
|
|
||||||
public class EnvironmentContext {
|
|
||||||
|
|
||||||
private static Environment environment;
|
|
||||||
|
|
||||||
public static Environment getEnvironment() {
|
|
||||||
return environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setEnvironment(Environment environment) {
|
|
||||||
EnvironmentContext.environment = environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getProfile(Environment env) {
|
|
||||||
return env.getProperty("spring.profiles.active", "default");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getProfile() {
|
|
||||||
return getProfile(environment);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,11 @@
|
|||||||
|
package com.gitee.sop.servercommon.bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class ServiceConstants {
|
||||||
|
/**
|
||||||
|
* zookeeper存放接口路由信息的根目录
|
||||||
|
*/
|
||||||
|
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
|
||||||
|
}
|
@@ -0,0 +1,99 @@
|
|||||||
|
package com.gitee.sop.servercommon.bean;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.lang.math.NumberUtils;
|
||||||
|
import org.apache.curator.framework.CuratorFramework;
|
||||||
|
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||||
|
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Getter
|
||||||
|
public class ZookeeperTool implements Closeable {
|
||||||
|
|
||||||
|
private CuratorFramework client;
|
||||||
|
private Environment environment;
|
||||||
|
|
||||||
|
public ZookeeperTool(Environment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
initZookeeperClient(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initZookeeperClient(Environment environment) {
|
||||||
|
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
||||||
|
if (StringUtils.isBlank(zookeeperServerAddr)) {
|
||||||
|
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
|
||||||
|
}
|
||||||
|
String baseSleepTimeMs = environment.getProperty("spring.cloud.zookeeper.baseSleepTimeMs");
|
||||||
|
String maxRetries = environment.getProperty("spring.cloud.zookeeper.maxRetries");
|
||||||
|
log.info("初始化zookeeper客户端,zookeeperServerAddr:{}, baseSleepTimeMs:{}, maxRetries:{}",
|
||||||
|
zookeeperServerAddr, baseSleepTimeMs, maxRetries);
|
||||||
|
CuratorFramework client = CuratorFrameworkFactory.builder()
|
||||||
|
.connectString(zookeeperServerAddr)
|
||||||
|
.retryPolicy(new ExponentialBackoffRetry(NumberUtils.toInt(baseSleepTimeMs, 3000), NumberUtils.toInt(maxRetries, 3)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPathExist(String path) {
|
||||||
|
try {
|
||||||
|
return client.checkExists().forPath(path) != null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建path,如果path存在不报错,静默返回path名称
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @param data
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public String createPath(String path, String data) throws Exception {
|
||||||
|
if (isPathExist(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return getClient().create()
|
||||||
|
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
||||||
|
.creatingParentContainersIfNeeded()
|
||||||
|
.forPath(path, data.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新建或保存节点
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @param data
|
||||||
|
* @return 返回path
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public String createOrUpdateData(String path, String data) throws Exception {
|
||||||
|
return getClient().create()
|
||||||
|
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
|
||||||
|
.orSetData()
|
||||||
|
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
||||||
|
.creatingParentContainersIfNeeded()
|
||||||
|
.forPath(path, data.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (this.client != null) {
|
||||||
|
this.client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
package com.gitee.sop.servercommon.configuration;
|
||||||
|
|
||||||
|
import com.gitee.easyopen.doc.ApiDocHolder;
|
||||||
|
import com.gitee.sop.servercommon.swagger.SwaggerValidator;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档支持
|
||||||
|
* @author thc
|
||||||
|
*/
|
||||||
|
public abstract class EasyopenDocSupportController {
|
||||||
|
|
||||||
|
private SwaggerValidator swaggerValidator;
|
||||||
|
|
||||||
|
public abstract String getDocTitle();
|
||||||
|
|
||||||
|
public EasyopenDocSupportController() {
|
||||||
|
swaggerValidator = new SwaggerValidator(this.swaggerAccessProtected());
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/v2/api-docs")
|
||||||
|
@ResponseBody
|
||||||
|
public Map<String, Object> getDocInfo(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
if (swaggerValidator.swaggerAccessProtected() && !swaggerValidator.validate(request)) {
|
||||||
|
swaggerValidator.writeForbidden(response);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<String, Object> context = this.getContext();
|
||||||
|
context.put("easyopen", "1.16.3");
|
||||||
|
context.put("apiModules", ApiDocHolder.getApiDocBuilder().getApiModules());
|
||||||
|
context.put("title", getDocTitle());
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getContext() {
|
||||||
|
return new HashMap<>(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* swagger访问是否加密保护
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean swaggerAccessProtected() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
package com.gitee.sop.servercommon.configuration;
|
package com.gitee.sop.servercommon.configuration;
|
||||||
|
|
||||||
import com.gitee.sop.servercommon.bean.EnvironmentContext;
|
|
||||||
import com.gitee.sop.servercommon.bean.ServiceConfig;
|
import com.gitee.sop.servercommon.bean.ServiceConfig;
|
||||||
import com.gitee.sop.servercommon.manager.ApiMetaManager;
|
import com.gitee.sop.servercommon.manager.ApiMetaManager;
|
||||||
import com.gitee.sop.servercommon.manager.DefaultRequestMappingEvent;
|
import com.gitee.sop.servercommon.manager.DefaultRequestMappingEvent;
|
||||||
@@ -55,7 +54,6 @@ public class SpringMvcServiceConfiguration {
|
|||||||
@PostConstruct
|
@PostConstruct
|
||||||
public final void after() {
|
public final void after() {
|
||||||
log.info("-----spring容器加载完毕-----");
|
log.info("-----spring容器加载完毕-----");
|
||||||
EnvironmentContext.setEnvironment(environment);
|
|
||||||
Executors.newSingleThreadExecutor().execute(()->{
|
Executors.newSingleThreadExecutor().execute(()->{
|
||||||
uploadRouteToZookeeper();
|
uploadRouteToZookeeper();
|
||||||
});
|
});
|
||||||
|
@@ -2,15 +2,15 @@ package com.gitee.sop.servercommon.manager;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.gitee.sop.servercommon.bean.ServiceApiInfo;
|
import com.gitee.sop.servercommon.bean.ServiceApiInfo;
|
||||||
|
import com.gitee.sop.servercommon.bean.ServiceConstants;
|
||||||
|
import com.gitee.sop.servercommon.bean.ZookeeperTool;
|
||||||
import com.gitee.sop.servercommon.route.GatewayPredicateDefinition;
|
import com.gitee.sop.servercommon.route.GatewayPredicateDefinition;
|
||||||
import com.gitee.sop.servercommon.route.GatewayRouteDefinition;
|
import com.gitee.sop.servercommon.route.GatewayRouteDefinition;
|
||||||
import com.gitee.sop.servercommon.route.ServiceRouteInfo;
|
import com.gitee.sop.servercommon.route.ServiceRouteInfo;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.curator.framework.CuratorFramework;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
|
||||||
import org.apache.curator.retry.ExponentialBackoffRetry;
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@@ -33,7 +33,7 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
|
|||||||
/**
|
/**
|
||||||
* zookeeper存放接口路由信息的根目录
|
* zookeeper存放接口路由信息的根目录
|
||||||
*/
|
*/
|
||||||
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
|
public static final String SOP_SERVICE_ROUTE_PATH = ServiceConstants.SOP_SERVICE_ROUTE_PATH;
|
||||||
public static final String PATH_START_CHAR = "/";
|
public static final String PATH_START_CHAR = "/";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,24 +44,56 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
|
|||||||
|
|
||||||
private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000");
|
private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000");
|
||||||
|
|
||||||
private final String routeRootPath;
|
private final String routeRootPath = SOP_SERVICE_ROUTE_PATH;
|
||||||
private final String zookeeperServerAddr;
|
private final String zookeeperServerAddr;
|
||||||
|
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
|
|
||||||
|
private ZookeeperTool zookeeperTool;
|
||||||
|
|
||||||
|
private String serviceId;
|
||||||
|
|
||||||
public ServiceZookeeperApiMetaManager(Environment environment) {
|
public ServiceZookeeperApiMetaManager(Environment environment) {
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.routeRootPath = SOP_SERVICE_ROUTE_PATH;
|
serviceId = environment.getProperty("spring.application.name");
|
||||||
|
if (StringUtils.isEmpty(serviceId)) {
|
||||||
|
throw new IllegalArgumentException("请在application.properties中指定spring.application.name属性");
|
||||||
|
}
|
||||||
zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
||||||
if (StringUtils.isEmpty(zookeeperServerAddr)) {
|
if (StringUtils.isEmpty(zookeeperServerAddr)) {
|
||||||
throw new IllegalArgumentException("未指定spring.cloud.zookeeper.connect-string参数");
|
throw new IllegalArgumentException("未指定spring.cloud.zookeeper.connect-string参数");
|
||||||
}
|
}
|
||||||
|
this.zookeeperTool = new ZookeeperTool(environment);
|
||||||
|
|
||||||
|
this.uploadServiceId(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传serviceId目录
|
||||||
|
*
|
||||||
|
* @param environment
|
||||||
|
*/
|
||||||
|
protected void uploadServiceId(Environment environment) {
|
||||||
|
try {
|
||||||
|
ServiceRouteInfo serviceRouteInfo = this.buildServiceRouteInfo();
|
||||||
|
// 保存路径
|
||||||
|
String savePath = routeRootPath + "/" + serviceId;
|
||||||
|
String nodeData = JSON.toJSONString(serviceRouteInfo);
|
||||||
|
log.info("zookeeper创建serviceId路径,path:{}, nodeData:{}", savePath, nodeData);
|
||||||
|
this.zookeeperTool.createPath(savePath, nodeData);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("zookeeper操作失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uploadApi(ServiceApiInfo serviceApiInfo) {
|
public void uploadApi(ServiceApiInfo serviceApiInfo) {
|
||||||
|
try {
|
||||||
ServiceRouteInfo serviceRouteInfo = this.buildServiceGatewayInfo(serviceApiInfo);
|
ServiceRouteInfo serviceRouteInfo = this.buildServiceGatewayInfo(serviceApiInfo);
|
||||||
this.uploadServiceRouteInfoToZookeeper(serviceRouteInfo);
|
this.uploadServiceRouteInfoToZookeeper(serviceRouteInfo);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(zookeeperTool);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,11 +110,16 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
|
|||||||
GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
|
GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
|
||||||
routeDefinitionList.add(gatewayRouteDefinition);
|
routeDefinitionList.add(gatewayRouteDefinition);
|
||||||
}
|
}
|
||||||
|
ServiceRouteInfo serviceRouteInfo = this.buildServiceRouteInfo();
|
||||||
|
serviceRouteInfo.setRouteDefinitionList(routeDefinitionList);
|
||||||
|
return serviceRouteInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ServiceRouteInfo buildServiceRouteInfo() {
|
||||||
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
|
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
|
||||||
serviceRouteInfo.setServiceId(serviceApiInfo.getServiceId());
|
serviceRouteInfo.setServiceId(serviceId);
|
||||||
String description = environment.getProperty("spring.application.description");
|
String description = environment.getProperty("spring.application.description");
|
||||||
serviceRouteInfo.setDescription(description);
|
serviceRouteInfo.setDescription(description);
|
||||||
serviceRouteInfo.setRouteDefinitionList(routeDefinitionList);
|
|
||||||
return serviceRouteInfo;
|
return serviceRouteInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,75 +181,51 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
|
|||||||
* @param serviceRouteInfo
|
* @param serviceRouteInfo
|
||||||
*/
|
*/
|
||||||
protected void uploadServiceRouteInfoToZookeeper(ServiceRouteInfo serviceRouteInfo) {
|
protected void uploadServiceRouteInfoToZookeeper(ServiceRouteInfo serviceRouteInfo) {
|
||||||
CuratorFramework client = null;
|
|
||||||
try {
|
try {
|
||||||
// 保存路径
|
// 保存路径
|
||||||
String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId();
|
String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId();
|
||||||
|
|
||||||
client = CuratorFrameworkFactory.builder()
|
|
||||||
.connectString(zookeeperServerAddr)
|
|
||||||
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
client.start();
|
|
||||||
|
|
||||||
log.info("上传接口信息到zookeeper,path:{}, serviceId:{}, 接口数量:{}",
|
log.info("上传接口信息到zookeeper,path:{}, serviceId:{}, 接口数量:{}",
|
||||||
savePath,
|
savePath,
|
||||||
serviceRouteInfo.getServiceId(),
|
serviceRouteInfo.getServiceId(),
|
||||||
serviceRouteInfo.getRouteDefinitionList().size());
|
serviceRouteInfo.getRouteDefinitionList().size());
|
||||||
|
|
||||||
String parentPath = this.uploadFolder(client, serviceRouteInfo);
|
String parentPath = this.uploadFolder(serviceRouteInfo);
|
||||||
this.uploadRouteItems(client, serviceRouteInfo, parentPath);
|
this.uploadRouteItems(serviceRouteInfo, parentPath);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新接口信息到zookeeper失败, serviceId:{}", serviceRouteInfo.getServiceId(), e);
|
log.error("更新接口信息到zookeeper失败, serviceId:{}", serviceRouteInfo.getServiceId(), e);
|
||||||
} finally {
|
|
||||||
if (client != null) {
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件夹内容
|
* 上传文件夹内容
|
||||||
*
|
*
|
||||||
* @param client
|
|
||||||
* @param serviceRouteInfo
|
* @param serviceRouteInfo
|
||||||
* @return 返回文件夹路径
|
* @return 返回文件夹路径
|
||||||
*/
|
*/
|
||||||
protected String uploadFolder(CuratorFramework client, ServiceRouteInfo serviceRouteInfo) throws Exception {
|
protected String uploadFolder(ServiceRouteInfo serviceRouteInfo) throws Exception {
|
||||||
// 保存路径
|
// 保存路径
|
||||||
String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId();
|
String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId();
|
||||||
String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo);
|
String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo);
|
||||||
log.info("上传service目录到zookeeper,路径:{},内容:{}", savePath, serviceRouteInfoJson);
|
log.info("上传service目录到zookeeper,路径:{},内容:{}", savePath, serviceRouteInfoJson);
|
||||||
this.saveNode(client, savePath, serviceRouteInfoJson.getBytes());
|
this.zookeeperTool.createOrUpdateData(savePath, serviceRouteInfoJson);
|
||||||
return savePath;
|
return savePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传路由信息
|
* 上传路由信息
|
||||||
*
|
*
|
||||||
* @param client
|
|
||||||
* @param serviceRouteInfo
|
* @param serviceRouteInfo
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected void uploadRouteItems(CuratorFramework client, ServiceRouteInfo serviceRouteInfo, String parentPath) throws Exception {
|
protected void uploadRouteItems(ServiceRouteInfo serviceRouteInfo, String parentPath) throws Exception {
|
||||||
List<GatewayRouteDefinition> routeDefinitionList = serviceRouteInfo.getRouteDefinitionList();
|
List<GatewayRouteDefinition> routeDefinitionList = serviceRouteInfo.getRouteDefinitionList();
|
||||||
for (GatewayRouteDefinition routeDefinition : routeDefinitionList) {
|
for (GatewayRouteDefinition routeDefinition : routeDefinitionList) {
|
||||||
// 父目录/子目录
|
// 父目录/子目录
|
||||||
String savePath = parentPath + PATH_START_CHAR + routeDefinition.getId();
|
String savePath = parentPath + PATH_START_CHAR + routeDefinition.getId();
|
||||||
String routeDefinitionJson = JSON.toJSONString(routeDefinition);
|
String routeDefinitionJson = JSON.toJSONString(routeDefinition);
|
||||||
log.info("上传路由配置到zookeeper,路径:{},路由数据:{}", savePath, routeDefinitionJson);
|
log.info("上传路由配置到zookeeper,路径:{},路由数据:{}", savePath, routeDefinitionJson);
|
||||||
this.saveNode(client, savePath, routeDefinitionJson.getBytes());
|
this.zookeeperTool.createOrUpdateData(savePath, routeDefinitionJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void saveNode(CuratorFramework client, String path, byte[] data) throws Exception {
|
|
||||||
client.create()
|
|
||||||
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
|
|
||||||
.orSetData()
|
|
||||||
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
|
||||||
.creatingParentContainersIfNeeded()
|
|
||||||
.forPath(path, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析request参数中的业务参数,隐射到方法参数上
|
* 解析request参数中的业务参数,绑定到方法参数上
|
||||||
*
|
*
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
package com.gitee.sop.servercommon.swagger;
|
|
||||||
|
|
||||||
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
|
|
||||||
import springfox.documentation.spring.web.scanners.ApiDescriptionReader;
|
|
||||||
import springfox.documentation.spring.web.scanners.ApiListingScanner;
|
|
||||||
import springfox.documentation.spring.web.scanners.ApiModelReader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author tanghc
|
|
||||||
*/
|
|
||||||
public class ApiListingScannerExt extends ApiListingScanner {
|
|
||||||
public ApiListingScannerExt(ApiDescriptionReader apiDescriptionReader, ApiModelReader apiModelReader, DocumentationPluginsManager pluginsManager) {
|
|
||||||
super(apiDescriptionReader, apiModelReader, pluginsManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,87 @@
|
|||||||
|
package com.gitee.sop.servercommon.swagger;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.FilterConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SwaggerSecurityFilter implements Filter {
|
||||||
|
|
||||||
|
protected List<String> urlFilters = new ArrayList<>();
|
||||||
|
|
||||||
|
{
|
||||||
|
urlFilters.add(".*?/doc\\.html.*");
|
||||||
|
urlFilters.add(".*?/v2/api-docs.*");
|
||||||
|
urlFilters.add(".*?/v2/api-docs-ext.*");
|
||||||
|
urlFilters.add(".*?/swagger-resources.*");
|
||||||
|
urlFilters.add(".*?/swagger-ui\\.html.*");
|
||||||
|
urlFilters.add(".*?/swagger-resources/configuration/ui.*");
|
||||||
|
urlFilters.add(".*?/swagger-resources/configuration/security.*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SwaggerValidator swaggerValidator;
|
||||||
|
|
||||||
|
public SwaggerSecurityFilter(boolean swaggerAccessProtected) {
|
||||||
|
this.swaggerValidator = new SwaggerValidator(swaggerAccessProtected);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean match(String uri) {
|
||||||
|
boolean match = false;
|
||||||
|
if (uri != null) {
|
||||||
|
for (String regex : urlFilters) {
|
||||||
|
if (uri.matches(regex)) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
if (!swaggerValidator.swaggerAccessProtected()) {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
// 没有匹配到,直接放行
|
||||||
|
if (!match(uri)) {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
} else {
|
||||||
|
if (swaggerValidator.validate(request)) {
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
} else {
|
||||||
|
swaggerValidator.writeForbidden(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -30,6 +30,19 @@ public abstract class SwaggerSupport {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SwaggerSecurityFilter swaggerSecurityFilter() {
|
||||||
|
return new SwaggerSecurityFilter(swaggerAccessProtected());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* swagger访问是否加密保护
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean swaggerAccessProtected() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected ApiInfo apiInfo() {
|
protected ApiInfo apiInfo() {
|
||||||
return new ApiInfoBuilder()
|
return new ApiInfoBuilder()
|
||||||
.title(getDocTitle())
|
.title(getDocTitle())
|
||||||
|
@@ -0,0 +1,53 @@
|
|||||||
|
package com.gitee.sop.servercommon.swagger;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class SwaggerValidator {
|
||||||
|
|
||||||
|
private String secret = "b749a2ec000f4f29";
|
||||||
|
|
||||||
|
private boolean swaggerAccessProtected = true;
|
||||||
|
|
||||||
|
public SwaggerValidator(boolean swaggerAccessProtected) {
|
||||||
|
this.swaggerAccessProtected = swaggerAccessProtected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwaggerValidator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* swagger访问是否加密保护
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean swaggerAccessProtected() {
|
||||||
|
return swaggerAccessProtected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate(HttpServletRequest request) {
|
||||||
|
String time = request.getParameter("time");
|
||||||
|
String sign = request.getParameter("sign");
|
||||||
|
if (StringUtils.isAnyBlank(time, sign)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String source = secret + time + secret;
|
||||||
|
String serverSign = DigestUtils.md5DigestAsHex(source.getBytes());
|
||||||
|
return serverSign.equals(sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeForbidden(HttpServletResponse response) throws IOException {
|
||||||
|
response.setContentType("text/palin;charset=UTF-8");
|
||||||
|
response.setStatus(403);
|
||||||
|
PrintWriter printWriter = response.getWriter();
|
||||||
|
printWriter.write("access forbidden");
|
||||||
|
printWriter.flush();
|
||||||
|
}
|
||||||
|
}
|
@@ -23,7 +23,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-service-common</artifactId>
|
<artifactId>sop-service-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-service-common</artifactId>
|
<artifactId>sop-service-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
package com.gitee.easyopen.server.api;
|
||||||
|
|
||||||
|
import com.gitee.easyopen.annotation.Api;
|
||||||
|
import com.gitee.easyopen.annotation.ApiService;
|
||||||
|
import com.gitee.easyopen.doc.annotation.ApiDoc;
|
||||||
|
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
|
||||||
|
import com.gitee.easyopen.server.api.param.GoodsParam;
|
||||||
|
import com.gitee.easyopen.server.api.result.Goods;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务类
|
||||||
|
*
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@ApiService
|
||||||
|
@ApiDoc("库存接口")
|
||||||
|
public class Goods2Api {
|
||||||
|
|
||||||
|
@Api(name = "store.get")
|
||||||
|
@ApiDocMethod(description = "获取库存")
|
||||||
|
Goods getGoods(GoodsParam param) {
|
||||||
|
Goods goods = new Goods();
|
||||||
|
goods.setId(1L);
|
||||||
|
goods.setGoods_name("苹果iPhoneX");
|
||||||
|
goods.setPrice(new BigDecimal(8000));
|
||||||
|
return goods;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -15,7 +15,7 @@ import java.math.BigDecimal;
|
|||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@ApiService
|
@ApiService
|
||||||
@ApiDoc("商品模块")
|
@ApiDoc("商品接口")
|
||||||
public class GoodsApi {
|
public class GoodsApi {
|
||||||
|
|
||||||
@Api(name = "goods.get")
|
@Api(name = "goods.get")
|
||||||
|
@@ -1,11 +1,21 @@
|
|||||||
package com.gitee.easyopen.server.config;
|
package com.gitee.easyopen.server.config;
|
||||||
|
|
||||||
|
import com.gitee.sop.servercommon.configuration.EasyopenDocSupportController;
|
||||||
import com.gitee.sop.servercommon.configuration.EasyopenServiceConfiguration;
|
import com.gitee.sop.servercommon.configuration.EasyopenServiceConfiguration;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SopConfig extends EasyopenServiceConfiguration {
|
public class SopConfig extends EasyopenServiceConfiguration {
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public static class SopDocController extends EasyopenDocSupportController {
|
||||||
|
@Override
|
||||||
|
public String getDocTitle() {
|
||||||
|
return "商品API";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-service-common</artifactId>
|
<artifactId>sop-service-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- eureka 服务发现 -->
|
<!-- eureka 服务发现 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-service-common</artifactId>
|
<artifactId>sop-service-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
|
@@ -3,6 +3,7 @@ package com.gitee.sop.bookweb.controller;
|
|||||||
import com.gitee.sop.bookweb.controller.param.StoryParam;
|
import com.gitee.sop.bookweb.controller.param.StoryParam;
|
||||||
import com.gitee.sop.servercommon.annotation.ApiMapping;
|
import com.gitee.sop.servercommon.annotation.ApiMapping;
|
||||||
import com.gitee.sop.story.api.domain.Story;
|
import com.gitee.sop.story.api.domain.Story;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -16,6 +17,7 @@ import java.util.Date;
|
|||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
|
@Api(tags = "故事接口")
|
||||||
public class AlipayController {
|
public class AlipayController {
|
||||||
|
|
||||||
@ApiMapping(value = "alipay.story.get")
|
@ApiMapping(value = "alipay.story.get")
|
||||||
|
@@ -3,6 +3,7 @@ package com.gitee.sop.bookweb.controller;
|
|||||||
import com.gitee.sop.servercommon.annotation.ApiAbility;
|
import com.gitee.sop.servercommon.annotation.ApiAbility;
|
||||||
import com.gitee.sop.servercommon.annotation.ApiMapping;
|
import com.gitee.sop.servercommon.annotation.ApiMapping;
|
||||||
import com.gitee.sop.story.api.domain.Story;
|
import com.gitee.sop.story.api.domain.Story;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@ApiAbility // 放在这里,下面所有的接口都具备接口提供能力
|
@ApiAbility // 放在这里,下面所有的接口都具备接口提供能力
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("story2")
|
@RequestMapping("story2")
|
||||||
|
@Api(tags = "故事接口2")
|
||||||
public class Story2Controller{
|
public class Story2Controller{
|
||||||
|
|
||||||
@RequestMapping("getStory4")
|
@RequestMapping("getStory4")
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.gitee.sop</groupId>
|
<groupId>com.gitee.sop</groupId>
|
||||||
<artifactId>sop-gateway-common</artifactId>
|
<artifactId>sop-gateway-common</artifactId>
|
||||||
<version>1.3.0-SNAPSHOT</version>
|
<version>1.4.0-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓ -->
|
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓ -->
|
||||||
|
@@ -1,28 +1,20 @@
|
|||||||
package com.gitee.sop.gateway.config;
|
package com.gitee.sop.gateway.config;
|
||||||
|
|
||||||
import com.gitee.sop.gateway.entity.IsvInfo;
|
|
||||||
import com.gitee.sop.gateway.manager.ManagerInitializer;
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
|
||||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
|
||||||
import com.gitee.sop.gatewaycommon.easyopen.EasyopenZuulConfiguration;
|
|
||||||
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
|
|
||||||
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
|
||||||
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
|
|
||||||
import com.gitee.sop.gatewaycommon.zuul.configuration.TaobaoZuulConfiguration;
|
|
||||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用Spring Cloud Zuul,推荐使用
|
* 使用Spring Cloud Zuul,推荐使用
|
||||||
*
|
*
|
||||||
* 注意:下面两个只能使用一个
|
* 注意:下面两个只能使用一个
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import com.gitee.sop.gateway.manager.ManagerInitializer;
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||||
|
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开通支付宝开放平台能力
|
* 开通支付宝开放平台能力
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
|
@@ -11,7 +11,7 @@ import org.apache.curator.retry.ExponentialBackoffRetry;
|
|||||||
*/
|
*/
|
||||||
public class CuratorTest extends TestCase {
|
public class CuratorTest extends TestCase {
|
||||||
|
|
||||||
private String zookeeperServerAddr = "127.0.0.1:2181";
|
private String zookeeperServerAddr = "localhost:2181";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归删除节点,只能在测试环境用。
|
* 递归删除节点,只能在测试环境用。
|
||||||
@@ -30,17 +30,5 @@ public class CuratorTest extends TestCase {
|
|||||||
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH);
|
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH + "-default");
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH + "-dev");
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH + "-test");
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
sop-registry/src/main/resources/application-dev.yml
Normal file
17
sop-registry/src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
eureka:
|
||||||
|
client:
|
||||||
|
fetch-registry: false
|
||||||
|
# 不注册自己
|
||||||
|
register-with-eureka: false
|
||||||
|
serviceUrl:
|
||||||
|
defaultZone: http://${eureka.host}:${eureka.port}/eureka/
|
||||||
|
# 注册中心地址
|
||||||
|
host: localhost
|
||||||
|
port: 1111
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 1111
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: sop-registry
|
@@ -1,12 +0,0 @@
|
|||||||
spring.application.name=sop-registry
|
|
||||||
server.port=1111
|
|
||||||
|
|
||||||
# ---- eureka注册中心 ----
|
|
||||||
# 不注册自己
|
|
||||||
eureka.client.register-with-eureka=false
|
|
||||||
eureka.client.fetch-registry=false
|
|
||||||
# 注册中心地址
|
|
||||||
eureka.host=localhost
|
|
||||||
eureka.port=1111
|
|
||||||
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/
|
|
||||||
|
|
3
sop-registry/src/main/resources/application.yml
Normal file
3
sop-registry/src/main/resources/application.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: dev
|
@@ -82,12 +82,6 @@
|
|||||||
<blockquote class="layui-elem-quote layui-text sop-description">
|
<blockquote class="layui-elem-quote layui-text sop-description">
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
|
||||||
|
|
||||||
<div class="site-title">
|
|
||||||
<fieldset class="layui-elem-field layui-field-title site-title">
|
|
||||||
<legend><a name="use">公共参数</a></legend>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
<div class="site-text">
|
<div class="site-text">
|
||||||
<h3>请求地址</h3>
|
<h3>请求地址</h3>
|
||||||
<table class="layui-table">
|
<table class="layui-table">
|
||||||
@@ -106,6 +100,12 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="site-title">
|
||||||
|
<fieldset class="layui-elem-field layui-field-title site-title">
|
||||||
|
<legend><a name="use">公共参数</a></legend>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="site-text">
|
<div class="site-text">
|
||||||
<h3>公共请求参数</h3>
|
<h3>公共请求参数</h3>
|
||||||
<table class="layui-table">
|
<table class="layui-table">
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
<td>String</td>
|
<td>String</td>
|
||||||
<td>否</td>
|
<td>否</td>
|
||||||
<td>40</td>
|
<td>40</td>
|
||||||
<td>详见<a href="#" target="_blank">应用授权概述</a></td>
|
<td>暂未实现</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@@ -7,28 +7,38 @@ layui.use(['element', 'form'], function(){ //加载code模块
|
|||||||
function initDocModules() {
|
function initDocModules() {
|
||||||
$.getJSON(SopConfig.url + '/doc/getDocBaseInfo', function (baseInfo) {
|
$.getJSON(SopConfig.url + '/doc/getDocBaseInfo', function (baseInfo) {
|
||||||
var html = [];
|
var html = [];
|
||||||
var modules = baseInfo.docModuleVOList;
|
var docInfoList = baseInfo.docInfoList;
|
||||||
for (var i = 0; i < modules.length; i++) {
|
for (var i = 0; i < docInfoList.length; i++) {
|
||||||
var docDefinition = modules[i];
|
var docInfo = docInfoList[i];
|
||||||
var module = docDefinition.module;
|
|
||||||
var selected = i === 0 ? 'selected="selected"' : '';
|
var selected = i === 0 ? 'selected="selected"' : '';
|
||||||
html.push('<option value="' + module + '" ' + selected + '>' + module + '</option>');
|
var title = docInfo.title;
|
||||||
|
html.push('<option value="' + title + '" ' + selected + '>' + title + '</option>');
|
||||||
}
|
}
|
||||||
$('#moduleList').html(html.join(''));
|
$('#moduleList').html(html.join(''));
|
||||||
form.render('select');
|
|
||||||
if (modules && modules.length > 0) {
|
|
||||||
selectModule(modules[0].module);
|
|
||||||
}
|
|
||||||
$('.url-prod').text(baseInfo.urlProd);
|
$('.url-prod').text(baseInfo.urlProd);
|
||||||
|
form.render('select');
|
||||||
|
|
||||||
|
if (docInfoList && docInfoList.length > 0) {
|
||||||
|
selectDocInfo(docInfoList[0].title);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectModule(docModule) {
|
function selectDocInfo(title) {
|
||||||
$.getJSON(SopConfig.url + '/doc/module/' + docModule, function (module) {
|
$.getJSON(SopConfig.url + '/doc/docinfo/' + title, function (docInfo) {
|
||||||
|
var moduleList = docInfo.docModuleList;
|
||||||
|
var html = [];
|
||||||
|
var firstItem;
|
||||||
|
for (var j = 0; j < moduleList.length; j++) {
|
||||||
|
var module = moduleList[j];
|
||||||
var docItems = module.docItems;
|
var docItems = module.docItems;
|
||||||
var html = ['<li><h2>' + docModule + '</h2></li>'];
|
html.push('<li><h2>' + module.module + '</h2></li>');
|
||||||
for (var i = 0; i < docItems.length; i++) {
|
for (var i = 0; i < docItems.length; i++) {
|
||||||
var docItem = docItems[i];
|
var docItem = docItems[i];
|
||||||
|
var first = j == 0 && j == 0;
|
||||||
|
if (first) {
|
||||||
|
firstItem = docItem;
|
||||||
|
}
|
||||||
docItemStore[docItem.nameVersion] = docItem;
|
docItemStore[docItem.nameVersion] = docItem;
|
||||||
/*
|
/*
|
||||||
<li class="site-tree-noicon layui-this">
|
<li class="site-tree-noicon layui-this">
|
||||||
@@ -37,13 +47,13 @@ layui.use(['element', 'form'], function(){ //加载code模块
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
*/
|
*/
|
||||||
var selectedClass = i === 0 ? 'layui-this' : '';
|
html.push('<li class="site-tree-noicon" nameversion="'+docItem.nameVersion+'">');
|
||||||
html.push('<li class="site-tree-noicon ' + selectedClass + '">');
|
html.push('<a href="#"><cite>'+docItem.summary+'</cite></a>')
|
||||||
html.push('<a href="#" nameversion="'+docItem.nameVersion+'"><cite>'+docItem.summary+'</cite></a>')
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$('#docItemTree').html(html.join(''));
|
$('#docItemTree').html(html.join(''));
|
||||||
if (docItems && docItems.length > 0) {
|
if (firstItem) {
|
||||||
var firstItem = docItems[0];
|
|
||||||
selectDocItem(firstItem.nameVersion);
|
selectDocItem(firstItem.nameVersion);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -51,12 +61,11 @@ layui.use(['element', 'form'], function(){ //加载code模块
|
|||||||
|
|
||||||
function initEvent() {
|
function initEvent() {
|
||||||
form.on('select(moduleListFilter)', function (data) {
|
form.on('select(moduleListFilter)', function (data) {
|
||||||
selectModule(data.value);
|
selectDocInfo(data.value);
|
||||||
})
|
})
|
||||||
$('#docItemTree').on('click', 'a', function () {
|
$('#docItemTree').on('click', 'li', function () {
|
||||||
var $tagA = $(this);
|
var $li = $(this);
|
||||||
selectDocItem($tagA.attr('nameversion'));
|
selectDocItem($li.attr('nameversion'));
|
||||||
$tagA.parent().addClass('layui-this').siblings().removeClass('layui-this');
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +79,9 @@ layui.use(['element', 'form'], function(){ //加载code模块
|
|||||||
createRequestParameter(docItem);
|
createRequestParameter(docItem);
|
||||||
createResponseParameter(docItem);
|
createResponseParameter(docItem);
|
||||||
createResponseCode(docItem);
|
createResponseCode(docItem);
|
||||||
|
|
||||||
|
var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]');
|
||||||
|
$li.addClass('layui-this').siblings().removeClass('layui-this');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRequestParameter(docItem) {
|
function createRequestParameter(docItem) {
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
package com.gitee.sop.websiteserver.bean;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DocInfo {
|
||||||
|
private String title;
|
||||||
|
private List<DocModule> docModuleList;
|
||||||
|
}
|
@@ -9,6 +9,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class DocItem {
|
public class DocItem {
|
||||||
|
private String module;
|
||||||
private String name;
|
private String name;
|
||||||
private String version;
|
private String version;
|
||||||
private String summary;
|
private String summary;
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
package com.gitee.sop.websiteserver.bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class WebsiteConstants {
|
||||||
|
/**
|
||||||
|
* zookeeper存放接口路由信息的根目录
|
||||||
|
*/
|
||||||
|
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
|
||||||
|
}
|
@@ -0,0 +1,80 @@
|
|||||||
|
package com.gitee.sop.websiteserver.bean;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.lang.math.NumberUtils;
|
||||||
|
import org.apache.curator.framework.CuratorFramework;
|
||||||
|
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||||
|
import org.apache.curator.framework.recipes.cache.TreeCache;
|
||||||
|
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
|
||||||
|
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ZookeeperContext {
|
||||||
|
|
||||||
|
private static CuratorFramework client;
|
||||||
|
|
||||||
|
public static void setEnvironment(Environment environment) {
|
||||||
|
Assert.notNull(environment, "environment不能为null");
|
||||||
|
initZookeeperClient(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void initZookeeperClient(Environment environment) {
|
||||||
|
if (client != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
||||||
|
if (StringUtils.isBlank(zookeeperServerAddr)) {
|
||||||
|
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
|
||||||
|
}
|
||||||
|
String baseSleepTimeMs = environment.getProperty("spring.cloud.zookeeper.baseSleepTimeMs");
|
||||||
|
String maxRetries = environment.getProperty("spring.cloud.zookeeper.maxRetries");
|
||||||
|
log.info("初始化zookeeper客户端,zookeeperServerAddr:{}, baseSleepTimeMs:{}, maxRetries:{}",
|
||||||
|
zookeeperServerAddr, baseSleepTimeMs, maxRetries);
|
||||||
|
CuratorFramework client = CuratorFrameworkFactory.builder()
|
||||||
|
.connectString(zookeeperServerAddr)
|
||||||
|
.retryPolicy(new ExponentialBackoffRetry(NumberUtils.toInt(baseSleepTimeMs, 3000), NumberUtils.toInt(maxRetries, 3)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
setClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getRouteRootPath() {
|
||||||
|
return WebsiteConstants.SOP_SERVICE_ROUTE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CuratorFramework getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setClient(CuratorFramework client) {
|
||||||
|
ZookeeperContext.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听子节点,可以自定义层级
|
||||||
|
* @param parentPath 父节点路径
|
||||||
|
* @param maxDepth 层级,从1开始。比如当前监听节点/t1,目录最深为/t1/t2/t3/t4,则maxDepth=3,说明下面3级子目录全
|
||||||
|
* @param listener
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static void listenChildren(String parentPath, int maxDepth, TreeCacheListener listener) throws Exception {
|
||||||
|
final TreeCache treeCache = TreeCache
|
||||||
|
.newBuilder(client, parentPath)
|
||||||
|
.setCacheData(true)
|
||||||
|
.setMaxDepth(maxDepth)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
treeCache.getListenable().addListener(listener);
|
||||||
|
//没有开启模式作为入参的方法
|
||||||
|
treeCache.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,10 +1,10 @@
|
|||||||
package com.gitee.sop.websiteserver.controller;
|
package com.gitee.sop.websiteserver.controller;
|
||||||
|
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocInfo;
|
||||||
import com.gitee.sop.websiteserver.bean.DocItem;
|
import com.gitee.sop.websiteserver.bean.DocItem;
|
||||||
import com.gitee.sop.websiteserver.bean.DocModule;
|
|
||||||
import com.gitee.sop.websiteserver.manager.DocManager;
|
import com.gitee.sop.websiteserver.manager.DocManager;
|
||||||
import com.gitee.sop.websiteserver.vo.DocBaseInfoVO;
|
import com.gitee.sop.websiteserver.vo.DocBaseInfoVO;
|
||||||
import com.gitee.sop.websiteserver.vo.DocModuleVO;
|
import com.gitee.sop.websiteserver.vo.DocInfoVO;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -38,11 +38,11 @@ public class DocController {
|
|||||||
|
|
||||||
@GetMapping("/getDocBaseInfo")
|
@GetMapping("/getDocBaseInfo")
|
||||||
public DocBaseInfoVO getDocBaseInfo() {
|
public DocBaseInfoVO getDocBaseInfo() {
|
||||||
List<DocModuleVO> docModuleVOList = docManager.listAll()
|
List<DocInfoVO> docInfoList = docManager.listAll()
|
||||||
.stream()
|
.stream()
|
||||||
.map(docModule -> {
|
.map(docInfo -> {
|
||||||
DocModuleVO vo = new DocModuleVO();
|
DocInfoVO vo = new DocInfoVO();
|
||||||
BeanUtils.copyProperties(docModule, vo);
|
BeanUtils.copyProperties(docInfo, vo);
|
||||||
return vo;
|
return vo;
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -50,13 +50,13 @@ public class DocController {
|
|||||||
DocBaseInfoVO baseInfoVO = new DocBaseInfoVO();
|
DocBaseInfoVO baseInfoVO = new DocBaseInfoVO();
|
||||||
baseInfoVO.setUrlTest(urlTest);
|
baseInfoVO.setUrlTest(urlTest);
|
||||||
baseInfoVO.setUrlProd(urlProd);
|
baseInfoVO.setUrlProd(urlProd);
|
||||||
baseInfoVO.setDocModuleVOList(docModuleVOList);
|
baseInfoVO.setDocInfoList(docInfoList);
|
||||||
return baseInfoVO;
|
return baseInfoVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/module/{module}")
|
@GetMapping("/docinfo/{title}")
|
||||||
public DocModule getDocModule(@PathVariable("module") String module) {
|
public DocInfo getDocModule(@PathVariable("title") String title) {
|
||||||
return docManager.getByTitle(module);
|
return docManager.getByTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/item/{method}/{version}/")
|
@GetMapping("/item/{method}/{version}/")
|
||||||
@@ -65,10 +65,13 @@ public class DocController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/doc/reload")
|
// 后门地址,可手动更新文档内容,一般情况下用不到
|
||||||
public void reload(String pwd) {
|
@GetMapping("/reload")
|
||||||
if (StringUtils.equals(this.pwd, pwd)) {
|
public String reload(String pwd) {
|
||||||
|
boolean correct = StringUtils.equals(this.pwd, pwd);
|
||||||
|
if (correct) {
|
||||||
docManager.load();
|
docManager.load();
|
||||||
}
|
}
|
||||||
|
return String.valueOf(correct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package com.gitee.sop.websiteserver.manager;
|
package com.gitee.sop.websiteserver.manager;
|
||||||
|
|
||||||
import com.gitee.sop.websiteserver.bean.DocModule;
|
import com.gitee.sop.websiteserver.bean.DocInfo;
|
||||||
import com.gitee.sop.websiteserver.bean.DocItem;
|
import com.gitee.sop.websiteserver.bean.DocItem;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -14,7 +14,7 @@ public interface DocManager {
|
|||||||
|
|
||||||
DocItem get(String method, String version);
|
DocItem get(String method, String version);
|
||||||
|
|
||||||
DocModule getByTitle(String title);
|
DocInfo getByTitle(String title);
|
||||||
|
|
||||||
Collection<DocModule> listAll();
|
Collection<DocInfo> listAll();
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,27 @@
|
|||||||
package com.gitee.sop.websiteserver.manager;
|
package com.gitee.sop.websiteserver.manager;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocInfo;
|
||||||
import com.gitee.sop.websiteserver.bean.DocItem;
|
import com.gitee.sop.websiteserver.bean.DocItem;
|
||||||
import com.gitee.sop.websiteserver.bean.DocModule;
|
|
||||||
import com.gitee.sop.websiteserver.bean.DocParameter;
|
|
||||||
import com.gitee.sop.websiteserver.bean.EurekaApplication;
|
import com.gitee.sop.websiteserver.bean.EurekaApplication;
|
||||||
import com.gitee.sop.websiteserver.bean.EurekaApps;
|
import com.gitee.sop.websiteserver.bean.EurekaApps;
|
||||||
import com.gitee.sop.websiteserver.bean.EurekaInstance;
|
import com.gitee.sop.websiteserver.bean.EurekaInstance;
|
||||||
import com.gitee.sop.websiteserver.bean.EurekaUri;
|
import com.gitee.sop.websiteserver.bean.EurekaUri;
|
||||||
|
import com.gitee.sop.websiteserver.bean.ZookeeperContext;
|
||||||
import com.gitee.sop.websiteserver.vo.ServiceInfoVO;
|
import com.gitee.sop.websiteserver.vo.ServiceInfoVO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestClientException;
|
import org.springframework.util.DigestUtils;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
@@ -30,9 +31,11 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.concurrent.DelayQueue;
|
||||||
import java.util.Set;
|
import java.util.concurrent.Delayed;
|
||||||
import java.util.function.Function;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,8 +45,8 @@ import java.util.stream.Collectors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class DocManagerImpl implements DocManager {
|
public class DocManagerImpl implements DocManager {
|
||||||
|
|
||||||
// key:module
|
// key:title
|
||||||
Map<String, DocModule> docDefinitionMap = new HashMap<>();
|
Map<String, DocInfo> docDefinitionMap = new HashMap<>();
|
||||||
|
|
||||||
// key: name+version
|
// key: name+version
|
||||||
Map<String, DocItem> docItemMap = new HashMap<>();
|
Map<String, DocItem> docItemMap = new HashMap<>();
|
||||||
@@ -53,11 +56,23 @@ public class DocManagerImpl implements DocManager {
|
|||||||
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
DocParser swaggerDocParser = new SwaggerDocParser();
|
||||||
|
|
||||||
|
DocParser easyopenDocParser = new EasyopenDocParser();
|
||||||
|
|
||||||
|
ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
DelayQueue<Msg> queue = new DelayQueue<>();
|
||||||
|
|
||||||
|
private String secret = "b749a2ec000f4f29";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
|
|
||||||
private String eurekaUrl;
|
private String eurekaUrl;
|
||||||
|
|
||||||
|
private volatile boolean listenInited;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
try {
|
try {
|
||||||
@@ -68,97 +83,45 @@ public class DocManagerImpl implements DocManager {
|
|||||||
ServiceInfoVO serviceInfoVo = entry.getValue().get(0);
|
ServiceInfoVO serviceInfoVo = entry.getValue().get(0);
|
||||||
loadDocInfo(serviceInfoVo);
|
loadDocInfo(serviceInfoVo);
|
||||||
}
|
}
|
||||||
Map<String, DocItem> itemMap = docDefinitionMap.values()
|
} catch (Exception e) {
|
||||||
.stream()
|
|
||||||
.map(DocModule::getDocItems)
|
|
||||||
.flatMap(docItems -> docItems.stream())
|
|
||||||
.collect(Collectors.toMap(DocItem::getNameVersion, Function.identity()));
|
|
||||||
this.docItemMap.putAll(itemMap);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("加载失败", e);
|
log.error("加载失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadDocInfo(ServiceInfoVO serviceInfoVo) {
|
protected void loadDocInfo(ServiceInfoVO serviceInfoVo) {
|
||||||
String url = "http://" + serviceInfoVo.getIpAddr() + ":" + serviceInfoVo.getServerPort() + "/v2/api-docs";
|
String query = this.buildQuery();
|
||||||
|
String url = "http://" + serviceInfoVo.getIpAddr() + ":" + serviceInfoVo.getServerPort() + "/v2/api-docs" + query;
|
||||||
try {
|
try {
|
||||||
ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
|
ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
|
||||||
|
if (entity.getStatusCode() != HttpStatus.OK) {
|
||||||
|
throw new IllegalAccessException("无权访问");
|
||||||
|
}
|
||||||
String docInfoJson = entity.getBody();
|
String docInfoJson = entity.getBody();
|
||||||
DocModule docDefinition = this.parseDocJson(docInfoJson);
|
JSONObject docRoot = JSON.parseObject(docInfoJson);
|
||||||
docDefinitionMap.put(docDefinition.getModule(), docDefinition);
|
DocParser docParser = this.buildDocParser(docRoot);
|
||||||
} catch (RestClientException e) {
|
DocInfo docInfo = docParser.parseJson(docRoot);
|
||||||
|
docDefinitionMap.put(docInfo.getTitle(), docInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
// 这里报错可能是因为有些微服务没有配置swagger文档,导致404访问不到
|
// 这里报错可能是因为有些微服务没有配置swagger文档,导致404访问不到
|
||||||
// 这里catch跳过即可
|
// 这里catch跳过即可
|
||||||
log.warn("读取文档失败, url:{}", url, e);
|
log.warn("读取文档失败, url:{}, msg:{}", url, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DocModule parseDocJson(String docInfoJson) {
|
protected String buildQuery() {
|
||||||
JSONObject docRoot = JSON.parseObject(docInfoJson);
|
String time = String.valueOf(System.currentTimeMillis());
|
||||||
String title = docRoot.getJSONObject("info").getString("title");
|
String source = secret + time + secret;
|
||||||
List<DocItem> docItems = new ArrayList<>();
|
String sign = DigestUtils.md5DigestAsHex(source.getBytes());
|
||||||
|
return "?time=" + time + "&sign=" + sign;
|
||||||
JSONObject paths = docRoot.getJSONObject("paths");
|
|
||||||
Set<String> pathNameSet = paths.keySet();
|
|
||||||
for (String pathName : pathNameSet) {
|
|
||||||
JSONObject pathInfo = paths.getJSONObject(pathName);
|
|
||||||
Set<String> pathSet = pathInfo.keySet();
|
|
||||||
Optional<String> first = pathSet.stream().findFirst();
|
|
||||||
if (first.isPresent()) {
|
|
||||||
String path = first.get();
|
|
||||||
JSONObject docInfo = pathInfo.getJSONObject(path);
|
|
||||||
DocItem docItem = buildDocItem(docInfo, docRoot);
|
|
||||||
docItems.add(docItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DocModule docDefinition = new DocModule();
|
protected DocParser buildDocParser(JSONObject rootDoc) {
|
||||||
docDefinition.setModule(title);
|
Object easyopen = rootDoc.get("easyopen");
|
||||||
docDefinition.setDocItems(docItems);
|
if (easyopen != null) {
|
||||||
return docDefinition;
|
return easyopenDocParser;
|
||||||
|
} else {
|
||||||
|
return swaggerDocParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DocItem buildDocItem(JSONObject docInfo, JSONObject docRoot) {
|
|
||||||
DocItem docItem = new DocItem();
|
|
||||||
docItem.setName(docInfo.getString("sop_name"));
|
|
||||||
docItem.setVersion(docInfo.getString("sop_version"));
|
|
||||||
docItem.setSummary(docInfo.getString("summary"));
|
|
||||||
docItem.setDescription(docInfo.getString("description"));
|
|
||||||
Optional<JSONArray> parametersOptional = Optional.ofNullable(docInfo.getJSONArray("parameters"));
|
|
||||||
JSONArray parameters = parametersOptional.orElse(new JSONArray());
|
|
||||||
List<DocParameter> docParameterList = parameters.toJavaList(DocParameter.class);
|
|
||||||
docItem.setRequestParameters(docParameterList);
|
|
||||||
|
|
||||||
List<DocParameter> responseParameterList = this.buildResponseParameterList(docInfo, docRoot);
|
|
||||||
docItem.setResponseParameters(responseParameterList);
|
|
||||||
|
|
||||||
return docItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<DocParameter> buildResponseParameterList(JSONObject docInfo, JSONObject docRoot) {
|
|
||||||
String responseRef = getResponseRef(docInfo);
|
|
||||||
List<DocParameter> respParameterList = new ArrayList<>();
|
|
||||||
if (StringUtils.isNotBlank(responseRef)) {
|
|
||||||
JSONObject responseObject = docRoot.getJSONObject("definitions").getJSONObject(responseRef);
|
|
||||||
JSONObject properties = responseObject.getJSONObject("properties");
|
|
||||||
Set<String> fieldNames = properties.keySet();
|
|
||||||
for (String fieldName : fieldNames) {
|
|
||||||
JSONObject fieldInfo = properties.getJSONObject(fieldName);
|
|
||||||
DocParameter respParam = fieldInfo.toJavaObject(DocParameter.class);
|
|
||||||
respParam.setName(fieldName);
|
|
||||||
respParameterList.add(respParam);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return respParameterList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -167,12 +130,12 @@ public class DocManagerImpl implements DocManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DocModule getByTitle(String title) {
|
public DocInfo getByTitle(String title) {
|
||||||
return docDefinitionMap.get(title);
|
return docDefinitionMap.get(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<DocModule> listAll() {
|
public Collection<DocInfo> listAll() {
|
||||||
return docDefinitionMap.values();
|
return docDefinitionMap.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,12 +149,14 @@ public class DocManagerImpl implements DocManager {
|
|||||||
.forEach(eurekaApplication -> {
|
.forEach(eurekaApplication -> {
|
||||||
List<EurekaInstance> instanceList = eurekaApplication.getInstance();
|
List<EurekaInstance> instanceList = eurekaApplication.getInstance();
|
||||||
for (EurekaInstance instance : instanceList) {
|
for (EurekaInstance instance : instanceList) {
|
||||||
|
if ("UP".equals(instance.getStatus())) {
|
||||||
ServiceInfoVO vo = new ServiceInfoVO();
|
ServiceInfoVO vo = new ServiceInfoVO();
|
||||||
vo.setName(eurekaApplication.getName());
|
vo.setName(eurekaApplication.getName());
|
||||||
vo.setIpAddr(instance.getIpAddr());
|
vo.setIpAddr(instance.getIpAddr());
|
||||||
vo.setServerPort(instance.fetchPort());
|
vo.setServerPort(instance.fetchPort());
|
||||||
serviceInfoVoList.add(vo);
|
serviceInfoVoList.add(vo);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, List<ServiceInfoVO>> listMap = serviceInfoVoList.stream()
|
Map<String, List<ServiceInfoVO>> listMap = serviceInfoVoList.stream()
|
||||||
@@ -200,7 +165,7 @@ public class DocManagerImpl implements DocManager {
|
|||||||
return listMap;
|
return listMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String requestEurekaServer(EurekaUri eurekaUri, String... args) throws IOException {
|
protected String requestEurekaServer(EurekaUri eurekaUri, String... args) throws IOException {
|
||||||
Request request = eurekaUri.getRequest(this.eurekaUrl, args);
|
Request request = eurekaUri.getRequest(this.eurekaUrl, args);
|
||||||
Response response = client.newCall(request).execute();
|
Response response = client.newCall(request).execute();
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
@@ -212,7 +177,7 @@ public class DocManagerImpl implements DocManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
protected void after() {
|
protected void after() throws Exception {
|
||||||
String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone");
|
String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone");
|
||||||
if (StringUtils.isBlank(eurekaUrls)) {
|
if (StringUtils.isBlank(eurekaUrls)) {
|
||||||
throw new IllegalArgumentException("未指定eureka.client.serviceUrl.defaultZone参数");
|
throw new IllegalArgumentException("未指定eureka.client.serviceUrl.defaultZone参数");
|
||||||
@@ -222,5 +187,82 @@ public class DocManagerImpl implements DocManager {
|
|||||||
url = eurekaUrls.substring(0, eurekaUrls.length() - 1);
|
url = eurekaUrls.substring(0, eurekaUrls.length() - 1);
|
||||||
}
|
}
|
||||||
this.eurekaUrl = url;
|
this.eurekaUrl = url;
|
||||||
|
|
||||||
|
this.listenServiceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听serviceId更改
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected void listenServiceId() throws Exception {
|
||||||
|
|
||||||
|
executorService.execute(new Consumer(queue, this));
|
||||||
|
|
||||||
|
ZookeeperContext.setEnvironment(environment);
|
||||||
|
String routeRootPath = ZookeeperContext.getRouteRootPath();
|
||||||
|
// 如果节点内容有变化则自动更新文档
|
||||||
|
ZookeeperContext.listenChildren(routeRootPath, 1, (client, event) -> {
|
||||||
|
if (listenInited) {
|
||||||
|
long id = System.currentTimeMillis();
|
||||||
|
// 延迟20秒执行
|
||||||
|
queue.offer(new Msg(id, 1000 * 20));
|
||||||
|
}
|
||||||
|
TreeCacheEvent.Type type = event.getType();
|
||||||
|
if (type == TreeCacheEvent.Type.INITIALIZED) {
|
||||||
|
listenInited = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Msg implements Delayed {
|
||||||
|
private long id;
|
||||||
|
private long delay;
|
||||||
|
|
||||||
|
// 自定义实现比较方法返回 1 0 -1三个参数
|
||||||
|
|
||||||
|
|
||||||
|
public Msg(long id, long delay) {
|
||||||
|
this.id = id;
|
||||||
|
this.delay = delay + System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Delayed delayed) {
|
||||||
|
Msg msg = (Msg) delayed;
|
||||||
|
return Long.valueOf(this.id) > Long.valueOf(msg.id) ? 1
|
||||||
|
: (Long.valueOf(this.id) < Long.valueOf(msg.id) ? -1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延迟任务是否到时就是按照这个方法判断如果返回的是负数则说明到期否则还没到期
|
||||||
|
@Override
|
||||||
|
public long getDelay(TimeUnit unit) {
|
||||||
|
return unit.convert(this.delay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
static class Consumer implements Runnable {
|
||||||
|
private DelayQueue<Msg> queue;
|
||||||
|
private DocManager docManager;
|
||||||
|
|
||||||
|
public Consumer(DelayQueue<Msg> queue, DocManager docManager) {
|
||||||
|
this.queue = queue;
|
||||||
|
this.docManager = docManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
queue.take();
|
||||||
|
log.info("延迟队列触发--重新加载文档信息");
|
||||||
|
docManager.load();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,11 @@
|
|||||||
|
package com.gitee.sop.websiteserver.manager;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public interface DocParser {
|
||||||
|
DocInfo parseJson(JSONObject docRoot);
|
||||||
|
}
|
@@ -0,0 +1,83 @@
|
|||||||
|
package com.gitee.sop.websiteserver.manager;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocInfo;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocItem;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocModule;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocParameter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class EasyopenDocParser implements DocParser {
|
||||||
|
@Override
|
||||||
|
public DocInfo parseJson(JSONObject docRoot) {
|
||||||
|
String title = docRoot.getString("title");
|
||||||
|
List<DocItem> docItems = new ArrayList<>();
|
||||||
|
JSONArray apiModules = docRoot.getJSONArray("apiModules");
|
||||||
|
for (int i = 0; i < apiModules.size(); i++) {
|
||||||
|
JSONObject module = apiModules.getJSONObject(i);
|
||||||
|
JSONArray moduleItems = module.getJSONArray("moduleItems");
|
||||||
|
for (int k = 0; k < moduleItems.size(); k++) {
|
||||||
|
JSONObject docInfo = moduleItems.getJSONObject(k);
|
||||||
|
DocItem docItem = buildDocItem(docInfo);
|
||||||
|
docItem.setModule(module.getString("name"));
|
||||||
|
docItems.add(docItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DocModule> docModuleList = docItems.stream()
|
||||||
|
.collect(Collectors.groupingBy(DocItem::getModule))
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> {
|
||||||
|
DocModule docModule = new DocModule();
|
||||||
|
docModule.setModule(entry.getKey());
|
||||||
|
docModule.setDocItems(entry.getValue());
|
||||||
|
return docModule;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
DocInfo docInfo = new DocInfo();
|
||||||
|
docInfo.setTitle(title);
|
||||||
|
docInfo.setDocModuleList(docModuleList);
|
||||||
|
return docInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DocItem buildDocItem(JSONObject docInfo) {
|
||||||
|
DocItem docItem = new DocItem();
|
||||||
|
docItem.setName(docInfo.getString("name"));
|
||||||
|
docItem.setVersion(docInfo.getString("version"));
|
||||||
|
docItem.setSummary(docInfo.getString("description"));
|
||||||
|
docItem.setDescription(docInfo.getString("description"));
|
||||||
|
List<DocParameter> docParameterList = this.buildParameterList(docInfo, "paramDefinitions");
|
||||||
|
docItem.setRequestParameters(docParameterList);
|
||||||
|
|
||||||
|
List<DocParameter> responseParameterList = this.buildParameterList(docInfo, "resultDefinitions");
|
||||||
|
docItem.setResponseParameters(responseParameterList);
|
||||||
|
|
||||||
|
return docItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<DocParameter> buildParameterList(JSONObject docInfo, String key) {
|
||||||
|
JSONArray params = docInfo.getJSONArray(key);
|
||||||
|
if (params == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<DocParameter> docParameterList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < params.size(); i++) {
|
||||||
|
JSONObject jsonObject = params.getJSONObject(i);
|
||||||
|
DocParameter docParameter = jsonObject.toJavaObject(DocParameter.class);
|
||||||
|
docParameter.setType(jsonObject.getString("dataType"));
|
||||||
|
docParameterList.add(docParameter);
|
||||||
|
}
|
||||||
|
return docParameterList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,115 @@
|
|||||||
|
package com.gitee.sop.websiteserver.manager;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocInfo;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocItem;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocModule;
|
||||||
|
import com.gitee.sop.websiteserver.bean.DocParameter;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析swagger的json内容
|
||||||
|
*
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class SwaggerDocParser implements DocParser {
|
||||||
|
@Override
|
||||||
|
public DocInfo parseJson(JSONObject docRoot) {
|
||||||
|
String title = docRoot.getJSONObject("info").getString("title");
|
||||||
|
List<DocItem> docItems = new ArrayList<>();
|
||||||
|
|
||||||
|
JSONObject paths = docRoot.getJSONObject("paths");
|
||||||
|
Set<String> pathNameSet = paths.keySet();
|
||||||
|
for (String pathName : pathNameSet) {
|
||||||
|
JSONObject pathInfo = paths.getJSONObject(pathName);
|
||||||
|
Set<String> pathSet = pathInfo.keySet();
|
||||||
|
Optional<String> first = pathSet.stream().findFirst();
|
||||||
|
if (first.isPresent()) {
|
||||||
|
String path = first.get();
|
||||||
|
JSONObject docInfo = pathInfo.getJSONObject(path);
|
||||||
|
DocItem docItem = buildDocItem(docInfo, docRoot);
|
||||||
|
docItems.add(docItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DocModule> docModuleList = docItems.stream()
|
||||||
|
.collect(Collectors.groupingBy(DocItem::getModule))
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> {
|
||||||
|
DocModule docModule = new DocModule();
|
||||||
|
docModule.setModule(entry.getKey());
|
||||||
|
docModule.setDocItems(entry.getValue());
|
||||||
|
return docModule;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|
||||||
|
DocInfo docInfo = new DocInfo();
|
||||||
|
docInfo.setTitle(title);
|
||||||
|
docInfo.setDocModuleList(docModuleList);
|
||||||
|
return docInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DocItem buildDocItem(JSONObject docInfo, JSONObject docRoot) {
|
||||||
|
DocItem docItem = new DocItem();
|
||||||
|
docItem.setName(docInfo.getString("sop_name"));
|
||||||
|
docItem.setVersion(docInfo.getString("sop_version"));
|
||||||
|
docItem.setSummary(docInfo.getString("summary"));
|
||||||
|
docItem.setDescription(docInfo.getString("description"));
|
||||||
|
String moduleName = this.buildModuleName(docInfo, docRoot);
|
||||||
|
docItem.setModule(moduleName);
|
||||||
|
Optional<JSONArray> parametersOptional = Optional.ofNullable(docInfo.getJSONArray("parameters"));
|
||||||
|
JSONArray parameters = parametersOptional.orElse(new JSONArray());
|
||||||
|
List<DocParameter> docParameterList = parameters.toJavaList(DocParameter.class);
|
||||||
|
docItem.setRequestParameters(docParameterList);
|
||||||
|
|
||||||
|
List<DocParameter> responseParameterList = this.buildResponseParameterList(docInfo, docRoot);
|
||||||
|
docItem.setResponseParameters(responseParameterList);
|
||||||
|
|
||||||
|
return docItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String buildModuleName(JSONObject docInfo, JSONObject docRoot) {
|
||||||
|
String title = docRoot.getJSONObject("info").getString("title");
|
||||||
|
JSONArray tags = docInfo.getJSONArray("tags");
|
||||||
|
if (tags != null && tags.size() > 0) {
|
||||||
|
return tags.getString(0);
|
||||||
|
}
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<DocParameter> buildResponseParameterList(JSONObject docInfo, JSONObject docRoot) {
|
||||||
|
String responseRef = getResponseRef(docInfo);
|
||||||
|
List<DocParameter> respParameterList = new ArrayList<>();
|
||||||
|
if (StringUtils.isNotBlank(responseRef)) {
|
||||||
|
JSONObject responseObject = docRoot.getJSONObject("definitions").getJSONObject(responseRef);
|
||||||
|
JSONObject properties = responseObject.getJSONObject("properties");
|
||||||
|
Set<String> fieldNames = properties.keySet();
|
||||||
|
for (String fieldName : fieldNames) {
|
||||||
|
JSONObject fieldInfo = properties.getJSONObject(fieldName);
|
||||||
|
DocParameter respParam = fieldInfo.toJavaObject(DocParameter.class);
|
||||||
|
respParam.setName(fieldName);
|
||||||
|
respParameterList.add(respParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return respParameterList;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -11,5 +11,5 @@ import java.util.List;
|
|||||||
public class DocBaseInfoVO {
|
public class DocBaseInfoVO {
|
||||||
private String urlTest;
|
private String urlTest;
|
||||||
private String urlProd;
|
private String urlProd;
|
||||||
private List<DocModuleVO> docModuleVOList;
|
private List<DocInfoVO> docInfoList;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,6 @@ import lombok.Data;
|
|||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class DocModuleVO {
|
public class DocInfoVO {
|
||||||
private String module;
|
private String title;
|
||||||
}
|
}
|
Reference in New Issue
Block a user