自定义路由

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.ApiDocField;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.easyopen.util.KeyStore;
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.bean.ChannelMsg;
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.ZookeeperOperationException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.IsvInfo;
import com.gitee.sop.adminserver.entity.PermIsvRole;
import com.gitee.sop.adminserver.entity.PermRole;
@ -133,7 +135,7 @@ public class IsvApi {
@Transactional(rollbackFor = Exception.class)
public void addIsv(IsvInfoFormAdd param) throws Exception {
if (isvInfoMapper.getByColumn("app_key", param.getAppKey()) != null) {
throw new ApiException("appKey已存在");
throw new BizException("appKey已存在");
}
formatForm(param);
IsvInfo rec = new IsvInfo();
@ -175,9 +177,9 @@ public class IsvApi {
try {
log.info("消息推送--ISV信息(update), path:{}, data:{}", path, data);
ZookeeperContext.updatePathData(path, data);
} catch (Exception e) {
} catch (ZookeeperPathNotExistException 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);
} catch (Exception e) {
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.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query;
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.bean.GatewayRouteDefinition;
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.entity.ConfigRouteLimit;
import com.gitee.sop.adminserver.mapper.ConfigRouteLimitMapper;
@ -118,7 +118,7 @@ public class LimitApi {
routeConfigService.sendRouteConfigMsg(routeConfigDto);
} catch (Exception 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.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.PageInfo;
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.result.LimitNewVO;
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.mapper.ConfigLimitMapper;
import com.gitee.sop.adminserver.service.RouteConfigService;
@ -65,7 +65,7 @@ public class LimitNewApi {
routeConfigService.sendLimitConfigMsg(configLimitDto);
} catch (Exception e) {
log.error("推送限流消息错误, param:{}", param, e);
throw new ApiException("新增失败,请查看日志");
throw new BizException("新增失败,请查看日志");
}
}
@ -87,7 +87,7 @@ public class LimitNewApi {
routeConfigService.sendLimitConfigMsg(configLimitDto);
} catch (Exception 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.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.verify.DefaultMd5Verifier;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.service.param.LogMonitorInstanceAddParam;
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.entity.ConfigCommon;
import com.gitee.sop.adminserver.mapper.ConfigCommonMapper;
@ -80,7 +80,7 @@ public class LogApi {
}
} catch (Exception e) {
log.error("获取日志信息出错", e);
throw new ApiException("获取日志信息出错");
throw new BizException("获取日志信息出错");
}
}
Collections.sort(ret, Comparator.comparing(LogMonitorInstanceVO::getCount));
@ -98,7 +98,7 @@ public class LogApi {
String ipPort = configCommon.getConfigKey();
this.requestLogServer(ipPort, "clearErrors");
} catch (Exception e) {
throw new ApiException("清除失败");
throw new BizException("清除失败");
}
}
@ -122,7 +122,7 @@ public class LogApi {
.eq("config_key", ipPort);
ConfigCommon rec = configCommonMapper.getByQuery(query);
if (rec != null) {
throw new ApiException("该实例已添加");
throw new BizException("该实例已添加");
}
ConfigCommon configCommon = new ConfigCommon();
configCommon.setConfigGroup(LOG_MONITOR_INSTANCE);
@ -137,11 +137,11 @@ public class LogApi {
JSONObject jsonObject = JSON.parseObject(json);
if (!CODE_SUCCESS.equals(jsonObject.getString("code"))) {
log.error("请求结果:{}", json);
throw new ApiException("添加失败");
throw new BizException("添加失败");
}
} catch (Exception 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.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query;
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.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.ServiceInfo;
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
import com.gitee.sop.adminserver.bean.RouteConfigDto;
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.PermRole;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@ -96,67 +98,84 @@ public class RouteApi {
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")
@ApiDocMethod(description = "修改路由")
void updateRoute(RouteParam param) throws Exception {
String serviceIdPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId();
String zookeeperRoutePath = serviceIdPath + "/" + param.getId();
String data = ZookeeperContext.getData(zookeeperRoutePath);
GatewayRouteDefinition routeDefinition = JSON.parseObject(data, GatewayRouteDefinition.class);
void updateRoute(RouteUpdateParam param) {
String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
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")
@ApiDocMethod(description = "新增路由")
void addRoute(RouteParam param) throws Exception {
String serviceIdPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId();
String zookeeperRoutePath = serviceIdPath + "/" + param.getId();
if (ZookeeperContext.isPathExist(zookeeperRoutePath)) {
throw new ApiException("id已存在");
private GatewayRouteDefinition getGatewayRouteDefinition(String zookeeperRoutePath) {
String data = null;
try {
data = ZookeeperContext.getData(zookeeperRoutePath);
} catch (ZookeeperPathNotExistException e) {
throw new BizException("路由不存在");
}
GatewayRouteDefinition routeDefinition = new GatewayRouteDefinition();
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);
return JSON.parseObject(data, GatewayRouteDefinition.class);
}
private void updateRouteConfig(RouteParam param) {
private void updateRouteConfig(GatewayRouteDefinition routeDefinition) {
try {
String routeId = param.getId();
String routeId = routeDefinition.getId();
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId);
boolean doSave = configRouteBase == null;
if (doSave) {
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)
: configRouteBaseMapper.update(configRouteBase);
if (i > 0) {
this.sendMsg(param);
this.sendMsg(routeDefinition);
}
} catch (Exception e) {
log.error("发送msg失败", e);
}
}
private void sendMsg(RouteParam param) throws Exception {
private void sendMsg(GatewayRouteDefinition routeDefinition) {
RouteConfigDto routeConfigDto = new RouteConfigDto();
routeConfigDto.setRouteId(param.getId());
routeConfigDto.setStatus(param.getStatus());
routeConfigDto.setRouteId(routeDefinition.getId());
routeConfigDto.setStatus(routeDefinition.getStatus());
routeConfigService.sendRouteConfigMsg(routeConfigDto);
}
@ -164,7 +183,7 @@ public class RouteApi {
@ApiDocMethod(description = "获取路由对应的角色", elementClass = RoleVO.class)
List<RoleVO> getRouteRole(RouteSearchParam param) {
if (StringUtils.isBlank(param.getId())) {
throw new ApiException("id不能为空");
throw new BizException("id不能为空");
}
return this.getRouteRole(param.getId());
}
@ -209,7 +228,7 @@ public class RouteApi {
routePermissionService.sendRoutePermissionReloadMsg();
} catch (Exception 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.doc.annotation.ApiDoc;
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.ServiceSearchParam;
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.EurekaInstance;
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.common.BizException;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.springframework.beans.BeanUtils;
@ -28,6 +33,7 @@ import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@ -48,8 +54,6 @@ public class ServiceApi {
private String eurekaUrl;
// eureka.client.serviceUrl.defaultZone
@Api(name = "service.list")
@ApiDocMethod(description = "服务列表(旧)", elementClass = ServiceInfo.class)
List<ServiceInfo> listServiceInfo(ServiceSearchParam param) throws Exception {
@ -74,6 +78,48 @@ public class ServiceApi {
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")
@ApiDocMethod(description = "服务列表", elementClass = ServiceInfoVo.class)
List<ServiceInfoVo> listService(ServiceSearchParam param) throws Exception {
@ -133,7 +179,7 @@ public class ServiceApi {
return response.body().string();
} else {
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() {
String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone");
if (StringUtils.isBlank(eurekaUrls)) {
throw new ApiException("未指定eureka.client.serviceUrl.defaultZone参数");
throw new BizException("未指定eureka.client.serviceUrl.defaultZone参数");
}
String url = eurekaUrls.split("\\,")[0];
if (url.endsWith("/")) {

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

@ -46,10 +46,12 @@ public class LimitNewUpdateParam {
/** 返回的错误码, 数据库字段:limit_code */
@ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64")
private String limitCode;
/** 返回的错误信息, 数据库字段:limit_msg */
@ApiDocField(description = "返回的错误信息")
@Length(max = 100, message = "limitMsg长度不能超过100")
private String limitMsg;
/** 令牌桶容量, 数据库字段: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 lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@ -12,23 +13,25 @@ import javax.validation.constraints.NotNull;
*/
@Getter
@Setter
public class RouteParam {
@NotBlank(message = "serviceId不能为空")
@ApiDocField(description = "serviceId")
private String serviceId;
public class RouteUpdateParam {
/**
* 路由的Id
*/
@NotBlank(message = "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
*/
@NotBlank(message = "uri不能为空")
@Length(max = 100, message = "uri长度不能超过100")
@ApiDocField(description = "路由uri")
private String uri;
@ -36,8 +39,21 @@ public class RouteParam {
* 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;
/**
* 状态
*/
@ -45,4 +61,7 @@ public class RouteParam {
@ApiDocField(description = "状态,0:审核,1:启用,2:禁用")
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 = "描述")
private String description;
/** 是否是自定义服务,1:是,0:否 */
@ApiDocField(description = "是否是自定义服务,1:是,0:否")
private Integer custom;
}

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

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

@ -1,6 +1,9 @@
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.math.NumberUtils;
import org.apache.curator.framework.CuratorFramework;
@ -23,6 +26,7 @@ import static com.gitee.sop.adminserver.bean.SopAdminConstants.SOP_ROUTE_PERMISS
* @author tanghc
*/
@Configuration
@Slf4j
public class ZookeeperContext {
private static CuratorFramework client;
@ -55,6 +59,24 @@ public class ZookeeperContext {
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() {
return SOP_ROUTE_PERMISSION_PATH;
}
@ -95,30 +117,72 @@ public class ZookeeperContext {
* 对已存在的path赋值如果path不存在抛异常
*
* @param path 已存在的
* @param data
* @param data 数据
* @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)) {
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已存在抛异常
* @param path 待创建的path
* @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)) {
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 data
* @return
* @throws Exception
*/
public static String createOrUpdateData(String path, String data) throws Exception {
return getClient().create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
public static String createOrUpdateData(String path, String data) {
try {
return getClient().create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.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)) {
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 父节点
* @return
* @throws Exception
*/
public static List<ChildData> getChildrenData(String parentPath) throws Exception {
public static List<ChildData> getChildrenData(String parentPath) {
PathChildrenCache pathChildrenCache = buildPathChildrenCache(parentPath);
if (pathChildrenCache == null) {
return Collections.emptyList();
@ -160,7 +236,7 @@ public class ZookeeperContext {
return pathChildrenCache.getCurrentData();
}
public static PathChildrenCache buildPathChildrenCache(String path) throws Exception {
public static PathChildrenCache buildPathChildrenCache(String path) {
if (!isPathExist(path)) {
return null;
}
@ -168,7 +244,11 @@ public class ZookeeperContext {
// 且第三个参数要设置为true,不然ChildData对象中的getData返回null
PathChildrenCache childrenCache = new PathChildrenCache(client, path, true);
// 列出子节点数据列表,需要使用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;
}

@ -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
* @throws Exception
*/
public void sendRouteConfigMsg(RouteConfigDto routeConfigDto) throws Exception {
public void sendRouteConfigMsg(RouteConfigDto routeConfigDto) {
String configData = JSON.toJSONString(routeConfigDto);
ChannelMsg channelMsg = new ChannelMsg("update", configData);
String jsonData = JSON.toJSONString(channelMsg);

@ -10,7 +10,6 @@ import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
@ -44,8 +43,7 @@ public class RouteService {
return isRoute && gatewayRouteDefinition.getId().contains(id);
}
})
.collect(toList())
;
.collect(toList());
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实例
const client = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
timeout: 5000 // 请求超时时间
timeout: 10000 // 请求超时时间
})
Object.assign(Vue.prototype, {
@ -94,6 +94,14 @@ Object.assign(Vue.prototype, {
}
}).catch(function() {})
},
/**
* 重置表单
* @param formName 表单元素的ref
*/
resetForm(formName) {
const frm = this.$refs[formName]
frm && frm.resetFields()
},
logout: function() {
removeToken()
const fullPath = this.$route.fullPath

@ -1,7 +1,7 @@
<template>
<div class="app-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-tree
ref="tree2"
@ -17,10 +17,10 @@
@node-click="onNodeClick"
>
<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>
<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>
</span>
</span>
@ -222,6 +222,7 @@
export default {
data() {
return {
serviceTextLimitSize: 20,
filterText: '',
treeData: [],
tableData: [],
@ -407,13 +408,8 @@ export default {
}
})
},
resetForm(formName) {
const frm = this.$refs[formName]
frm && frm.resetFields()
},
onLimitDialogClose: function() {
this.resetForm('limitDialogForm')
this.limitDialogVisible = false
},
infoRender: function(row) {
const html = []

@ -1,27 +1,57 @@
<template>
<div class="app-container">
<el-container>
<el-aside style="min-height: 300px;width: 200px;">
<el-input v-model="filterText" prefix-icon="el-icon-search" placeholder="搜索服务..." style="margin-bottom:20px;" size="mini" clearable />
<el-aside style="min-height: 300px;width: 250px;">
<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
ref="tree2"
ref="serviceTree"
:data="treeData"
:props="defaultProps"
:filter-node-method="filterNode"
:highlight-current="true"
:expand-on-click-node="false"
empty-text="无数据"
node-key="id"
node-key="serviceId"
class="filter-tree"
default-expand-all
@node-click="onNodeClick"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<span v-if="data.label.length < 15">{{ data.label }}</span>
<span v-else>
<el-tooltip :content="data.label" class="item" effect="light" placement="right">
<span>{{ data.label.substring(0, 15) + '...' }}</span>
<div>
<el-tooltip v-show="data.custom" content="自定义服务" class="item" effect="light" placement="top">
<i class="el-icon-warning-outline"></i>
</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>
</el-tree>
@ -35,10 +65,20 @@
<el-button type="primary" icon="el-icon-search" @click="onSearchTable">查询</el-button>
</el-form-item>
</el-form>
<el-button
v-show="isCustomService"
type="primary"
size="mini"
icon="el-icon-plus"
@click.stop="addRoute"
>
新建路由
</el-button>
<el-table
:data="tableData"
border
max-height="500"
style="margin-top: 10px;"
>
<el-table-column
prop="name"
@ -69,12 +109,12 @@
</el-table-column>
<el-table-column
prop="ignoreValidate"
label="忽略验证"
label="签名校验"
width="80"
>
<template slot-scope="scope">
<span v-if="scope.row.ignoreValidate === 1" style="color:#67C23A"></span>
<span v-if="scope.row.ignoreValidate === 0" style="color:#909399"></span>
<span v-if="scope.row.ignoreValidate === 0" style="color:#67C23A">校验</span>
<span v-if="scope.row.ignoreValidate === 1" style="color:#E6A23C">不校验</span>
</template>
</el-table-column>
<el-table-column
@ -93,7 +133,7 @@
width="80"
>
<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 === 2" style="color:#F56C6C">已禁用</span>
</template>
@ -106,79 +146,170 @@
<template slot-scope="scope">
<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.custom" type="text" size="mini" @click="onTableDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</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-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>
</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>
export default {
data() {
return {
serviceTextLimitSize: 20,
filterText: '',
treeData: [],
tableData: [],
serviceId: '',
searchFormData: {},
isCustomService: false,
searchFormData: {
id: '',
serviceId: ''
},
defaultProps: {
children: 'children',
label: 'label'
},
routeDialogTitle: '修改路由',
// dialog
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,
roles: [],
@ -186,12 +317,23 @@ export default {
routeId: '',
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: {
filterText(val) {
this.$refs.tree2.filter(val)
this.$refs.serviceTree.filter(val)
}
},
created() {
@ -204,6 +346,12 @@ export default {
this.post('service.list', {}, function(resp) {
const respData = resp.data
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) {
this.serviceId = data.label
this.searchFormData.serviceId = this.serviceId
this.isCustomService = Boolean(data.custom)
this.loadTable()
}
},
@ -250,16 +399,18 @@ export default {
}
const children = []
for (let i = 0; i < data.length; i++) {
const item = { label: data[i].serviceId, parentId: 1 }
children.push(item)
data[i].parentId = 1
data[i].label = data[i].serviceId
children.push(data[i])
}
root.children = children
result.push(root)
return result
},
// table
loadTable: function() {
this.post('route.list', this.searchFormData, function(resp) {
loadTable: function(param) {
const postData = param || this.searchFormData
this.post('route.list', postData, function(resp) {
this.tableData = resp.data
})
},
@ -267,8 +418,11 @@ export default {
this.loadTable()
},
onTableUpdate: function(row) {
Object.assign(this.routeDialogFormData, row)
this.routeDialogTitle = '修改路由'
this.routeDialogVisible = true
this.$nextTick(() => {
Object.assign(this.routeDialogFormData, row)
})
},
onTableAuth: function(row) {
this.authDialogFormData.routeId = row.id
@ -283,6 +437,28 @@ export default {
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() {
if (this.roles.length === 0) {
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) {
if (!row.permission) {
return '(公开)'
@ -302,18 +487,52 @@ export default {
return html.length > 0 ? html.join(', ') : '<span class="x-red">未授权</span>'
},
onRouteDialogSave: function() {
this.routeDialogFormData.serviceId = this.serviceId
this.post('route.update', this.routeDialogFormData, function() {
this.routeDialogVisible = false
this.loadTable()
this.$refs.routeDialogFormRef.validate((valid) => {
if (valid) {
const uri = this.routeDialogFormData.id ? 'route.update' : 'route.add'
this.routeDialogFormData.serviceId = this.serviceId
this.post(uri, this.routeDialogFormData, function() {
this.routeDialogVisible = false
this.loadTable()
})
}
})
},
onAuthDialogSave: function() {
this.post('route.role.update', this.authDialogFormData, function() {
console.log(this.authDialogFormData)
this.authDialogVisible = false
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