自定义路由

1.x
tanghc 6 years ago
parent 3ee105a092
commit b21a02c4a5
  1. 12
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/IsvApi.java
  2. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitApi.java
  3. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitNewApi.java
  4. 12
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java
  5. 101
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/RouteApi.java
  6. 56
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java
  7. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitNewAddParam.java
  8. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitNewUpdateParam.java
  9. 76
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteAddParam.java
  10. 22
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteDeleteParam.java
  11. 31
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteUpdateParam.java
  12. 21
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ServiceAddParam.java
  13. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ServiceInfo.java
  14. 15
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/GatewayRouteDefinition.java
  15. 3
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ServiceRouteInfo.java
  16. 136
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ZookeeperContext.java
  17. 33
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/BizException.java
  18. 26
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/ZookeeperOperationException.java
  19. 10
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/ZookeeperPathExistException.java
  20. 10
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/ZookeeperPathNotExistException.java
  21. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/RouteConfigService.java
  22. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/RouteService.java
  23. 2
      sop-admin/sop-admin-server/src/main/resources/public/index.html
  24. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-29b24c82.857979fb.css
  25. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-2a9bc230.2dae711a.css
  26. 0
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-37401378.a43114f3.css
  27. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-6a68a33e.3b12267b.css
  28. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.3225a72c.js
  29. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-29b24c82.2b3ac7ca.js
  30. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2a9bc230.6f8c0d92.js
  31. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.447435b3.js
  32. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-37401378.4e39ec9b.js
  33. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-6a68a33e.8b63492e.js
  34. 10
      sop-admin/sop-admin-vue/src/utils/global.js
  35. 12
      sop-admin/sop-admin-vue/src/views/service/limit/index2.vue
  36. 371
      sop-admin/sop-admin-vue/src/views/service/route/index.vue

@ -7,7 +7,6 @@ import com.gitee.easyopen.doc.DataType;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocField; import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil; import com.gitee.easyopen.util.CopyUtil;
import com.gitee.easyopen.util.KeyStore; import com.gitee.easyopen.util.KeyStore;
import com.gitee.easyopen.util.RSAUtil; import com.gitee.easyopen.util.RSAUtil;
@ -24,7 +23,10 @@ import com.gitee.sop.adminserver.api.isv.result.IsvVO;
import com.gitee.sop.adminserver.api.isv.result.RoleVO; import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.bean.ChannelMsg; import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.ZookeeperContext; import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.IdGen; import com.gitee.sop.adminserver.common.IdGen;
import com.gitee.sop.adminserver.common.ZookeeperOperationException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.IsvInfo; import com.gitee.sop.adminserver.entity.IsvInfo;
import com.gitee.sop.adminserver.entity.PermIsvRole; import com.gitee.sop.adminserver.entity.PermIsvRole;
import com.gitee.sop.adminserver.entity.PermRole; import com.gitee.sop.adminserver.entity.PermRole;
@ -133,7 +135,7 @@ public class IsvApi {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void addIsv(IsvInfoFormAdd param) throws Exception { public void addIsv(IsvInfoFormAdd param) throws Exception {
if (isvInfoMapper.getByColumn("app_key", param.getAppKey()) != null) { if (isvInfoMapper.getByColumn("app_key", param.getAppKey()) != null) {
throw new ApiException("appKey已存在"); throw new BizException("appKey已存在");
} }
formatForm(param); formatForm(param);
IsvInfo rec = new IsvInfo(); IsvInfo rec = new IsvInfo();
@ -175,9 +177,9 @@ public class IsvApi {
try { try {
log.info("消息推送--ISV信息(update), path:{}, data:{}", path, data); log.info("消息推送--ISV信息(update), path:{}, data:{}", path, data);
ZookeeperContext.updatePathData(path, data); ZookeeperContext.updatePathData(path, data);
} catch (Exception e) { } catch (ZookeeperPathNotExistException e) {
log.error("发送isvChannelMsg失败, path:{}, msg:{}", path, data, e); log.error("发送isvChannelMsg失败, path:{}, msg:{}", path, data, e);
throw new ApiException("保存失败,请查看日志"); throw new BizException("路径不存在");
} }
} }
@ -222,7 +224,7 @@ public class IsvApi {
routePermissionService.sendIsvRolePermissionToZookeeper(isvInfo.getAppKey(), roleCodeList); routePermissionService.sendIsvRolePermissionToZookeeper(isvInfo.getAppKey(), roleCodeList);
} catch (Exception e) { } catch (Exception e) {
log.error("保存到zookeeper中失败,isvInfo:{}, roleCodeList:{}", isvInfo, roleCodeList); log.error("保存到zookeeper中失败,isvInfo:{}, roleCodeList:{}", isvInfo, roleCodeList);
throw new ApiException("保存失败,请查看日志"); throw new BizException("保存失败,请查看日志");
} }
} }
} }

@ -4,7 +4,6 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService; import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil; import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query; import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.service.param.LimitParam; import com.gitee.sop.adminserver.api.service.param.LimitParam;
@ -12,6 +11,7 @@ import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.api.service.result.LimitVO; import com.gitee.sop.adminserver.api.service.result.LimitVO;
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition; import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
import com.gitee.sop.adminserver.bean.RouteConfigDto; import com.gitee.sop.adminserver.bean.RouteConfigDto;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.LimitEnum; import com.gitee.sop.adminserver.common.LimitEnum;
import com.gitee.sop.adminserver.entity.ConfigRouteLimit; import com.gitee.sop.adminserver.entity.ConfigRouteLimit;
import com.gitee.sop.adminserver.mapper.ConfigRouteLimitMapper; import com.gitee.sop.adminserver.mapper.ConfigRouteLimitMapper;
@ -118,7 +118,7 @@ public class LimitApi {
routeConfigService.sendRouteConfigMsg(routeConfigDto); routeConfigService.sendRouteConfigMsg(routeConfigDto);
} catch (Exception e) { } catch (Exception e) {
log.error("推送限流消息错误, param:{}", param, e); log.error("推送限流消息错误, param:{}", param, e);
throw new ApiException("修改失败,请查看日志"); throw new BizException("修改失败,请查看日志");
} }
} }
} }

@ -4,7 +4,6 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService; import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil; import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.PageInfo; import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.Query; import com.gitee.fastmybatis.core.query.Query;
@ -15,6 +14,7 @@ import com.gitee.sop.adminserver.api.service.param.LimitNewParam;
import com.gitee.sop.adminserver.api.service.param.LimitNewUpdateParam; import com.gitee.sop.adminserver.api.service.param.LimitNewUpdateParam;
import com.gitee.sop.adminserver.api.service.result.LimitNewVO; import com.gitee.sop.adminserver.api.service.result.LimitNewVO;
import com.gitee.sop.adminserver.bean.ConfigLimitDto; import com.gitee.sop.adminserver.bean.ConfigLimitDto;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.ConfigLimit; import com.gitee.sop.adminserver.entity.ConfigLimit;
import com.gitee.sop.adminserver.mapper.ConfigLimitMapper; import com.gitee.sop.adminserver.mapper.ConfigLimitMapper;
import com.gitee.sop.adminserver.service.RouteConfigService; import com.gitee.sop.adminserver.service.RouteConfigService;
@ -65,7 +65,7 @@ public class LimitNewApi {
routeConfigService.sendLimitConfigMsg(configLimitDto); routeConfigService.sendLimitConfigMsg(configLimitDto);
} catch (Exception e) { } catch (Exception e) {
log.error("推送限流消息错误, param:{}", param, e); log.error("推送限流消息错误, param:{}", param, e);
throw new ApiException("新增失败,请查看日志"); throw new BizException("新增失败,请查看日志");
} }
} }
@ -87,7 +87,7 @@ public class LimitNewApi {
routeConfigService.sendLimitConfigMsg(configLimitDto); routeConfigService.sendLimitConfigMsg(configLimitDto);
} catch (Exception e) { } catch (Exception e) {
log.error("推送限流消息错误, param:{}", param, e); log.error("推送限流消息错误, param:{}", param, e);
throw new ApiException("修改失败,请查看日志"); throw new BizException("修改失败,请查看日志");
} }
} }

