服务列表查询、上下线

1.x
tanghc 6 years ago
parent 44f3c76c78
commit 340598ee66
  1. 5
      sop-admin/sop-admin-front/assets/css/xadmin.css
  2. 6
      sop-admin/sop-admin-front/assets/js/ApiUtil.js
  3. 5
      sop-admin/sop-admin-front/pages/service/routeManager.html
  4. 10
      sop-admin/sop-admin-front/pages/service/routeManager.js
  5. 6
      sop-admin/sop-admin-front/pages/service/serviceList.html
  6. 63
      sop-admin/sop-admin-front/pages/service/serviceList.js
  7. 45
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java
  8. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java
  9. 20
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ServiceInstanceParam.java
  10. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ServiceInfoVo.java
  11. 62
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/EurekaUri.java

@ -644,6 +644,11 @@ table th, table td {
padding: 0 10px; padding: 0 10px;
} }
.layui-table-cell .layui-btn {
height: 27px;
line-height: 28px;
}
.x-admin-sm .layui-btn-lg{ .x-admin-sm .layui-btn-lg{
height: 38px; height: 38px;
line-height: 38px; line-height: 38px;

@ -55,11 +55,11 @@ var ApiUtil = (function () {
, getUrl: function () { , getUrl: function () {
return url; return url;
} }
, createUrl: function (uri) { , createUrl: function (uri, params) {
if (!uri) { 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) { , getParam: function (paramName) {
return params[paramName]; return params[paramName];

@ -43,7 +43,7 @@
</form> </form>
</div> </div>
<table class="layui-hide" id="routeTable" lay-filter="routeTable"></table> <table class="layui-hide" id="routeTable" lay-filter="routeTableFilter"></table>
<script type="text/html" id="toolbar"> <script type="text/html" id="toolbar">
<div class="layui-btn-container"> <div class="layui-btn-container">
<button class="layui-btn layui-btn-ms layui-btn-normal" lay-event="add"> <button class="layui-btn layui-btn-ms layui-btn-normal" lay-event="add">
@ -51,9 +51,6 @@
</button> </button>
</div> </div>
</script> </script>
<script type="text/html" id="optBar" >
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="edit">修改</a>
</script>
</div> </div>
</div> </div>

@ -128,12 +128,16 @@ lib.importJs('../../assets/js/profile.js').use(['element', 'table', 'tree', 'for
return ROUTE_STATUS[row.status + '']; return ROUTE_STATUS[row.status + ''];
} }
} }
, {fixed: 'right', title: '操作', toolbar: '#optBar', width: 100} , {
fixed: 'right', title: '操作', width: 100, templet: function (row) {
return '<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="edit">修改</a>';
}
}
]] ]]
}); });
//监听单元格事件 //监听单元格事件
table.on('tool(routeTable)', function(obj) { table.on('tool(routeTableFilter)', function(obj) {
var data = obj.data; var data = obj.data;
if(obj.event === 'edit'){ if(obj.event === 'edit'){
//表单初始赋值 //表单初始赋值
@ -142,8 +146,6 @@ lib.importJs('../../assets/js/profile.js').use(['element', 'table', 'tree', 'for
updateForm.setData(data); updateForm.setData(data);
// form.val('updateWinFilter', data);
layer.open({ layer.open({
type: 1 type: 1
,title: '修改路由' + smTitle ,title: '修改路由' + smTitle

@ -32,11 +32,7 @@
</form> </form>
</div> </div>
<table class="layui-hide" id="treeTable"></table> <table class="layui-hide" id="treeTable" lay-filter="treeTableFilter"></table>
<script type="text/html" id="optCol">
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="edit">修改</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">下线</a>
</script>
</div> </div>
<script type="text/javascript" src="../../assets/js/lib.js"></script> <script type="text/javascript" src="../../assets/js/lib.js"></script>

@ -3,23 +3,23 @@ lib.config({
}).extend({ }).extend({
treetable: 'treetable-lay/treetable' treetable: 'treetable-lay/treetable'
}).use(['element', 'table', 'form', 'treetable'], function () { }).use(['element', 'table', 'form', 'treetable'], function () {
var table = layui.table;
var layer = layui.layer; var layer = layui.layer;
var form = layui.form; var form = layui.form;
var treetable = layui.treetable; var treetable = layui.treetable;
var serverTable;
// 渲染表格 // 渲染表格
var renderTable = function () { var renderTable = function (params) {
layer.load(2); layer.load(2);
serverTable = treetable.render({ treetable.render({
elem: '#treeTable',
treeColIndex: 1, treeColIndex: 1,
treeSpid: 0, treeSpid: 0,
treeIdName: 'id', treeIdName: 'id',
treePidName: 'parentId', treePidName: 'parentId',
treeDefaultClose: true, treeDefaultClose: false,
treeLinkage: false, treeLinkage: false,
elem: '#treeTable', url: ApiUtil.createUrl('service.instance.list', params),
url: ApiUtil.createUrl('service.instance.list'),
page: false, page: false,
cols: [[ cols: [[
{type: 'numbers'}, {type: 'numbers'},
@ -29,24 +29,61 @@ lib.config({
{field: 'serverPort', title: '端口号', width: 100}, {field: 'serverPort', title: '端口号', width: 100},
{field: 'status', title: 'status', width: 100}, {field: 'status', title: 'status', width: 100},
{field: 'updateTime', title: '最后更新时间', width: 150}, {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('<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="offline">下线</a>');
}
if (row.status === 'OUT_OF_SERVICE') {
html.push('<a class="layui-btn layui-btn-xs" lay-event="online">上线</a>');
}
return html.join('');
}
return '';
}}
]], ]],
done: function () { done: function () {
layer.closeAll('loading'); 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(); renderTable();
form.on('submit(searchFilter)', function(data){ form.on('submit(searchFilter)', function(data){
var param = data.field; var param = data.field;
serverTable.reload({ renderTable(param)
where: {
data: JSON.stringify(param)
}
})
return false; return false;
}); });

@ -6,6 +6,7 @@ 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.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.param.ServiceSearchParam;
import com.gitee.sop.adminserver.api.service.result.ServiceInfo; import com.gitee.sop.adminserver.api.service.result.ServiceInfo;
import com.gitee.sop.adminserver.api.service.result.ServiceInfoVo; import com.gitee.sop.adminserver.api.service.result.ServiceInfoVo;
@ -50,12 +51,8 @@ public class ServiceApi {
// eureka.client.serviceUrl.defaultZone // 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 {
String json = this.requestEurekaServer(EurekaUri.QUERY_APPS);
EurekaApps eurekaApps = JSON.parseObject(json, EurekaApps.class);
String routeRootPath = ZookeeperContext.getSopRouteRootPath(param.getProfile()); String routeRootPath = ZookeeperContext.getSopRouteRootPath(param.getProfile());
List<ChildData> childDataList = ZookeeperContext.getChildrenData(routeRootPath); List<ChildData> childDataList = ZookeeperContext.getChildrenData(routeRootPath);
List<ServiceInfo> serviceInfoList = childDataList.stream() List<ServiceInfo> serviceInfoList = childDataList.stream()
@ -78,7 +75,7 @@ public class ServiceApi {
} }
@Api(name = "service.instance.list") @Api(name = "service.instance.list")
@ApiDocMethod(description = "服务列表", elementClass = ServiceInfo.class) @ApiDocMethod(description = "服务列表", elementClass = ServiceInfoVo.class)
List<ServiceInfoVo> listService(ServiceSearchParam param) throws Exception { List<ServiceInfoVo> listService(ServiceSearchParam param) throws Exception {
String json = this.requestEurekaServer(EurekaUri.QUERY_APPS); String json = this.requestEurekaServer(EurekaUri.QUERY_APPS);
EurekaApps eurekaApps = JSON.parseObject(json, EurekaApps.class); EurekaApps eurekaApps = JSON.parseObject(json, EurekaApps.class);
@ -87,6 +84,12 @@ public class ServiceApi {
List<EurekaApplication> applicationList = eurekaApps.getApplications().getApplication(); List<EurekaApplication> applicationList = eurekaApps.getApplications().getApplication();
AtomicInteger idGen = new AtomicInteger(1); AtomicInteger idGen = new AtomicInteger(1);
applicationList.stream() applicationList.stream()
.filter(eurekaApplication -> {
if (StringUtils.isBlank(param.getServiceId())) {
return true;
}
return StringUtils.containsIgnoreCase(eurekaApplication.getName(), param.getServiceId());
})
.forEach(eurekaApplication -> { .forEach(eurekaApplication -> {
int pid = idGen.getAndIncrement(); int pid = idGen.getAndIncrement();
String name = eurekaApplication.getName(); String name = eurekaApplication.getName();
@ -107,29 +110,33 @@ public class ServiceApi {
serviceInfoVoList.add(vo); serviceInfoVoList.add(vo);
} }
}); });
return serviceInfoVoList; return serviceInfoVoList;
} }
private String requestEurekaServer(EurekaUri uri) throws IOException { @Api(name = "service.instance.offline")
Request request = this.buildRequest(EurekaUri.QUERY_APPS); @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(); Response response = client.newCall(request).execute();
if (response.isSuccessful()) { if (response.isSuccessful()) {
return response.body().string(); return response.body().string();
} else { } 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 @PostConstruct
protected void after() { protected void after() {
String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone"); String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone");

@ -1,5 +1,6 @@
package com.gitee.sop.adminserver.api.service.param; package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -9,5 +10,6 @@ import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class RouteSearchParam extends ServiceSearchParam { public class RouteSearchParam extends ServiceSearchParam {
@ApiDocField(description = "id")
private String id; private String id;
} }

@ -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;
}

@ -15,7 +15,7 @@ public class ServiceInfoVo {
@ApiDocField(description = "id") @ApiDocField(description = "id")
private Integer id; private Integer id;
@ApiDocField(description = "服务名称") @ApiDocField(description = "服务名称(serviceId)")
private String name; private String name;
@ApiDocField(description = "instanceId") @ApiDocField(description = "instanceId")
@ -42,6 +42,10 @@ public class ServiceInfoVo {
@ApiDocField(description = "parentId") @ApiDocField(description = "parentId")
private Integer parentId; private Integer parentId;
public String getServiceId() {
return this.name;
}
@JSONField(format = "yyyy-MM-dd HH:mm:ss") @JSONField(format = "yyyy-MM-dd HH:mm:ss")
public Date getUpdateTime() { public Date getUpdateTime() {
if (StringUtils.isBlank(lastUpdatedTimestamp)) { if (StringUtils.isBlank(lastUpdatedTimestamp)) {

@ -1,20 +1,70 @@
package com.gitee.sop.adminserver.bean; 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 * https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
*
* @author tanghc * @author tanghc
*/ */
public enum EurekaUri { 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; String uri;
RequestMethod requestMethod;
EurekaUri(String uri) { EurekaUri(RequestMethod httpMethod, String uri) {
if (!uri.startsWith(URL_PREFIX)) {
uri = "/" + uri;
}
this.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() { public Request getRequest(String url, String... args) {
return uri; 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);
}
}

Loading…
Cancel
Save