diff --git a/sop-admin/sop-admin-front/assets/css/xadmin.css b/sop-admin/sop-admin-front/assets/css/xadmin.css index d3baaa9c..2228b48a 100644 --- a/sop-admin/sop-admin-front/assets/css/xadmin.css +++ b/sop-admin/sop-admin-front/assets/css/xadmin.css @@ -644,6 +644,11 @@ table th, table td { padding: 0 10px; } +.layui-table-cell .layui-btn { + height: 27px; + line-height: 28px; +} + .x-admin-sm .layui-btn-lg{ height: 38px; line-height: 38px; diff --git a/sop-admin/sop-admin-front/assets/js/ApiUtil.js b/sop-admin/sop-admin-front/assets/js/ApiUtil.js index 3c7033dd..bce95141 100644 --- a/sop-admin/sop-admin-front/assets/js/ApiUtil.js +++ b/sop-admin/sop-admin-front/assets/js/ApiUtil.js @@ -55,11 +55,11 @@ var ApiUtil = (function () { , getUrl: function () { return url; } - , createUrl: function (uri) { + , createUrl: function (uri, params) { if (!uri) { - throw new Error('name不能为空'); + throw new Error('uri不能为空'); } - return url + formatUri(uri); + return url + formatUri(uri) + (params ? '?data=' + encodeURIComponent(JSON.stringify(params)) : ''); } , getParam: function (paramName) { return params[paramName]; diff --git a/sop-admin/sop-admin-front/pages/service/routeManager.html b/sop-admin/sop-admin-front/pages/service/routeManager.html index b061b9e6..e826bb6d 100644 --- a/sop-admin/sop-admin-front/pages/service/routeManager.html +++ b/sop-admin/sop-admin-front/pages/service/routeManager.html @@ -43,7 +43,7 @@ -
+
- diff --git a/sop-admin/sop-admin-front/pages/service/routeManager.js b/sop-admin/sop-admin-front/pages/service/routeManager.js index 353895c3..4cc02e52 100644 --- a/sop-admin/sop-admin-front/pages/service/routeManager.js +++ b/sop-admin/sop-admin-front/pages/service/routeManager.js @@ -128,12 +128,16 @@ lib.importJs('../../assets/js/profile.js').use(['element', 'table', 'tree', 'for return ROUTE_STATUS[row.status + '']; } } - , {fixed: 'right', title: '操作', toolbar: '#optBar', width: 100} + , { + fixed: 'right', title: '操作', width: 100, templet: function (row) { + return '修改'; + } + } ]] }); //监听单元格事件 - table.on('tool(routeTable)', function(obj) { + table.on('tool(routeTableFilter)', function(obj) { var data = obj.data; if(obj.event === 'edit'){ //表单初始赋值 @@ -142,8 +146,6 @@ lib.importJs('../../assets/js/profile.js').use(['element', 'table', 'tree', 'for updateForm.setData(data); - // form.val('updateWinFilter', data); - layer.open({ type: 1 ,title: '修改路由' + smTitle diff --git a/sop-admin/sop-admin-front/pages/service/serviceList.html b/sop-admin/sop-admin-front/pages/service/serviceList.html index fc8ab65a..50c44e90 100644 --- a/sop-admin/sop-admin-front/pages/service/serviceList.html +++ b/sop-admin/sop-admin-front/pages/service/serviceList.html @@ -32,11 +32,7 @@ -
- +
diff --git a/sop-admin/sop-admin-front/pages/service/serviceList.js b/sop-admin/sop-admin-front/pages/service/serviceList.js index 7dadee53..fe45c9ea 100644 --- a/sop-admin/sop-admin-front/pages/service/serviceList.js +++ b/sop-admin/sop-admin-front/pages/service/serviceList.js @@ -3,23 +3,23 @@ lib.config({ }).extend({ treetable: 'treetable-lay/treetable' }).use(['element', 'table', 'form', 'treetable'], function () { + var table = layui.table; var layer = layui.layer; var form = layui.form; var treetable = layui.treetable; - var serverTable; // 渲染表格 - var renderTable = function () { + var renderTable = function (params) { layer.load(2); - serverTable = treetable.render({ + treetable.render({ + elem: '#treeTable', treeColIndex: 1, treeSpid: 0, treeIdName: 'id', treePidName: 'parentId', - treeDefaultClose: true, + treeDefaultClose: false, treeLinkage: false, - elem: '#treeTable', - url: ApiUtil.createUrl('service.instance.list'), + url: ApiUtil.createUrl('service.instance.list', params), page: false, cols: [[ {type: 'numbers'}, @@ -29,24 +29,61 @@ lib.config({ {field: 'serverPort', title: '端口号', width: 100}, {field: 'status', title: 'status', width: 100}, {field: 'updateTime', title: '最后更新时间', width: 150}, - {fixed: 'right', templet: '#optCol', title: '操作', width: 200} + {fixed: 'right', title: '操作', width: 200, templet: function (row) { + if (row.parentId > 0) { + var html = []; + if (row.status === 'UP') { + html.push('下线'); + } + if (row.status === 'OUT_OF_SERVICE') { + html.push('上线'); + } + return html.join(''); + } + return ''; + }} ]], done: function () { layer.closeAll('loading'); } }); + + //监听单元格事件 + table.on('tool(treeTableFilter)', function(obj) { + if (obj.event === 'offline') { + var data = obj.data; + layer.confirm('确定要下线【'+data.name+'】吗?', {icon: 3, title:'提示'}, function(index){ + var params = { + serviceId: data.name + , instanceId: data.instanceId + }; + ApiUtil.post('service.instance.offline', params, function (resp) { + layer.alert('修改成功'); + }); + layer.close(index); + }); + } + if (obj.event === 'online') { + var data = obj.data; + layer.confirm('确定要上线【'+data.name+'】吗?', {icon: 3, title:'提示'}, function(index){ + var params = { + serviceId: data.name + , instanceId: data.instanceId + }; + ApiUtil.post('service.instance.online', params, function (resp) { + layer.alert('修改成功'); + }); + layer.close(index); + }); + } + }); }; renderTable(); - form.on('submit(searchFilter)', function(data){ var param = data.field; - serverTable.reload({ - where: { - data: JSON.stringify(param) - } - }) + renderTable(param) return false; }); diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java index 9e7a5595..cf16b8fc 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java @@ -6,6 +6,7 @@ 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.ServiceInstanceParam; 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.ServiceInfoVo; @@ -50,12 +51,8 @@ public class ServiceApi { // eureka.client.serviceUrl.defaultZone @Api(name = "service.list") - @ApiDocMethod(description = "服务列表", elementClass = ServiceInfo.class) + @ApiDocMethod(description = "服务列表(旧)", elementClass = ServiceInfo.class) List listServiceInfo(ServiceSearchParam param) throws Exception { - String json = this.requestEurekaServer(EurekaUri.QUERY_APPS); - EurekaApps eurekaApps = JSON.parseObject(json, EurekaApps.class); - - String routeRootPath = ZookeeperContext.getSopRouteRootPath(param.getProfile()); List childDataList = ZookeeperContext.getChildrenData(routeRootPath); List serviceInfoList = childDataList.stream() @@ -78,7 +75,7 @@ public class ServiceApi { } @Api(name = "service.instance.list") - @ApiDocMethod(description = "服务列表", elementClass = ServiceInfo.class) + @ApiDocMethod(description = "服务列表", elementClass = ServiceInfoVo.class) List listService(ServiceSearchParam param) throws Exception { String json = this.requestEurekaServer(EurekaUri.QUERY_APPS); EurekaApps eurekaApps = JSON.parseObject(json, EurekaApps.class); @@ -87,6 +84,12 @@ public class ServiceApi { List applicationList = eurekaApps.getApplications().getApplication(); AtomicInteger idGen = new AtomicInteger(1); applicationList.stream() + .filter(eurekaApplication -> { + if (StringUtils.isBlank(param.getServiceId())) { + return true; + } + return StringUtils.containsIgnoreCase(eurekaApplication.getName(), param.getServiceId()); + }) .forEach(eurekaApplication -> { int pid = idGen.getAndIncrement(); String name = eurekaApplication.getName(); @@ -107,29 +110,33 @@ public class ServiceApi { serviceInfoVoList.add(vo); } }); + return serviceInfoVoList; } - private String requestEurekaServer(EurekaUri uri) throws IOException { - Request request = this.buildRequest(EurekaUri.QUERY_APPS); + @Api(name = "service.instance.offline") + @ApiDocMethod(description = "服务下线") + void serviceOffline(ServiceInstanceParam param) throws IOException { + this.requestEurekaServer(EurekaUri.OFFLINE_SERVICE, param.getServiceId(), param.getInstanceId()); + } + + @Api(name = "service.instance.online") + @ApiDocMethod(description = "服务上线") + void serviceOnline(ServiceInstanceParam param) throws IOException { + this.requestEurekaServer(EurekaUri.ONLINE_SERVICE, param.getServiceId(), param.getInstanceId()); + } + + private String requestEurekaServer(EurekaUri eurekaUri, String... args) throws IOException { + Request request = eurekaUri.getRequest(this.eurekaUrl, args); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { return response.body().string(); } else { - return "{}"; + log.error("操作失败,url:{}, msg:{}, code:{}", eurekaUri.getUri(args), response.message(), response.code()); + throw new ApiException("操作失败", String.valueOf(response.code())); } } - private Request buildRequest(EurekaUri uri) { - String apiUrl = this.eurekaUrl + uri.getUri(); - Request request = new Request.Builder() - .url(apiUrl) - .addHeader("Content-Type", "application/json") - .addHeader("Accept", "application/json") - .build(); - return request; - } - @PostConstruct protected void after() { String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone"); diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java index e5fd5d88..39904ef6 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java @@ -1,5 +1,6 @@ package com.gitee.sop.adminserver.api.service.param; +import com.gitee.easyopen.doc.annotation.ApiDocField; import lombok.Getter; import lombok.Setter; @@ -9,5 +10,6 @@ import lombok.Setter; @Getter @Setter public class RouteSearchParam extends ServiceSearchParam { + @ApiDocField(description = "id") private String id; } diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ServiceInstanceParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ServiceInstanceParam.java new file mode 100644 index 00000000..7dfe26fc --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ServiceInstanceParam.java @@ -0,0 +1,20 @@ +package com.gitee.sop.adminserver.api.service.param; + +import com.gitee.easyopen.doc.annotation.ApiDocField; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author tanghc + */ +@Data +public class ServiceInstanceParam { + @ApiDocField(description = "serviceId") + @NotBlank(message = "serviceId不能为空") + private String serviceId; + + @ApiDocField(description = "instanceId") + @NotBlank(message = "instanceId不能为空") + private String instanceId; +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ServiceInfoVo.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ServiceInfoVo.java index 68cf73a6..e1065cd9 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ServiceInfoVo.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ServiceInfoVo.java @@ -15,7 +15,7 @@ public class ServiceInfoVo { @ApiDocField(description = "id") private Integer id; - @ApiDocField(description = "服务名称") + @ApiDocField(description = "服务名称(serviceId)") private String name; @ApiDocField(description = "instanceId") @@ -42,6 +42,10 @@ public class ServiceInfoVo { @ApiDocField(description = "parentId") private Integer parentId; + public String getServiceId() { + return this.name; + } + @JSONField(format = "yyyy-MM-dd HH:mm:ss") public Date getUpdateTime() { if (StringUtils.isBlank(lastUpdatedTimestamp)) { diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/EurekaUri.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/EurekaUri.java index 8f345a82..5f70f86b 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/EurekaUri.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/EurekaUri.java @@ -1,20 +1,70 @@ package com.gitee.sop.adminserver.bean; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.internal.http.HttpMethod; +import org.apache.commons.lang.ArrayUtils; +import org.springframework.web.bind.annotation.RequestMethod; + /** * https://github.com/Netflix/eureka/wiki/Eureka-REST-operations + * * @author tanghc */ public enum EurekaUri { - /** Query for all instances */ - QUERY_APPS("/apps"), + /** + * 查询所有实例 Query for all instances + */ + QUERY_APPS(RequestMethod.GET, "/apps"), + /** + * 下线 Take instance out of service + */ + OFFLINE_SERVICE(RequestMethod.PUT, "/apps/%s/%s/status?value=OUT_OF_SERVICE"), + /** + * 上线 Move instance back into service (remove override) + */ + ONLINE_SERVICE(RequestMethod.DELETE, "/apps/%s/%s/status?value=UP"), ; + public static final String URL_PREFIX = "/"; + String uri; + RequestMethod requestMethod; - EurekaUri(String uri) { + EurekaUri(RequestMethod httpMethod, String uri) { + if (!uri.startsWith(URL_PREFIX)) { + uri = "/" + uri; + } this.uri = uri; + this.requestMethod = httpMethod; + } + + public String getUri(String... args) { + if (ArrayUtils.isEmpty(args)) { + return uri; + } + Object[] param = ArrayUtils.clone(args); + return String.format(uri, param); } - public String getUri() { - return uri; - }} + public Request getRequest(String url, String... args) { + String requestUrl = url + getUri(args); + Request request = this.getBuilder() + .url(requestUrl) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + return request; + } + + public Request.Builder getBuilder() { + String method = requestMethod.name(); + RequestBody requestBody = null; + if (HttpMethod.requiresRequestBody(method)) { + MediaType contentType = MediaType.parse(org.springframework.http.MediaType.APPLICATION_JSON_VALUE); + requestBody = RequestBody.create(contentType, "{}"); + } + return new Request.Builder().method(requestMethod.name(), requestBody); + } +}