@ -6,11 +6,11 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService; import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.verify.DefaultMd5Verifier; import com.gitee.easyopen.verify.DefaultMd5Verifier;
import com.gitee.fastmybatis.core.query.Query; import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.service.param.LogMonitorInstanceAddParam; import com.gitee.sop.adminserver.api.service.param.LogMonitorInstanceAddParam;
import com.gitee.sop.adminserver.api.service.result.LogMonitorInstanceVO; import com.gitee.sop.adminserver.api.service.result.LogMonitorInstanceVO;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.QueryUtil; import com.gitee.sop.adminserver.common.QueryUtil;
import com.gitee.sop.adminserver.entity.ConfigCommon; import com.gitee.sop.adminserver.entity.ConfigCommon;
import com.gitee.sop.adminserver.mapper.ConfigCommonMapper; import com.gitee.sop.adminserver.mapper.ConfigCommonMapper;
@ -80,7 +80,7 @@ public class LogApi {
} }
} catch (Exception e) { } catch (Exception e) {
log.error("获取日志信息出错", e); log.error("获取日志信息出错", e);
throw new ApiException("获取日志信息出错"); throw new BizException("获取日志信息出错");
} }
} }
Collections.sort(ret, Comparator.comparing(LogMonitorInstanceVO::getCount)); Collections.sort(ret, Comparator.comparing(LogMonitorInstanceVO::getCount));
@ -98,7 +98,7 @@ public class LogApi {
String ipPort = configCommon.getConfigKey(); String ipPort = configCommon.getConfigKey();
this.requestLogServer(ipPort, "clearErrors"); this.requestLogServer(ipPort, "clearErrors");
} catch (Exception e) { } catch (Exception e) {
throw new ApiException("清除失败"); throw new BizException("清除失败");
} }
} }
@ -122,7 +122,7 @@ public class LogApi {
.eq("config_key", ipPort); .eq("config_key", ipPort);
ConfigCommon rec = configCommonMapper.getByQuery(query); ConfigCommon rec = configCommonMapper.getByQuery(query);
if (rec != null) { if (rec != null) {
throw new ApiException("该实例已添加"); throw new BizException("该实例已添加");
} }
ConfigCommon configCommon = new ConfigCommon(); ConfigCommon configCommon = new ConfigCommon();
configCommon.setConfigGroup(LOG_MONITOR_INSTANCE); configCommon.setConfigGroup(LOG_MONITOR_INSTANCE);
@ -137,11 +137,11 @@ public class LogApi {
JSONObject jsonObject = JSON.parseObject(json); JSONObject jsonObject = JSON.parseObject(json);
if (!CODE_SUCCESS.equals(jsonObject.getString("code"))) { if (!CODE_SUCCESS.equals(jsonObject.getString("code"))) {
log.error("请求结果:{}", json); log.error("请求结果:{}", json);
throw new ApiException("添加失败"); throw new BizException("添加失败");
} }
} catch (Exception e) { } catch (Exception e) {
log.error("添加失败", e); log.error("添加失败", e);
throw new ApiException("添加失败"); throw new BizException("添加失败");
} }
} }

@ -5,18 +5,21 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService; import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil; import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query; import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.isv.result.RoleVO; import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.api.service.param.RouteParam; import com.gitee.sop.adminserver.api.service.param.RouteAddParam;
import com.gitee.sop.adminserver.api.service.param.RouteDeleteParam;
import com.gitee.sop.adminserver.api.service.param.RoutePermissionParam; import com.gitee.sop.adminserver.api.service.param.RoutePermissionParam;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam; import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.api.service.param.RouteUpdateParam;
import com.gitee.sop.adminserver.api.service.result.RouteVO; import com.gitee.sop.adminserver.api.service.result.RouteVO;
import com.gitee.sop.adminserver.api.service.result.ServiceInfo;
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition; import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
import com.gitee.sop.adminserver.bean.RouteConfigDto; import com.gitee.sop.adminserver.bean.RouteConfigDto;
import com.gitee.sop.adminserver.bean.ZookeeperContext; import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.ConfigRouteBase; import com.gitee.sop.adminserver.entity.ConfigRouteBase;
import com.gitee.sop.adminserver.entity.PermRole; import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.entity.PermRolePermission; import com.gitee.sop.adminserver.entity.PermRolePermission;
@ -28,14 +31,13 @@ import com.gitee.sop.adminserver.service.RoutePermissionService;
import com.gitee.sop.adminserver.service.RouteService; import com.gitee.sop.adminserver.service.RouteService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
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;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -96,67 +98,84 @@ public class RouteApi {
return routeDefinitionList; return routeDefinitionList;
} }
@Api(name = "route.add")
@ApiDocMethod(description = "新增路由")
void addRoute(RouteAddParam param) {
String id = param.getName() + param.getVersion();
String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), id);
GatewayRouteDefinition routeDefinition = new GatewayRouteDefinition();
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
routeDefinition.setId(id);
routeDefinition.setCustom(1);
try {
ZookeeperContext.addPath(routePath, JSON.toJSONString(routeDefinition));
} catch (ZookeeperPathExistException e) {
throw new BizException("路由已存在");
}
this.updateRouteConfig(routeDefinition);
}
@Api(name = "route.update") @Api(name = "route.update")
@ApiDocMethod(description = "修改路由") @ApiDocMethod(description = "修改路由")
void updateRoute(RouteParam param) throws Exception { void updateRoute(RouteUpdateParam param) {
String serviceIdPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId(); String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
String zookeeperRoutePath = serviceIdPath + "/" + param.getId(); GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
String data = ZookeeperContext.getData(zookeeperRoutePath);
GatewayRouteDefinition routeDefinition = JSON.parseObject(data, GatewayRouteDefinition.class);
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition); CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
ZookeeperContext.updatePathData(zookeeperRoutePath, JSON.toJSONString(routeDefinition)); try {
ZookeeperContext.updatePathData(routePath, JSON.toJSONString(routeDefinition));
} catch (ZookeeperPathNotExistException e) {
throw new BizException("路由不存在");
}
this.updateRouteConfig(routeDefinition);
}
this.updateRouteConfig(param); @Api(name = "route.del")
@ApiDocMethod(description = "删除路由")
void delRoute(RouteDeleteParam param) {
String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
if (!BooleanUtils.toBoolean(routeDefinition.getCustom())) {
throw new BizException("非自定义路由,无法删除");
}
ZookeeperContext.deletePathDeep(routePath);
} }
@Api(name = "route.add") private GatewayRouteDefinition getGatewayRouteDefinition(String zookeeperRoutePath) {
@ApiDocMethod(description = "新增路由") String data = null;
void addRoute(RouteParam param) throws Exception { try {
String serviceIdPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId(); data = ZookeeperContext.getData(zookeeperRoutePath);
String zookeeperRoutePath = serviceIdPath + "/" + param.getId(); } catch (ZookeeperPathNotExistException e) {
if (ZookeeperContext.isPathExist(zookeeperRoutePath)) { throw new BizException("路由不存在");
throw new ApiException("id已存在");
} }
GatewayRouteDefinition routeDefinition = new GatewayRouteDefinition(); return JSON.parseObject(data, GatewayRouteDefinition.class);
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
ZookeeperContext.createNewData(zookeeperRoutePath, JSON.toJSONString(routeDefinition));
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setServiceId(param.getServiceId());
serviceInfo.setDescription(param.getServiceId());
String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
serviceInfo.setCreateTime(now);
serviceInfo.setUpdateTime(now);
ZookeeperContext.updatePathData(serviceIdPath, JSON.toJSONString(serviceInfo));
this.updateRouteConfig(param);
} }
private void updateRouteConfig(RouteParam param) { private void updateRouteConfig(GatewayRouteDefinition routeDefinition) {
try { try {
String routeId = param.getId(); String routeId = routeDefinition.getId();
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId); ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId);
boolean doSave = configRouteBase == null; boolean doSave = configRouteBase == null;
if (doSave) { if (doSave) {
configRouteBase = new ConfigRouteBase(); configRouteBase = new ConfigRouteBase();
configRouteBase.setRouteId(param.getId()); configRouteBase.setRouteId(routeId);
} }
configRouteBase.setStatus(param.getStatus().byteValue()); configRouteBase.setStatus((byte) routeDefinition.getStatus());
int i = doSave ? configRouteBaseMapper.save(configRouteBase) int i = doSave ? configRouteBaseMapper.save(configRouteBase)
: configRouteBaseMapper.update(configRouteBase); : configRouteBaseMapper.update(configRouteBase);
if (i > 0) { if (i > 0) {
this.sendMsg(param); this.sendMsg(routeDefinition);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("发送msg失败", e); log.error("发送msg失败", e);
} }
} }
private void sendMsg(RouteParam param) throws Exception { private void sendMsg(GatewayRouteDefinition routeDefinition) {
RouteConfigDto routeConfigDto = new RouteConfigDto(); RouteConfigDto routeConfigDto = new RouteConfigDto();
routeConfigDto.setRouteId(param.getId()); routeConfigDto.setRouteId(routeDefinition.getId());
routeConfigDto.setStatus(param.getStatus()); routeConfigDto.setStatus(routeDefinition.getStatus());
routeConfigService.sendRouteConfigMsg(routeConfigDto); routeConfigService.sendRouteConfigMsg(routeConfigDto);
} }
@ -164,7 +183,7 @@ public class RouteApi {
@ApiDocMethod(description = "获取路由对应的角色", elementClass = RoleVO.class) @ApiDocMethod(description = "获取路由对应的角色", elementClass = RoleVO.class)
List<RoleVO> getRouteRole(RouteSearchParam param) { List<RoleVO> getRouteRole(RouteSearchParam param) {
if (StringUtils.isBlank(param.getId())) { if (StringUtils.isBlank(param.getId())) {
throw new ApiException("id不能为空"); throw new BizException("id不能为空");
} }
return this.getRouteRole(param.getId()); return this.getRouteRole(param.getId());
} }
@ -209,7 +228,7 @@ public class RouteApi {
routePermissionService.sendRoutePermissionReloadMsg(); routePermissionService.sendRoutePermissionReloadMsg();
} catch (Exception e) { } catch (Exception e) {
log.info("消息推送--路由权限(reload)失败", e); log.info("消息推送--路由权限(reload)失败", e);
throw new ApiException("修改失败,请查看日志"); throw new BizException("修改失败,请查看日志");
} }
} }

@ -5,7 +5,7 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService; import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc; import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod; import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException; import com.gitee.sop.adminserver.api.service.param.ServiceAddParam;
import com.gitee.sop.adminserver.api.service.param.ServiceInstanceParam; import com.gitee.sop.adminserver.api.service.param.ServiceInstanceParam;
import com.gitee.sop.adminserver.api.service.param.ServiceSearchParam; import com.gitee.sop.adminserver.api.service.param.ServiceSearchParam;
import com.gitee.sop.adminserver.api.service.result.ServiceInfo; import com.gitee.sop.adminserver.api.service.result.ServiceInfo;
@ -14,11 +14,16 @@ import com.gitee.sop.adminserver.bean.EurekaApplication;
import com.gitee.sop.adminserver.bean.EurekaApps; import com.gitee.sop.adminserver.bean.EurekaApps;
import com.gitee.sop.adminserver.bean.EurekaInstance; import com.gitee.sop.adminserver.bean.EurekaInstance;
import com.gitee.sop.adminserver.bean.EurekaUri; import com.gitee.sop.adminserver.bean.EurekaUri;
import com.gitee.sop.adminserver.bean.ServiceRouteInfo;
import com.gitee.sop.adminserver.bean.ZookeeperContext; import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
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.BooleanUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.ChildData;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
@ -28,6 +33,7 @@ import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -48,8 +54,6 @@ public class ServiceApi {
private String eurekaUrl; private String eurekaUrl;
// eureka.client.serviceUrl.defaultZone
@Api(name = "service.list") @Api(name = "service.list")
@ApiDocMethod(description = "服务列表(旧)", elementClass = ServiceInfo.class) @ApiDocMethod(description = "服务列表(旧)", elementClass = ServiceInfo.class)
List<ServiceInfo> listServiceInfo(ServiceSearchParam param) throws Exception { List<ServiceInfo> listServiceInfo(ServiceSearchParam param) throws Exception {
@ -74,6 +78,48 @@ public class ServiceApi {
return serviceInfoList; return serviceInfoList;
} }
@Api(name = "service.custom.add")
@ApiDocMethod(description = "添加服务")
void addService(ServiceAddParam param) {
String serviceId = param.getServiceId();
String servicePath = ZookeeperContext.buildServiceIdPath(serviceId);
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
Date now = new Date();
serviceRouteInfo.setServiceId(serviceId);
serviceRouteInfo.setDescription("自定义服务");
serviceRouteInfo.setCreateTime(now);
serviceRouteInfo.setUpdateTime(now);
serviceRouteInfo.setCustom(BooleanUtils.toInteger(true));
String serviceData = JSON.toJSONString(serviceRouteInfo);
try {
ZookeeperContext.addPath(servicePath, serviceData);
} catch (ZookeeperPathExistException e) {
throw new BizException("服务已存在");
}
}
@Api(name = "service.custom.del")
@ApiDocMethod(description = "删除自定义服务")
void delService(ServiceSearchParam param) {
String serviceId = param.getServiceId();
String servicePath = ZookeeperContext.buildServiceIdPath(serviceId);
String data = null;
try {
data = ZookeeperContext.getData(servicePath);
} catch (ZookeeperPathNotExistException e) {
throw new BizException("服务不存在");
}
if (StringUtils.isBlank(data)) {
throw new BizException("非自定义服务,无法删除");
}
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(data, ServiceRouteInfo.class);
int custom = serviceRouteInfo.getCustom();
if (!BooleanUtils.toBoolean(custom)) {
throw new BizException("非自定义服务,无法删除");
}
ZookeeperContext.deletePathDeep(servicePath);
}
@Api(name = "service.instance.list") @Api(name = "service.instance.list")
@ApiDocMethod(description = "服务列表", elementClass = ServiceInfoVo.class) @ApiDocMethod(description = "服务列表", elementClass = ServiceInfoVo.class)
List<ServiceInfoVo> listService(ServiceSearchParam param) throws Exception { List<ServiceInfoVo> listService(ServiceSearchParam param) throws Exception {
@ -133,7 +179,7 @@ public class ServiceApi {
return response.body().string(); return response.body().string();
} else { } else {
log.error("操作失败,url:{}, msg:{}, code:{}", eurekaUri.getUri(args), response.message(), response.code()); log.error("操作失败,url:{}, msg:{}, code:{}", eurekaUri.getUri(args), response.message(), response.code());
throw new ApiException("操作失败", String.valueOf(response.code())); throw new BizException("操作失败", String.valueOf(response.code()));
} }
} }
@ -141,7 +187,7 @@ public class ServiceApi {
protected void after() { protected void after() {
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 ApiException("未指定eureka.client.serviceUrl.defaultZone参数"); throw new BizException("未指定eureka.client.serviceUrl.defaultZone参数");
} }
String url = eurekaUrls.split("\\,")[0]; String url = eurekaUrls.split("\\,")[0];
if (url.endsWith("/")) { if (url.endsWith("/")) {

@ -41,10 +41,12 @@ public class LimitNewAddParam {
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
@ApiDocField(description = "返回的错误码") @ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64")
private String limitCode; private String limitCode;
/** 返回的错误信息, 数据库字段:limit_msg */ /** 返回的错误信息, 数据库字段:limit_msg */
@ApiDocField(description = "返回的错误信息") @ApiDocField(description = "返回的错误信息")
@Length(max = 100, message = "limitMsg长度不能超过100")
private String limitMsg; private String limitMsg;
/** 令牌桶容量, 数据库字段:token_bucket_count */ /** 令牌桶容量, 数据库字段:token_bucket_count */

@ -46,10 +46,12 @@ public class LimitNewUpdateParam {
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
@ApiDocField(description = "返回的错误码") @ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64")
private String limitCode; private String limitCode;
/** 返回的错误信息, 数据库字段:limit_msg */ /** 返回的错误信息, 数据库字段:limit_msg */
@ApiDocField(description = "返回的错误信息") @ApiDocField(description = "返回的错误信息")
@Length(max = 100, message = "limitMsg长度不能超过100")
private String limitMsg; private String limitMsg;
/** 令牌桶容量, 数据库字段:token_bucket_count */ /** 令牌桶容量, 数据库字段:token_bucket_count */

@ -0,0 +1,76 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author tanghc
*/
@Getter
@Setter
public class RouteAddParam {
@NotBlank(message = "serviceId不能为空")
@Length(max = 100, message = "serviceId长度不能超过100")
@ApiDocField(description = "serviceId")
private String serviceId;
/**
* 接口名
*/
@NotBlank(message = "接口名不能为空")
@Length(max = 100, message = "name长度不能超过100")
@ApiDocField(description = "接口名")
private String name;
/**
* 版本号
*/
@NotBlank(message = "版本号不能为空")
@Length(max = 100, message = "version长度不能超过100")
@ApiDocField(description = "版本号")
private String version;
/**
* 路由规则转发的目标uri
*/
@NotBlank(message = "uri不能为空")
@Length(max = 100, message = "uri长度不能超过100")
@ApiDocField(description = "路由uri")
private String uri;
/**
* uri后面跟的path
*/
@ApiDocField(description = "路由path")
@Length(max = 100, message = "path长度不能超过100")
private String path;
/**
* 是否忽略验证业务参数验证除外
*/
@ApiDocField(description = "是否忽略验证,业务参数验证除外,1:忽略,0:不忽略")
private Integer ignoreValidate;
/**
* 合并结果统一格式输出
*/
@ApiDocField(description = "合并结果,统一格式输出,1:合并,2:不合并")
private Integer mergeResult;
/**
* 状态
*/
@NotNull
@ApiDocField(description = "状态,0:审核,1:启用,2:禁用")
private Integer status;
@ApiDocField(description = "是否是自定义路由,1:是,0:否")
private Integer custom;
}

@ -0,0 +1,22 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
/**
* @author tanghc
*/
@Getter
@Setter
public class RouteDeleteParam {
@ApiDocField(description = "服务名serviceId")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@ApiDocField(description = "路由id")
@NotBlank(message = "id不能为空")
private String id;
}

@ -3,6 +3,7 @@ package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField; import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -12,23 +13,25 @@ import javax.validation.constraints.NotNull;
*/ */
@Getter @Getter
@Setter @Setter
public class RouteParam { public class RouteUpdateParam {
@NotBlank(message = "serviceId不能为空")
@ApiDocField(description = "serviceId")
private String serviceId;
/** /**
* 路由的Id * 路由的Id
*/ */
@NotBlank(message = "id不能为空") @NotBlank(message = "id不能为空")
@ApiDocField(description = "路由id") @ApiDocField(description = "路由id")
private String id = ""; private String id;
@NotBlank(message = "serviceId不能为空")
@Length(max = 100, message = "serviceId长度不能超过100")
@ApiDocField(description = "serviceId")
private String serviceId;
/** /**
* 路由规则转发的目标uri * 路由规则转发的目标uri
*/ */
@NotBlank(message = "uri不能为空") @NotBlank(message = "uri不能为空")
@Length(max = 100, message = "uri长度不能超过100")
@ApiDocField(description = "路由uri") @ApiDocField(description = "路由uri")
private String uri; private String uri;
@ -36,8 +39,21 @@ public class RouteParam {
* uri后面跟的path * uri后面跟的path
*/ */
@ApiDocField(description = "路由path") @ApiDocField(description = "路由path")
@Length(max = 100, message = "path长度不能超过100")
private String path; private String path;
/**
* 是否忽略验证业务参数验证除外
*/
@ApiDocField(description = "是否忽略验证,业务参数验证除外,1:忽略,0:不忽略")
private Integer ignoreValidate;
/**
* 合并结果统一格式输出
*/
@ApiDocField(description = "合并结果,统一格式输出,1:合并,2:不合并")
private Integer mergeResult;
/** /**
* 状态 * 状态
*/ */
@ -45,4 +61,7 @@ public class RouteParam {
@ApiDocField(description = "状态,0:审核,1:启用,2:禁用") @ApiDocField(description = "状态,0:审核,1:启用,2:禁用")
private Integer status; private Integer status;
@ApiDocField(description = "是否是自定义路由,1:是,0:否")
private Integer custom;
} }

@ -0,0 +1,21 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* @author tanghc
*/
@Getter
@Setter
public class ServiceAddParam {
@ApiDocField(description = "服务名serviceId")
@Length(max = 100, message = "长度不能超过100")
@NotBlank(message = "serviceId不能为空")
private String serviceId;
}

@ -19,4 +19,8 @@ public class ServiceInfo {
@ApiDocField(description = "描述") @ApiDocField(description = "描述")
private String description; private String description;
/** 是否是自定义服务,1:是,0:否 */
@ApiDocField(description = "是否是自定义服务,1:是,0:否")
private Integer custom;
} }

@ -1,5 +1,6 @@
package com.gitee.sop.adminserver.bean; package com.gitee.sop.adminserver.bean;
import com.alibaba.fastjson.annotation.JSONField;
import com.gitee.easyopen.doc.annotation.ApiDocField; import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data; import lombok.Data;
@ -30,11 +31,13 @@ public class GatewayRouteDefinition {
/** /**
* 路由断言集合配置 * 路由断言集合配置
*/ */
@JSONField(serialize = false)
private List<GatewayPredicateDefinition> predicates = new ArrayList<>(); private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
/** /**
* 路由过滤器集合配置 * 路由过滤器集合配置
*/ */
@JSONField(serialize = false)
private List<GatewayFilterDefinition> filters = new ArrayList<>(); private List<GatewayFilterDefinition> filters = new ArrayList<>();
/** /**
@ -67,14 +70,20 @@ public class GatewayRouteDefinition {
private int status = 1; private int status = 1;
/** /**
* 合并结果 * 合并结果统一格式输出
*/ */
@ApiDocField(description = "合并结果,1:合并,2:不合并") @ApiDocField(description = "合并结果,统一格式输出,1:合并,0:不合并")
private int mergeResult = 1; private int mergeResult = 1;
/** /**
* 是否需要授权才能访问 * 是否需要授权才能访问
*/ */
@ApiDocField(description = "是否需要授权才能访问,1:是,2:否") @ApiDocField(description = "是否需要授权才能访问,1:是,0:否")
private int permission; private int permission;
/**
* 是否是自定义路由
*/
@ApiDocField(description = "是否是自定义路由,1:是,0:否")
private int custom;
} }

@ -24,4 +24,7 @@ public class ServiceRouteInfo {
@JSONField(serialize = false) @JSONField(serialize = false)
private List<GatewayRouteDefinition> routeDefinitionList; private List<GatewayRouteDefinition> routeDefinitionList;
/** 是否是自定义服务,1:是,0:否 */
private int custom;
} }

@ -1,6 +1,9 @@
package com.gitee.sop.adminserver.bean; package com.gitee.sop.adminserver.bean;
import com.gitee.easyopen.exception.ApiException; import com.gitee.sop.adminserver.common.ZookeeperOperationException;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.math.NumberUtils;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
@ -23,6 +26,7 @@ import static com.gitee.sop.adminserver.bean.SopAdminConstants.SOP_ROUTE_PERMISS
* @author tanghc * @author tanghc
*/ */
@Configuration @Configuration
@Slf4j
public class ZookeeperContext { public class ZookeeperContext {
private static CuratorFramework client; private static CuratorFramework client;
@ -55,6 +59,24 @@ public class ZookeeperContext {
return SopAdminConstants.SOP_SERVICE_ROUTE_PATH; return SopAdminConstants.SOP_SERVICE_ROUTE_PATH;
} }
public static String buildServiceIdPath(String serviceId) {
if (StringUtils.isBlank(serviceId)) {
throw new NullPointerException("serviceId不能为空");
}
return getSopRouteRootPath() + "/" + serviceId;
}
public static String buildRoutePath(String serviceId, String routeId) {
if (StringUtils.isBlank(serviceId)) {
throw new NullPointerException("serviceId不能为空");
}
if (StringUtils.isBlank(routeId)) {
throw new NullPointerException("routeId不能为空");
}
String serviceIdPath = getSopRouteRootPath() + "/" + serviceId;
return serviceIdPath + "/" + routeId;
}
public static String getRoutePermissionPath() { public static String getRoutePermissionPath() {
return SOP_ROUTE_PERMISSION_PATH; return SOP_ROUTE_PERMISSION_PATH;
} }
@ -95,30 +117,72 @@ public class ZookeeperContext {
* 对已存在的path赋值如果path不存在抛异常 * 对已存在的path赋值如果path不存在抛异常
* *
* @param path 已存在的 * @param path 已存在的
* @param data * @param data 数据
* @return * @return
* @throws Exception * @throws ZookeeperPathNotExistException
*/ */
public static Stat updatePathData(String path, String data) throws Exception { public static Stat updatePathData(String path, String data) throws ZookeeperPathNotExistException {
if (!isPathExist(path)) { if (!isPathExist(path)) {
throw new ApiException("path " + path + " 不存在"); throw new ZookeeperPathNotExistException("path " + path + " 不存在");
}
try {
return getClient().setData().forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("updatePathData error, path=" + path, e);
} }
return getClient().setData().forPath(path, data.getBytes());
} }
/** /**
* 创建新的path并赋值如果path已存在抛异常 * 创建新的path并赋值如果path已存在抛异常
* @param path 待创建的path * @param path 待创建的path
* @param data * @param data
* @throws ZookeeperPathExistException
*/ */
public static String createNewData(String path, String data) throws Exception { public static String addPath(String path, String data) throws ZookeeperPathExistException {
if (isPathExist(path)) { if (isPathExist(path)) {
throw new ApiException("path " + path + " 已存在"); throw new ZookeeperPathExistException("path " + path + " 已存在");
}
try {
return getClient().create()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("addPath error path=" + path, e);
}
}
/**
* 删除节点及子节点
* @param path
*/
public static void deletePathDeep(String path) {
try {
getClient().delete()
.deletingChildrenIfNeeded()
.forPath(path);
} catch (Exception e) {
throw new ZookeeperOperationException("deletePathDeep error path=" + path, e);
}
}
/**
* 创建新的path并赋值如果path已存在则不创建
* @param path 待创建的path
* @param data
*/
public static String addPathQuietly(String path, String data) {
if (isPathExist(path)) {
return path;
}
try {
return getClient().create()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("addPathQuietly error path=" + path, e);
} }
return getClient().create()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
} }
/** /**
@ -126,23 +190,36 @@ public class ZookeeperContext {
* @param path * @param path
* @param data * @param data
* @return * @return
* @throws Exception
*/ */
public static String createOrUpdateData(String path, String data) throws Exception { public static String createOrUpdateData(String path, String data) {
return getClient().create() try {
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值 return getClient().create()
.orSetData() // 如果节点存在则Curator将会使用给出的数据设置这个节点的值
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点 .orSetData()
.creatingParentContainersIfNeeded() // 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.forPath(path, data.getBytes()); .creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("createOrUpdateData error path=" + path, e);
}
} }
public static String getData(String path) throws Exception { /**
* 获取节点内容
* @param path
* @return
* @throws ZookeeperPathNotExistException
*/
public static String getData(String path) throws ZookeeperPathNotExistException {
if (!isPathExist(path)) { if (!isPathExist(path)) {
return null; throw new ZookeeperPathNotExistException("path 不存在, path=" + path);
}
try {
byte[] data = getClient().getData().forPath(path);
return new String(data);
} catch (Exception e) {
throw new ZookeeperOperationException("getData error path=" + path, e);
} }
byte[] data = getClient().getData().forPath(path);
return new String(data);
} }
/** /**
@ -150,9 +227,8 @@ public class ZookeeperContext {
* *
* @param parentPath 父节点 * @param parentPath 父节点
* @return * @return
* @throws Exception
*/ */
public static List<ChildData> getChildrenData(String parentPath) throws Exception { public static List<ChildData> getChildrenData(String parentPath) {
PathChildrenCache pathChildrenCache = buildPathChildrenCache(parentPath); PathChildrenCache pathChildrenCache = buildPathChildrenCache(parentPath);
if (pathChildrenCache == null) { if (pathChildrenCache == null) {
return Collections.emptyList(); return Collections.emptyList();
@ -160,7 +236,7 @@ public class ZookeeperContext {
return pathChildrenCache.getCurrentData(); return pathChildrenCache.getCurrentData();
} }
public static PathChildrenCache buildPathChildrenCache(String path) throws Exception { public static PathChildrenCache buildPathChildrenCache(String path) {
if (!isPathExist(path)) { if (!isPathExist(path)) {
return null; return null;
} }
@ -168,7 +244,11 @@ public class ZookeeperContext {
// 且第三个参数要设置为true,不然ChildData对象中的getData返回null // 且第三个参数要设置为true,不然ChildData对象中的getData返回null
PathChildrenCache childrenCache = new PathChildrenCache(client, path, true); PathChildrenCache childrenCache = new PathChildrenCache(client, path, true);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的 // 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); try {
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
} catch (Exception e) {
throw new ZookeeperOperationException("buildPathChildrenCache error path=" + path, e);
}
return childrenCache; return childrenCache;
} }

@ -0,0 +1,33 @@
package com.gitee.sop.adminserver.common;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.message.Error;
/**
* @author tanghc
*/
public class BizException extends ApiException {
public BizException(String msg) {
super(msg, "4000");
}
public BizException(Exception e) {
super(e);
}
public BizException(Error<String> error) {
super(error);
}
public BizException(String msg, String code) {
super(msg, code);
}
public BizException(String msg, String code, Object data) {
super(msg, code, data);
}
public BizException(Error<String> error, Object data) {
super(error, data);
}
}

@ -0,0 +1,26 @@
package com.gitee.sop.adminserver.common;
/**
* @author tanghc
*/
public class ZookeeperOperationException extends RuntimeException {
public ZookeeperOperationException() {
}
public ZookeeperOperationException(String message) {
super(message);
}
public ZookeeperOperationException(String message, Throwable cause) {
super(message, cause);
}
public ZookeeperOperationException(Throwable cause) {
super(cause);
}
public ZookeeperOperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

@ -0,0 +1,10 @@
package com.gitee.sop.adminserver.common;
/**
* @author tanghc
*/
public class ZookeeperPathExistException extends Exception {
public ZookeeperPathExistException(String message) {
super(message);
}
}

@ -0,0 +1,10 @@
package com.gitee.sop.adminserver.common;
/**
* @author tanghc
*/
public class ZookeeperPathNotExistException extends Exception {
public ZookeeperPathNotExistException(String message) {
super(message);
}
}

@ -20,7 +20,7 @@ public class RouteConfigService {
* @param routeConfigDto * @param routeConfigDto
* @throws Exception * @throws Exception
*/ */
public void sendRouteConfigMsg(RouteConfigDto routeConfigDto) throws Exception { public void sendRouteConfigMsg(RouteConfigDto routeConfigDto) {
String configData = JSON.toJSONString(routeConfigDto); String configData = JSON.toJSONString(routeConfigDto);
ChannelMsg channelMsg = new ChannelMsg("update", configData); ChannelMsg channelMsg = new ChannelMsg("update", configData);
String jsonData = JSON.toJSONString(channelMsg); String jsonData = JSON.toJSONString(channelMsg);

@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
@ -44,8 +43,7 @@ public class RouteService {
return isRoute && gatewayRouteDefinition.getId().contains(id); return isRoute && gatewayRouteDefinition.getId().contains(id);
} }
}) })
.collect(toList()) .collect(toList());
;
return routeDefinitionStream; return routeDefinitionStream;
} }

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,o,a=t[0],i=t[1],f=t[2],l=0,d=[];l<a.length;l++)o=a[l],c[o]&&d.push(c[o][0]),c[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);s&&s(t);while(d.length)d.shift()();return u.push.apply(u,f||[]),n()}function n(){for(var e,t=0;t<u.length;t++){for(var n=u[t],r=!0,o=1;o<n.length;o++){var a=n[o];0!==c[a]&&(r=!1)}r&&(u.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={runtime:0},c={runtime:0},u=[];function a(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-19100986":"3ec98327","chunk-238a81e9":"5955f13d","chunk-29b24c82":"2b3ac7ca","chunk-2d0aa95b":"447435b3","chunk-2d221836":"574c9ab6","chunk-2d22c2e3":"46f37153","chunk-4a59cbe4":"b5e74cc4","chunk-ea2e58a4":"f3f85b0e"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-19100986":1,"chunk-238a81e9":1,"chunk-29b24c82":1,"chunk-ea2e58a4":1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-19100986":"a43114f3","chunk-238a81e9":"e8e2beee","chunk-29b24c82":"857979fb","chunk-2d0aa95b":"31d6cfe0","chunk-2d221836":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-4a59cbe4":"31d6cfe0","chunk-ea2e58a4":"d10599db"}[e]+".css",c=i.p+r,u=document.getElementsByTagName("link"),a=0;a<u.length;a++){var f=u[a],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===c))return t()}var d=document.getElementsByTagName("style");for(a=0;a<d.length;a++){f=d[a],l=f.getAttribute("data-href");if(l===r||l===c)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=r,delete o[e],s.parentNode.removeChild(s),n(u)},s.href=c;var h=document.getElementsByTagName("head")[0];h.appendChild(s)}).then(function(){o[e]=0}));var r=c[e];if(0!==r)if(r)t.push(r[2]);else{var u=new Promise(function(t,n){r=c[e]=[t,n]});t.push(r[2]=u);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=a(e),f=function(t){l.onerror=l.onload=null,clearTimeout(d);var n=c[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,u=new Error("Loading chunk "+e+" failed.\n("+r+": "+o+")");u.type=r,u.request=o,n[1](u)}c[e]=void 0}};var d=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var d=0;d<f.length;d++)t(f[d]);var s=l;n()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.36a3cb86.js></script></body></html> <!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,a,c=t[0],i=t[1],f=t[2],l=0,s=[];l<c.length;l++)a=c[l],o[a]&&s.push(o[a][0]),o[a]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);d&&d(t);while(s.length)s.shift()();return u.push.apply(u,f||[]),n()}function n(){for(var e,t=0;t<u.length;t++){for(var n=u[t],r=!0,a=1;a<n.length;a++){var c=n[a];0!==o[c]&&(r=!1)}r&&(u.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},a={runtime:0},o={runtime:0},u=[];function c(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-238a81e9":"5955f13d","chunk-2a9bc230":"6f8c0d92","chunk-2d221836":"574c9ab6","chunk-2d22c2e3":"46f37153","chunk-37401378":"4e39ec9b","chunk-4a59cbe4":"b5e74cc4","chunk-6a68a33e":"8b63492e","chunk-ea2e58a4":"f3f85b0e"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-238a81e9":1,"chunk-2a9bc230":1,"chunk-37401378":1,"chunk-6a68a33e":1,"chunk-ea2e58a4":1};a[e]?t.push(a[e]):0!==a[e]&&n[e]&&t.push(a[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-238a81e9":"e8e2beee","chunk-2a9bc230":"2dae711a","chunk-2d221836":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-37401378":"a43114f3","chunk-4a59cbe4":"31d6cfe0","chunk-6a68a33e":"3b12267b","chunk-ea2e58a4":"d10599db"}[e]+".css",o=i.p+r,u=document.getElementsByTagName("link"),c=0;c<u.length;c++){var f=u[c],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===o))return t()}var s=document.getElementsByTagName("style");for(c=0;c<s.length;c++){f=s[c],l=f.getAttribute("data-href");if(l===r||l===o)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var r=t&&t.target&&t.target.src||o,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.code="CSS_CHUNK_LOAD_FAILED",u.request=r,delete a[e],d.parentNode.removeChild(d),n(u)},d.href=o;var h=document.getElementsByTagName("head")[0];h.appendChild(d)}).then(function(){a[e]=0}));var r=o[e];if(0!==r)if(r)t.push(r[2]);else{var u=new Promise(function(t,n){r=o[e]=[t,n]});t.push(r[2]=u);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=c(e),f=function(t){l.onerror=l.onload=null,clearTimeout(s);var n=o[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src,u=new Error("Loading chunk "+e+" failed.\n("+r+": "+a+")");u.type=r,u.request=a,n[1](u)}o[e]=void 0}};var s=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var s=0;s<f.length;s++)t(f[s]);var d=l;n()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.3225a72c.js></script></body></html>

@ -1 +0,0 @@
.limit-tip[data-v-29df7fee]{cursor:pointer;margin-left:10px}

@ -0,0 +1 @@
.limit-tip[data-v-31efca04]{cursor:pointer;margin-left:10px}

@ -0,0 +1 @@
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}

@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-19100986"],{5040:function(t,n,a){"use strict";var e=a("a5a4"),s=a.n(e);s.a},9406:function(t,n,a){"use strict";a.r(n);var e=function(){var t=this,n=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,n=t.$createElement,a=t._self._c||n;return a("div",{staticClass:"dashboard-container"},[a("div",{staticClass:"dashboard-text"},[t._v("欢迎使用SOP Admin")])])}],i={created:function(){this.post("admin.userinfo.get",{},function(t){})}},c=i,r=(a("5040"),a("2877")),o=Object(r["a"])(c,e,s,!1,null,"b4bd5162",null);n["default"]=o.exports},a5a4:function(t,n,a){}}]); (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-37401378"],{5040:function(t,n,a){"use strict";var e=a("a5a4"),s=a.n(e);s.a},9406:function(t,n,a){"use strict";a.r(n);var e=function(){var t=this,n=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,n=t.$createElement,a=t._self._c||n;return a("div",{staticClass:"dashboard-container"},[a("div",{staticClass:"dashboard-text"},[t._v("欢迎使用SOP Admin")])])}],i={created:function(){this.post("admin.userinfo.get",{},function(t){})}},c=i,r=(a("5040"),a("2877")),o=Object(r["a"])(c,e,s,!1,null,"b4bd5162",null);n["default"]=o.exports},a5a4:function(t,n,a){}}]);

@ -8,7 +8,7 @@ import { getToken, removeToken } from './auth'
// 创建axios实例 // 创建axios实例
const client = axios.create({ const client = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
timeout: 5000 // 请求超时时间 timeout: 10000 // 请求超时时间
}) })
Object.assign(Vue.prototype, { Object.assign(Vue.prototype, {
@ -94,6 +94,14 @@ Object.assign(Vue.prototype, {
} }
}).catch(function() {}) }).catch(function() {})
}, },
/**
* 重置表单
* @param formName 表单元素的ref
*/
resetForm(formName) {
const frm = this.$refs[formName]
frm && frm.resetFields()
},
logout: function() { logout: function() {
removeToken() removeToken()
const fullPath = this.$route.fullPath const fullPath = this.$route.fullPath

@ -1,7 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-container> <el-container>
<el-aside style="min-height: 300px;width: 200px;"> <el-aside style="min-height: 300px;width: 250px;">
<el-input v-model="filterText" prefix-icon="el-icon-search" placeholder="搜索服务..." style="margin-bottom:20px;" size="mini" clearable /> <el-input v-model="filterText" prefix-icon="el-icon-search" placeholder="搜索服务..." style="margin-bottom:20px;" size="mini" clearable />
<el-tree <el-tree
ref="tree2" ref="tree2"
@ -17,10 +17,10 @@
@node-click="onNodeClick" @node-click="onNodeClick"
> >
<span slot-scope="{ node, data }" class="custom-tree-node"> <span slot-scope="{ node, data }" class="custom-tree-node">
<span v-if="data.label.length < 15">{{ data.label }}</span> <span v-if="data.label.length < serviceTextLimitSize">{{ data.label }}</span>
<span v-else> <span v-else>
<el-tooltip :content="data.label" class="item" effect="light" placement="right"> <el-tooltip :content="data.label" class="item" effect="light" placement="right">
<span>{{ data.label.substring(0, 15) + '...' }}</span> <span>{{ data.label.substring(0, serviceTextLimitSize) + '...' }}</span>
</el-tooltip> </el-tooltip>
</span> </span>
</span> </span>
@ -222,6 +222,7 @@
export default { export default {
data() { data() {
return { return {
serviceTextLimitSize: 20,
filterText: '', filterText: '',
treeData: [], treeData: [],
tableData: [], tableData: [],
@ -407,13 +408,8 @@ export default {
} }
}) })
}, },
resetForm(formName) {
const frm = this.$refs[formName]
frm && frm.resetFields()
},
onLimitDialogClose: function() { onLimitDialogClose: function() {
this.resetForm('limitDialogForm') this.resetForm('limitDialogForm')
this.limitDialogVisible = false
}, },
infoRender: function(row) { infoRender: function(row) {
const html = [] const html = []

@ -1,27 +1,57 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-container> <el-container>
<el-aside style="min-height: 300px;width: 200px;"> <el-aside style="min-height: 300px;width: 250px;">
<el-input v-model="filterText" prefix-icon="el-icon-search" placeholder="搜索服务..." style="margin-bottom:20px;" size="mini" clearable /> <el-button
type="primary"
plain
size="mini"
icon="el-icon-plus"
@click.stop="addService"
>
新建服务
</el-button>
<el-input
v-model="filterText"
prefix-icon="el-icon-search"
placeholder="搜索服务..."
style="margin-bottom:10px;margin-top:10px;"
size="mini"
clearable
/>
<el-tree <el-tree
ref="tree2" ref="serviceTree"
:data="treeData" :data="treeData"
:props="defaultProps" :props="defaultProps"
:filter-node-method="filterNode" :filter-node-method="filterNode"
:highlight-current="true" :highlight-current="true"
:expand-on-click-node="false" :expand-on-click-node="false"
empty-text="无数据" empty-text="无数据"
node-key="id" node-key="serviceId"
class="filter-tree" class="filter-tree"
default-expand-all default-expand-all
@node-click="onNodeClick" @node-click="onNodeClick"
> >
<span slot-scope="{ node, data }" class="custom-tree-node"> <span slot-scope="{ node, data }" class="custom-tree-node">
<span v-if="data.label.length < 15">{{ data.label }}</span> <div>
<span v-else> <el-tooltip v-show="data.custom" content="自定义服务" class="item" effect="light" placement="top">
<el-tooltip :content="data.label" class="item" effect="light" placement="right"> <i class="el-icon-warning-outline"></i>
<span>{{ data.label.substring(0, 15) + '...' }}</span>
</el-tooltip> </el-tooltip>
<span v-if="data.label.length < serviceTextLimitSize">{{ data.label }}</span>
<span v-else>
<el-tooltip :content="data.label" class="item" effect="light" placement="right">
<span>{{ data.label.substring(0, serviceTextLimitSize) + '...' }}</span>
</el-tooltip>
</span>
</div>
<span>
<el-button
v-if="data.custom === 1"
type="text"
size="mini"
icon="el-icon-delete"
title="删除服务"
@click.stop="() => onDelService(data)"/>
</span> </span>
</span> </span>
</el-tree> </el-tree>
@ -35,10 +65,20 @@
<el-button type="primary" icon="el-icon-search" @click="onSearchTable">查询</el-button> <el-button type="primary" icon="el-icon-search" @click="onSearchTable">查询</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-button
v-show="isCustomService"
type="primary"
size="mini"
icon="el-icon-plus"
@click.stop="addRoute"
>
新建路由
</el-button>
<el-table <el-table
:data="tableData" :data="tableData"
border border
max-height="500" max-height="500"
style="margin-top: 10px;"
> >
<el-table-column <el-table-column
prop="name" prop="name"
@ -69,12 +109,12 @@
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="ignoreValidate" prop="ignoreValidate"
label="忽略验证" label="签名校验"
width="80" width="80"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<span v-if="scope.row.ignoreValidate === 1" style="color:#67C23A"></span> <span v-if="scope.row.ignoreValidate === 0" style="color:#67C23A">校验</span>
<span v-if="scope.row.ignoreValidate === 0" style="color:#909399"></span> <span v-if="scope.row.ignoreValidate === 1" style="color:#E6A23C">不校验</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -93,7 +133,7 @@
width="80" width="80"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<span v-if="scope.row.status === 0" style="color:#909399">待审核</span> <span v-if="scope.row.status === 0" style="color:#E6A23C">待审核</span>
<span v-if="scope.row.status === 1" style="color:#67C23A">已启用</span> <span v-if="scope.row.status === 1" style="color:#67C23A">已启用</span>
<span v-if="scope.row.status === 2" style="color:#F56C6C">已禁用</span> <span v-if="scope.row.status === 2" style="color:#F56C6C">已禁用</span>
</template> </template>
@ -106,79 +146,170 @@
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button> <el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
<el-button v-if="scope.row.permission" type="text" size="mini" @click="onTableAuth(scope.row)">授权</el-button> <el-button v-if="scope.row.permission" type="text" size="mini" @click="onTableAuth(scope.row)">授权</el-button>
<el-button v-if="scope.row.custom" type="text" size="mini" @click="onTableDel(scope.row)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- route dialog -->
<el-dialog title="修改路由" :visible.sync="routeDialogVisible" :close-on-click-modal="false">
<el-form
:model="routeDialogFormData"
label-width="120px"
size="mini"
>
<el-form-item label="id">
<el-input v-model="routeDialogFormData.id" readonly="readonly" />
</el-form-item>
<el-form-item label="uri">
<el-input v-model="routeDialogFormData.uri" />
</el-form-item>
<el-form-item label="path">
<el-input v-model="routeDialogFormData.path" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="routeDialogFormData.status">
<el-radio :label="1" name="status">启用</el-radio>
<el-radio :label="2" name="status" style="color:#F56C6C">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="routeDialogVisible = false"> </el-button>
<el-button type="primary" @click="onRouteDialogSave"> </el-button>
</div>
</el-dialog>
<!-- auth dialog -->
<el-dialog title="路由授权" :visible.sync="authDialogVisible" :close-on-click-modal="false">
<el-form
:model="authDialogFormData"
label-width="120px"
size="mini"
>
<el-form-item label="id">
<el-input v-model="authDialogFormData.routeId" readonly="readonly" />
</el-form-item>
<el-form-item label="角色">
<el-checkbox-group v-model="authDialogFormData.roleCode">
<el-checkbox v-for="item in roles" :key="item.roleCode" :label="item.roleCode">{{ item.description }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="authDialogVisible = false"> </el-button>
<el-button type="primary" @click="onAuthDialogSave"> </el-button>
</div>
</el-dialog>
</el-main> </el-main>
</el-container> </el-container>
<!-- route dialog -->
<el-dialog
:title="routeDialogTitle"
:visible.sync="routeDialogVisible"
:close-on-click-modal="false"
@close="onCloseRouteDialog"
>
<el-form
ref="routeDialogFormRef"
:model="routeDialogFormData"
:rules="routeDialogFormRules"
label-width="120px"
size="mini"
>
<el-input v-show="false" v-model="routeDialogFormData.id" />
<el-form-item label="接口名" prop="name">
<el-input v-model="routeDialogFormData.name" placeholder="接口名,如:product.goods.list" :disabled="Boolean(routeDialogFormData.id)" />
</el-form-item>
<el-form-item label="版本号" prop="version">
<el-input v-model="routeDialogFormData.version" placeholder="版本号,如:1.0" :disabled="Boolean(routeDialogFormData.id)" />
</el-form-item>
<el-form-item label="uri" prop="uri">
<el-input v-model="routeDialogFormData.uri" placeholder="如:http://www.xx.com" :disabled="routePropDisabled()" />
</el-form-item>
<el-form-item label="path" prop="path">
<el-input v-model="routeDialogFormData.path" placeholder="如:/order/list" :disabled="routePropDisabled()" />
</el-form-item>
<el-form-item label="签名校验">
<el-radio-group v-model="routeDialogFormData.ignoreValidate" :disabled="routePropDisabled()">
<el-radio :label="0" name="ignoreValidate">校验</el-radio>
<el-radio :label="1" name="ignoreValidate">不校验</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="统一格式输出">
<el-radio-group v-model="routeDialogFormData.mergeResult" :disabled="routePropDisabled()">
<el-radio :label="1" name="mergeResult"></el-radio>
<el-radio :label="0" name="mergeResult"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="routeDialogFormData.status">
<el-radio :label="1" name="status">启用</el-radio>
<el-radio :label="2" name="status" style="color:#F56C6C">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="routeDialogVisible = false"> </el-button>
<el-button type="primary" @click="onRouteDialogSave"> </el-button>
</div>
</el-dialog>
<!-- auth dialog -->
<el-dialog
title="路由授权"
:visible.sync="authDialogVisible"
:close-on-click-modal="false"
>
<el-form
:model="authDialogFormData"
label-width="120px"
size="mini"
>
<el-form-item label="id">
<el-input v-model="authDialogFormData.routeId" readonly="readonly" />
</el-form-item>
<el-form-item label="角色">
<el-checkbox-group v-model="authDialogFormData.roleCode">
<el-checkbox v-for="item in roles" :key="item.roleCode" :label="item.roleCode">{{ item.description }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="authDialogVisible = false"> </el-button>
<el-button type="primary" @click="onAuthDialogSave"> </el-button>
</div>
</el-dialog>
<!--添加服务-->
<el-dialog
title="添加服务"
:visible.sync="addServiceDialogVisible"
:close-on-click-modal="false"
@close="closeAddServiceDlg"
>
<el-form
ref="addServiceForm"
:model="addServiceForm"
:rules="addServiceFormRules"
label-width="200px"
>
<el-form-item label="服务名(serviceId)" prop="serviceId">
<el-input v-model="addServiceForm.serviceId" placeholder="服务名,如:order-service" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addServiceDialogVisible = false"> </el-button>
<el-button type="primary" @click="onAddService"> </el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
<style>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-input.is-disabled .el-input__inner {color: #909399;}
.el-radio__input.is-disabled+span.el-radio__label {color: #909399;}
</style>
<script> <script>
export default { export default {
data() { data() {
return { return {
serviceTextLimitSize: 20,
filterText: '', filterText: '',
treeData: [], treeData: [],
tableData: [], tableData: [],
serviceId: '', serviceId: '',
searchFormData: {}, isCustomService: false,
searchFormData: {
id: '',
serviceId: ''
},
defaultProps: { defaultProps: {
children: 'children', children: 'children',
label: 'label' label: 'label'
}, },
routeDialogTitle: '修改路由',
// dialog // dialog
routeDialogFormData: { routeDialogFormData: {
status: 1 id: '',
name: '',
version: '1.0',
uri: '',
path: '',
status: 1,
mergeResult: 1,
ignoreValidate: 0
},
routeDialogFormRules: {
name: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
],
version: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
],
uri: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
],
path: [
{ min: 0, max: 100, message: '长度不能超过 100 个字符', trigger: 'blur' }
]
}, },
routeDialogVisible: false, routeDialogVisible: false,
roles: [], roles: [],
@ -186,12 +317,23 @@ export default {
routeId: '', routeId: '',
roleCode: [] roleCode: []
}, },
authDialogVisible: false authDialogVisible: false,
// addService
addServiceDialogVisible: false,
addServiceForm: {
serviceId: ''
},
addServiceFormRules: {
serviceId: [
{ required: true, message: '请输入服务名称', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
]
}
} }
}, },
watch: { watch: {
filterText(val) { filterText(val) {
this.$refs.tree2.filter(val) this.$refs.serviceTree.filter(val)
} }
}, },
created() { created() {
@ -204,6 +346,12 @@ export default {
this.post('service.list', {}, function(resp) { this.post('service.list', {}, function(resp) {
const respData = resp.data const respData = resp.data
this.treeData = this.convertToTreeData(respData, 0) this.treeData = this.convertToTreeData(respData, 0)
this.$nextTick(() => {
//
if (this.serviceId) {
this.$refs.serviceTree.setCurrentKey(this.serviceId)
}
})
}) })
}, },
// //
@ -216,6 +364,7 @@ export default {
if (data.parentId) { if (data.parentId) {
this.serviceId = data.label this.serviceId = data.label
this.searchFormData.serviceId = this.serviceId this.searchFormData.serviceId = this.serviceId
this.isCustomService = Boolean(data.custom)
this.loadTable() this.loadTable()
} }
}, },
@ -250,16 +399,18 @@ export default {
} }
const children = [] const children = []
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const item = { label: data[i].serviceId, parentId: 1 } data[i].parentId = 1
children.push(item) data[i].label = data[i].serviceId
children.push(data[i])
} }
root.children = children root.children = children
result.push(root) result.push(root)
return result return result
}, },
// table // table
loadTable: function() { loadTable: function(param) {
this.post('route.list', this.searchFormData, function(resp) { const postData = param || this.searchFormData
this.post('route.list', postData, function(resp) {
this.tableData = resp.data this.tableData = resp.data
}) })
}, },
@ -267,8 +418,11 @@ export default {
this.loadTable() this.loadTable()
}, },
onTableUpdate: function(row) { onTableUpdate: function(row) {
Object.assign(this.routeDialogFormData, row) this.routeDialogTitle = '修改路由'
this.routeDialogVisible = true this.routeDialogVisible = true
this.$nextTick(() => {
Object.assign(this.routeDialogFormData, row)
})
}, },
onTableAuth: function(row) { onTableAuth: function(row) {
this.authDialogFormData.routeId = row.id this.authDialogFormData.routeId = row.id
@ -283,6 +437,28 @@ export default {
this.authDialogVisible = true this.authDialogVisible = true
}) })
}, },
onTableDel: function(row) {
this.confirm(`确认要删除路由【${row.id}】吗?`, function(done) {
const data = {
serviceId: this.serviceId,
id: row.id
}
this.post('route.del', data, function() {
done()
this.tip('删除成功')
this.loadTable()
})
})
},
onCloseRouteDialog: function() {
this.resetForm('routeDialogFormRef')
},
routePropDisabled: function() {
if (!this.routeDialogFormData.id) {
return false
}
return !this.isCustomService
},
loadRouteRole: function() { loadRouteRole: function() {
if (this.roles.length === 0) { if (this.roles.length === 0) {
this.post('role.listall', {}, function(resp) { this.post('role.listall', {}, function(resp) {
@ -290,6 +466,15 @@ export default {
}) })
} }
}, },
addRoute: function() {
this.routeDialogTitle = '新建路由'
this.routeDialogVisible = true
this.$nextTick(() => {
Object.assign(this.routeDialogFormData, {
id: ''
})
})
},
roleRender: function(row) { roleRender: function(row) {
if (!row.permission) { if (!row.permission) {
return '(公开)' return '(公开)'
@ -302,18 +487,52 @@ export default {
return html.length > 0 ? html.join(', ') : '<span class="x-red">未授权</span>' return html.length > 0 ? html.join(', ') : '<span class="x-red">未授权</span>'
}, },
onRouteDialogSave: function() { onRouteDialogSave: function() {
this.routeDialogFormData.serviceId = this.serviceId this.$refs.routeDialogFormRef.validate((valid) => {
this.post('route.update', this.routeDialogFormData, function() { if (valid) {
this.routeDialogVisible = false const uri = this.routeDialogFormData.id ? 'route.update' : 'route.add'
this.loadTable() this.routeDialogFormData.serviceId = this.serviceId
this.post(uri, this.routeDialogFormData, function() {
this.routeDialogVisible = false
this.loadTable()
})
}
}) })
}, },
onAuthDialogSave: function() { onAuthDialogSave: function() {
this.post('route.role.update', this.authDialogFormData, function() { this.post('route.role.update', this.authDialogFormData, function() {
console.log(this.authDialogFormData)
this.authDialogVisible = false this.authDialogVisible = false
this.loadTable() this.loadTable()
}) })
},
addService: function() {
this.addServiceDialogVisible = true
},
closeAddServiceDlg: function() {
this.$refs.addServiceForm.resetFields()
},
onAddService: function() {
this.$refs.addServiceForm.validate((valid) => {
if (valid) {
this.post('service.custom.add', this.addServiceForm, function(resp) {
this.addServiceDialogVisible = false
this.tip('添加成功')
this.loadTree()
})
}
})
},
onDelService: function(data) {
const serviceId = data.serviceId
this.confirm('确认要删除服务' + serviceId + '吗,【对应的路由配置会一起删除】', function(done) {
const postData = {
serviceId: serviceId
}
this.post('service.custom.del', postData, function() {
done()
this.tip('删除成功')
this.loadTree()
})
})
} }
} }
} }

Loading…
Cancel
Save