This commit is contained in:
tanghc
2020-10-14 21:02:41 +08:00
parent 1850d6d567
commit 0c4dd04bc7
29 changed files with 536 additions and 441 deletions

View File

@@ -15,11 +15,10 @@ boolean needToken() default false;
@ApiOperation(value="传递token", notes = "传递token") @ApiOperation(value="传递token", notes = "传递token")
@Open(value = "story.get.token", needToken = true/* 设置true网关会校验token是否存在 */) @Open(value = "story.get.token", needToken = true/* 设置true网关会校验token是否存在 */)
@RequestMapping("token") @RequestMapping("token")
public StoryResult token(StoryParam story) { public StoryResult token(StoryParam story, HttpServletRequest request) {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext(); OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
String appAuthToken = openContext.getAppAuthToken();
StoryResult result = new StoryResult(); StoryResult result = new StoryResult();
result.setName("appAuthToken:" + appAuthToken); result.setName("appAuthToken:" + openContext.getAppAuthToken());
return result; return result;
} }
``` ```

View File

@@ -13,6 +13,7 @@ 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.RouteDeleteParam;
import com.gitee.sop.adminserver.api.service.param.RoutePermissionParam; import com.gitee.sop.adminserver.api.service.param.RoutePermissionParam;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam; import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.api.service.param.RouteStatusUpdateParam;
import com.gitee.sop.adminserver.api.service.param.RouteUpdateParam; import com.gitee.sop.adminserver.api.service.param.RouteUpdateParam;
import com.gitee.sop.adminserver.api.service.result.RouteVO; import com.gitee.sop.adminserver.api.service.result.RouteVO;
import com.gitee.sop.adminserver.bean.RouteConfigDto; import com.gitee.sop.adminserver.bean.RouteConfigDto;
@@ -123,6 +124,26 @@ public class RouteApi {
this.updateRouteConfig(param); this.updateRouteConfig(param);
} }
@Api(name = "route.status.update")
@ApiDocMethod(description = "修改路由状态")
void updateRouteStatus(RouteStatusUpdateParam param) {
String routeId = param.getId();
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", routeId);
boolean doSave = configRouteBase == null;
if (doSave) {
configRouteBase = new ConfigRouteBase();
configRouteBase.setRouteId(routeId);
}
configRouteBase.setStatus(param.getStatus());
int i = doSave ? configRouteBaseMapper.save(configRouteBase)
: configRouteBaseMapper.update(configRouteBase);
if (i > 0) {
this.sendMsg(configRouteBase);
}
}
@Api(name = "route.del") @Api(name = "route.del")
@ApiDocMethod(description = "删除路由") @ApiDocMethod(description = "删除路由")
void delRoute(RouteDeleteParam param) { void delRoute(RouteDeleteParam param) {

View File

@@ -75,6 +75,7 @@ public class ServiceApi {
return serviceId.contains(param.getServiceId()); return serviceId.contains(param.getServiceId());
} }
}) })
.sorted()
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@@ -0,0 +1,32 @@
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 RouteStatusUpdateParam {
/**
* 路由的Id
*/
@NotBlank(message = "id不能为空")
@ApiDocField(description = "路由id")
private String id;
/**
* 状态
*/
@NotNull
@ApiDocField(description = "状态0审核1启用2禁用")
private Byte status;
}

View File

@@ -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.c6dfb7ee.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 n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"66819987","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d32e7":"213708f2","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-4bdcb5ea":"cf292569","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"52bc6b2c","chunk-9f479afe":"3e73ea9f","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-4bdcb5ea":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d32e7":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-4bdcb5ea":"b23e7407","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"3b12267b","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.2e027154.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.6095bfbf.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 n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"02b977ea","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d32e7":"e7c489be","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-30c6c34f":"b288bbf5","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"494fc338","chunk-9f479afe":"2093f9d0","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-30c6c34f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d32e7":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-30c6c34f":"3b12267b","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"c4612b4a","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.f323bdd7.js></script></body></html>

View File

@@ -1 +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}.limit-tip[data-v-08125a7c]{cursor:pointer;margin-left:10px} .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}.roles-content{cursor:pointer;color:#20a0ff}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d32e7"],{"5c58":function(t,e,l){"use strict";l.r(e);var a=function(){var t=this,e=t.$createElement,l=t._self._c||e;return l("div",{staticClass:"app-container"},[l("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[l("el-form-item",{attrs:{label:"接口名"}},[l("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"输入接口名或版本号"},model:{value:t.searchFormData.routeId,callback:function(e){t.$set(t.searchFormData,"routeId",e)},expression:"searchFormData.routeId"}})],1),t._v(" "),l("el-form-item",[l("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.loadTable}},[t._v("搜索")])],1)],1),t._v(" "),l("el-alert",{staticStyle:{"margin-bottom":"10px"},attrs:{title:"监控数据保存在网关服务器,重启网关数据会清空。",type:"info",closable:!1}}),t._v(" "),l("el-table",{attrs:{data:t.tableData,border:"","default-expand-all":!1,"row-key":"id",height:"500","empty-text":"无数据"}},[l("el-table-column",{attrs:{fixed:"",prop:"instanceId",label:"网关实例",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.children?t._e():l("span",[t._v(t._s(e.row.instanceId))])]}}])}),t._v(" "),l("el-table-column",{attrs:{fixed:"",prop:"name",label:"接口名 (版本号)",width:"280"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.name+(e.row.version?" ("+e.row.version+")":""))+"\n ")]}}])}),t._v(" "),l("el-table-column",{attrs:{prop:"serviceId",label:"serviceId",width:"170"}}),t._v(" "),l("el-table-column",{attrs:{prop:"maxTime",label:"最大耗时(ms)",width:"125"}},[l("template",{slot:"header"},[t._v("\n 最大耗时(ms)\n "),l("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"},on:{click:function(e){return t.$alert("耗时计算:签名验证成功后开始,微服务返回结果后结束")}}})])],2),t._v(" "),l("el-table-column",{attrs:{prop:"minTime",label:"最小耗时(ms)",width:"120"}}),t._v(" "),l("el-table-column",{attrs:{prop:"avgTime",label:"平均耗时(ms)",width:"120"}}),t._v(" "),l("el-table-column",{attrs:{prop:"totalCount",label:"总调用次数",width:"100"}}),t._v(" "),l("el-table-column",{attrs:{prop:"successCount",label:"成功次数",width:"100"}}),t._v(" "),l("el-table-column",{attrs:{prop:"errorCount",label:"失败次数",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.errorCount>0?l("el-link",{staticStyle:{"text-decoration":"underline"},attrs:{underline:!1,type:"danger"},on:{click:function(l){return t.onShowErrorDetail(e.row)}}},[t._v("\n "+t._s(e.row.errorCount)+"\n ")]):t._e(),t._v(" "),0===e.row.errorCount?l("span",[t._v("0")]):t._e()]}}])},[l("template",{slot:"header"},[t._v("\n 失败次数\n "),l("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"},on:{click:function(e){return t.$alert("只统计微服务返回的未知错误JSR-303验证错误算作成功")}}})])],2)],1),t._v(" "),l("el-dialog",{attrs:{title:"错误详情",visible:t.logDetailVisible,width:"60%"},on:{"update:visible":function(e){t.logDetailVisible=e}}},[l("div",{staticStyle:{"overflow-x":"auto"},domProps:{innerHTML:t._s(t.errorMsgDetail)}}),t._v(" "),l("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[l("el-button",{attrs:{type:"primary"},on:{click:function(e){t.logDetailVisible=!1}}},[t._v("关 闭")])],1)])],1)},o=[],r={data:function(){return{searchFormData:{routeId:""},tableData:[],logDetailVisible:!1,errorMsgDetail:""}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("monitor.data.list",this.searchFormData,function(t){var e=t.data;this.tableData=e.monitorInfoData})},onShowErrorDetail:function(t){var e=t.errorMsgList;this.errorMsgDetail=e.length>0?e.join("<br>"):"无内容",this.logDetailVisible=!0}}},i=r,n=l("2877"),s=Object(n["a"])(i,a,o,!1,null,null,null);e["default"]=s.exports}}]);

View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d32e7"],{"5c58":function(t,e,a){"use strict";a.r(e);var l=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"接口名"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"输入接口名或版本号"},model:{value:t.searchFormData.routeId,callback:function(e){t.$set(t.searchFormData,"routeId",e)},expression:"searchFormData.routeId"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.loadTable}},[t._v("搜索")])],1)],1),t._v(" "),a("el-alert",{staticStyle:{"margin-bottom":"10px"},attrs:{title:"监控数据保存在网关服务器,重启网关数据会清空。",type:"info",closable:!1}}),t._v(" "),a("el-table",{attrs:{data:t.tableData,border:"","default-expand-all":!1,"row-key":"id",height:"500","empty-text":"无数据"}},[a("el-table-column",{attrs:{fixed:"",prop:"instanceId",label:"网关实例",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.children?t._e():a("span",[t._v(t._s(e.row.instanceId))])]}}])}),t._v(" "),a("el-table-column",{attrs:{fixed:"",prop:"name",label:"接口名 (版本号)",width:"280"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.name+(e.row.version?" ("+e.row.version+")":""))+"\n ")]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"serviceId",label:"serviceId",width:"170"}}),t._v(" "),a("el-table-column",{attrs:{prop:"maxTime",label:"最大耗时(ms)",width:"125"}},[a("template",{slot:"header"},[t._v("\n 最大耗时(ms)\n "),a("el-tooltip",{attrs:{effect:"dark",content:"耗时计算:签名验证成功后开始,微服务返回结果后结束",placement:"top"}},[a("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"}})])],1)],2),t._v(" "),a("el-table-column",{attrs:{prop:"minTime",label:"最小耗时(ms)",width:"120"}}),t._v(" "),a("el-table-column",{attrs:{prop:"avgTime",label:"平均耗时(ms)",width:"120"}}),t._v(" "),a("el-table-column",{attrs:{prop:"totalCount",label:"总调用次数",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"successCount",label:"成功次数",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"errorCount",label:"失败次数",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.errorCount>0?a("el-link",{staticStyle:{"text-decoration":"underline"},attrs:{underline:!1,type:"danger"},on:{click:function(a){return t.onShowErrorDetail(e.row)}}},[t._v("\n "+t._s(e.row.errorCount)+"\n ")]):t._e(),t._v(" "),0===e.row.errorCount?a("span",[t._v("0")]):t._e()]}}])},[a("template",{slot:"header"},[t._v("\n 失败次数\n "),a("el-tooltip",{attrs:{effect:"dark",content:"只统计微服务返回的未知错误JSR-303验证错误算作成功",placement:"top-end"}},[a("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"}})])],1)],2)],1),t._v(" "),a("el-dialog",{attrs:{title:"错误详情",visible:t.logDetailVisible,width:"60%"},on:{"update:visible":function(e){t.logDetailVisible=e}}},[a("div",{staticStyle:{"overflow-x":"auto"},domProps:{innerHTML:t._s(t.errorMsgDetail)}}),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{attrs:{type:"primary"},on:{click:function(e){t.logDetailVisible=!1}}},[t._v("关 闭")])],1)])],1)},o=[],r={data:function(){return{searchFormData:{routeId:""},tableData:[],logDetailVisible:!1,errorMsgDetail:""}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("monitor.data.list",this.searchFormData,function(t){var e=t.data;this.tableData=e.monitorInfoData})},onShowErrorDetail:function(t){var e=t.errorMsgList;this.errorMsgDetail=e.length>0?e.join("<br>"):"无内容",this.logDetailVisible=!0}}},i=r,n=a("2877"),s=Object(n["a"])(i,l,o,!1,null,null,null);e["default"]=s.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -63,3 +63,6 @@ div:focus {
.app-container { .app-container {
padding: 20px; padding: 20px;
} }
.cell .el-button {padding: 0;}
span.tip {color: #909399;font-size: 12px;}

View File

@@ -94,6 +94,18 @@ Object.assign(Vue.prototype, {
} }
}).catch(function() {}) }).catch(function() {})
}, },
downloadText(filename, text) {
const element = document.createElement('a')
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text))
element.setAttribute('download', filename)
element.style.display = 'none'
document.body.appendChild(element)
element.click()
document.body.removeChild(element);
},
/** /**
* 重置表单 * 重置表单
* @param formName 表单元素的ref * @param formName 表单元素的ref

View File

@@ -15,14 +15,9 @@
fit fit
highlight-current-row highlight-current-row
> >
<el-table-column
prop="id"
label="ID"
width="80"
/>
<el-table-column <el-table-column
prop="appKey" prop="appKey"
label="appId" label="AppId"
width="250" width="250"
/> />
<el-table-column <el-table-column
@@ -37,11 +32,11 @@
<el-table-column <el-table-column
prop="roleList" prop="roleList"
label="角色" label="角色"
width="100" width="150"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<div v-html="roleRender(scope.row)"></div> <span v-html="roleRender(scope.row)"></span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@@ -59,24 +54,20 @@
label="添加时间" label="添加时间"
width="160" width="160"
/> />
<el-table-column
prop="gmtModified"
label="修改时间"
width="160"
/>
<el-table-column <el-table-column
prop="remark" prop="remark"
label="备注" label="备注"
width="120" width="200"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> />
<el-table-column <el-table-column
label="操作" label="操作"
width="150" width="200"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button> <el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
<el-button type="text" size="mini" @click="onKeysUpdate(scope.row)">秘钥管理</el-button> <el-button type="text" size="mini" @click="onKeysUpdate(scope.row)">秘钥管理</el-button>
<el-button type="text" size="mini" @click="onExportKeys(scope.row)">导出秘钥</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -281,6 +272,20 @@ export default {
onKeysUpdate: function(row) { onKeysUpdate: function(row) {
this.$router.push({ path: `keys?appKey=${row.appKey}` }) this.$router.push({ path: `keys?appKey=${row.appKey}` })
}, },
onExportKeys: function(row) {
this.post('isv.keys.get', { appKey: row.appKey }, function(resp) {
const data = resp.data
const appId = data.appKey
const privateKeyIsv = data.privateKeyIsv
const publicKeyPlatform = data.publicKeyPlatform
let content = `AppId${appId}\n\n开发者私钥\n${privateKeyIsv}\n\n`
if (publicKeyPlatform) {
content = content + `平台公钥:\n${publicKeyPlatform}`
}
const filename = `${appId}.txt`
this.downloadText(filename, content)
})
},
onSizeChange: function(size) { onSizeChange: function(size) {
this.searchFormData.pageSize = size this.searchFormData.pageSize = size
this.loadTable() this.loadTable()

View File

@@ -1,229 +1,215 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-container> <div v-if="tabsData.length === 0">
<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 /> </div>
<el-tree <div v-else>
ref="tree2" <el-tabs v-model="tabsActive" type="card" @tab-click="selectTab">
:data="treeData" <el-tab-pane v-for="tabName in tabsData" :key="tabName" :label="tabName" :name="tabName" />
:props="defaultProps" </el-tabs>
:filter-node-method="filterNode" <el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini">
:highlight-current="true" <el-form-item label="路由ID">
:expand-on-click-node="false" <el-input v-model="searchFormData.routeId" placeholder="接口名,支持模糊查询" clearable />
empty-text="无数据" </el-form-item>
node-key="id" <el-form-item label="AppId">
class="filter-tree" <el-input v-model="searchFormData.appKey" placeholder="AppId支持模糊查询" clearable />
default-expand-all </el-form-item>
@node-click="onNodeClick" <el-form-item label="IP">
<el-input v-model="searchFormData.limitIp" placeholder="ip支持模糊查询" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="onSearchTable">查询</el-button>
</el-form-item>
</el-form>
<el-button type="primary" size="mini" icon="el-icon-plus" style="margin-bottom: 10px;" @click="onAdd">新增限流</el-button>
<el-table
:data="pageInfo.list"
border
>
<el-table-column
prop="limitKey"
label="限流维度"
width="400"
> >
<span slot-scope="{ node, data }" class="custom-tree-node"> <template slot-scope="scope">
<div> <div v-html="limitRender(scope.row)"></div>
<el-tooltip v-show="data.custom" content="自定义服务" class="item" effect="light" placement="left"> </template>
<i class="el-icon-warning-outline"></i> </el-table-column>
</el-tooltip> <el-table-column
<span v-if="data.label.length < serviceTextLimitSize">{{ data.label }}</span> prop="limitType"
<span v-else> label="限流策略"
<el-tooltip :content="data.label" class="item" effect="light" placement="right"> width="120"
<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>
</el-aside>
<el-main style="padding-top:0">
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini">
<el-form-item label="路由ID">
<el-input v-model="searchFormData.routeId" placeholder="接口名,支持模糊查询" clearable />
</el-form-item>
<el-form-item label="AppId">
<el-input v-model="searchFormData.appKey" placeholder="AppId支持模糊查询" clearable />
</el-form-item>
<el-form-item label="IP">
<el-input v-model="searchFormData.limitIp" placeholder="ip支持模糊查询" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="onSearchTable">查询</el-button>
</el-form-item>
</el-form>
<el-button type="primary" size="mini" icon="el-icon-plus" style="margin-bottom: 10px;" @click="onAdd">新增限流</el-button>
<el-table
:data="pageInfo.list"
border
> >
<el-table-column <template slot="header" slot-scope>
prop="limitKey" 限流策略
label="限流维度" <el-popover
width="400" ref="popover"
> placement="top"
<template slot-scope="scope"> title="限流策略"
<div v-html="limitRender(scope.row)"></div> width="500"
</template> trigger="hover">
</el-table-column> <div>
<el-table-column <p>窗口策略每秒处理固定数量的请求超出请求数量返回错误信息</p>
prop="limitType" <p>令牌桶策略每秒放置固定数量的令牌数每个请求进来后先去拿令牌拿到了令牌才能继续拿不到则等候令牌重新生成了再拿</p>
label="限流策略" </div>
width="120" </el-popover>
> <i v-popover:popover class="el-icon-question" style="cursor: pointer"></i>
<template slot="header" slot-scope> </template>
限流策略 <i class="el-icon-question" style="cursor: pointer" @click="onLimitTypeTipClick"></i> <template slot-scope="scope">
</template> <span v-if="scope.row.limitType === 1">窗口策略</span>
<template slot-scope="scope"> <span v-if="scope.row.limitType === 2">令牌桶策略</span>
<span v-if="scope.row.limitType === 1">窗口策略</span> </template>
<span v-if="scope.row.limitType === 2">令牌桶策略</span> </el-table-column>
</template> <el-table-column
</el-table-column> prop="info"
<el-table-column label="限流信息"
prop="info" width="250"
label="限流信息" >
width="250" <template slot-scope="scope">
> <span v-html="infoRender(scope.row)"></span>
<template slot-scope="scope"> </template>
<span v-html="infoRender(scope.row)"></span> </el-table-column>
</template> <el-table-column
</el-table-column> prop="limitStatus"
<el-table-column label="状态"
prop="limitStatus" width="80"
label="状态" >
width="80" <template slot-scope="scope">
> <span v-if="scope.row.limitStatus === 1" style="color:#67C23A">已开启</span>
<template slot-scope="scope"> <span v-if="scope.row.limitStatus === 0" style="color:#909399">已关闭</span>
<span v-if="scope.row.limitStatus === 1" style="color:#67C23A">已开启</span> </template>
<span v-if="scope.row.limitStatus === 0" style="color:#909399">已关闭</span> </el-table-column>
</template> <el-table-column
</el-table-column> prop="orderIndex"
<el-table-column label="排序"
prop="orderIndex" width="80"
label="排序"
width="80"
/>
<el-table-column
prop="remark"
label="备注"
width="150"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="gmtCreate"
label="创建时间"
width="160"
/>
<el-table-column
prop="gmtModified"
label="修改时间"
width="160"
/>
<el-table-column
label="操作"
fixed="right"
width="80"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
style="margin-top: 5px"
:current-page="searchFormData.pageIndex"
:page-size="searchFormData.pageSize"
:page-sizes="[5, 10, 20, 40]"
:total="pageInfo.total"
layout="total, sizes, prev, pager, next"
@size-change="onSizeChange"
@current-change="onPageIndexChange"
/> />
<!-- dialog --> <el-table-column
<el-dialog prop="remark"
:title="dlgTitle" label="备注"
:visible.sync="limitDialogVisible" width="150"
:close-on-click-modal="false" :show-overflow-tooltip="true"
@close="onLimitDialogClose" />
<el-table-column
prop="gmtCreate"
label="创建时间"
width="160"
/>
<el-table-column
prop="gmtModified"
label="修改时间"
width="160"
/>
<el-table-column
label="操作"
fixed="right"
width="80"
> >
<el-form <template slot-scope="scope">
ref="limitDialogForm" <el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
:model="limitDialogFormData" </template>
:rules="rulesLimit" </el-table-column>
label-width="150px" </el-table>
size="mini" <el-pagination
background
style="margin-top: 5px"
:current-page="searchFormData.pageIndex"
:page-size="searchFormData.pageSize"
:page-sizes="[5, 10, 20, 40]"
:total="pageInfo.total"
layout="total, sizes, prev, pager, next"
@size-change="onSizeChange"
@current-change="onPageIndexChange"
/>
</div>
<!-- dialog -->
<el-dialog
:title="dlgTitle"
:visible.sync="limitDialogVisible"
:close-on-click-modal="false"
@close="onLimitDialogClose"
>
<el-form
ref="limitDialogForm"
:model="limitDialogFormData"
:rules="rulesLimit"
label-width="150px"
size="mini"
>
<el-form-item label="限流维度" prop="typeKey">
<el-checkbox-group v-model="limitDialogFormData.typeKey">
<el-checkbox v-model="limitDialogFormData.typeKey[0]" :label="1" name="typeKey">路由ID</el-checkbox>
<el-checkbox v-model="limitDialogFormData.typeKey[1]" :label="2" name="typeKey">AppId</el-checkbox>
<el-checkbox v-model="limitDialogFormData.typeKey[2]" :label="3" name="typeKey">IP</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item v-show="checkTypeKey(1)" prop="routeId" label="路由ID" :rules="checkTypeKey(1) ? rulesLimit.routeId : []">
<el-select v-model="limitDialogFormData.routeId" filterable placeholder="可筛选" style="width: 300px;">
<el-option
v-for="item in routeList"
:key="item.id"
:label="item.id"
:value="item.id"
>
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.version }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item v-show="checkTypeKey(2)" prop="appKey" label="AppId" :rules="checkTypeKey(2) ? rulesLimit.appKey : []">
<el-input v-model="limitDialogFormData.appKey" placeholder="需要限流的AppId" />
</el-form-item>
<el-form-item v-show="checkTypeKey(3)" label="限流IP" prop="limitIp" :rules="checkTypeKey(3) ? rulesLimit.ip : []">
<el-input v-model="limitDialogFormData.limitIp" type="textarea" :rows="2" placeholder="多个用英文逗号隔开" />
</el-form-item>
<el-form-item label="限流策略">
<el-radio-group v-model="limitDialogFormData.limitType">
<el-radio :label="1">窗口策略</el-radio>
<el-radio :label="2">令牌桶策略</el-radio>
</el-radio-group>
<el-popover
ref="popover"
placement="top"
title="限流策略"
width="500"
trigger="hover">
<div>
<p>窗口策略每秒处理固定数量的请求超出请求数量返回错误信息</p>
<p>令牌桶策略每秒放置固定数量的令牌数每个请求进来后先去拿令牌拿到了令牌才能继续拿不到则等候令牌重新生成了再拿</p>
</div>
</el-popover>
<i v-popover:popover class="el-icon-question" style="cursor: pointer"></i>
</el-form-item>
<el-form-item label="开启状态">
<el-switch
v-model="limitDialogFormData.limitStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
> >
<el-form-item label="限流维度" prop="typeKey"> </el-switch>
<el-checkbox-group v-model="limitDialogFormData.typeKey"> </el-form-item>
<el-checkbox v-model="limitDialogFormData.typeKey[0]" :label="1" name="typeKey">路由ID</el-checkbox> <el-form-item label="排序" prop="orderIndex">
<el-checkbox v-model="limitDialogFormData.typeKey[1]" :label="2" name="typeKey">AppId</el-checkbox> <el-input-number v-model="limitDialogFormData.orderIndex" controls-position="right" :min="0" />
<el-checkbox v-model="limitDialogFormData.typeKey[2]" :label="3" name="typeKey">IP</el-checkbox> <span class="tip" style="margin-left: 10px">值小优先执行</span>
</el-checkbox-group> </el-form-item>
</el-form-item> <el-form-item v-show="isWindowType()" label="请求数" prop="execCountPerSecond" :rules="isWindowType() ? rulesLimit.execCountPerSecond : []">
<el-form-item v-show="checkTypeKey(1)" prop="routeId" label="路由ID" :rules="checkTypeKey(1) ? rulesLimit.routeId : []"> <el-input-number v-model="limitDialogFormData.durationSeconds" controls-position="right" :min="1" /> 秒可处理
<el-select v-model="limitDialogFormData.routeId" filterable placeholder="可筛选" style="width: 300px;"> <el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" /> 个请求
<el-option </el-form-item>
v-for="item in routeList" <el-form-item v-show="isTokenType()" label="令牌桶容量" prop="tokenBucketCount" :rules="isTokenType() ? rulesLimit.tokenBucketCount : []">
:key="item.id" <el-input-number v-model="limitDialogFormData.tokenBucketCount" controls-position="right" :min="1" />
:label="item.id" </el-form-item>
:value="item.id" <el-form-item label="备注" prop="remark">
> <el-input v-model="limitDialogFormData.remark" type="textarea" :rows="2" />
<span style="float: left">{{ item.name }}</span> </el-form-item>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.version }}</span> </el-form>
</el-option> <div slot="footer" class="dialog-footer">
</el-select> <el-button @click="limitDialogVisible = false"> </el-button>
</el-form-item> <el-button type="primary" @click="onLimitDialogSave"> </el-button>
<el-form-item v-show="checkTypeKey(2)" prop="appKey" label="AppId" :rules="checkTypeKey(2) ? rulesLimit.appKey : []"> </div>
<el-input v-model="limitDialogFormData.appKey" placeholder="需要限流的AppId" /> </el-dialog>
</el-form-item>
<el-form-item v-show="checkTypeKey(3)" label="限流IP" prop="limitIp" :rules="checkTypeKey(3) ? rulesLimit.ip : []">
<el-input v-model="limitDialogFormData.limitIp" type="textarea" :rows="2" placeholder="多个用英文逗号隔开" />
</el-form-item>
<el-form-item label="限流策略">
<el-radio-group v-model="limitDialogFormData.limitType">
<el-radio :label="1">窗口策略</el-radio>
<el-radio :label="2">令牌桶策略</el-radio>
</el-radio-group>
<i class="el-icon-question limit-tip" @click="onLimitTypeTipClick"></i>
</el-form-item>
<el-form-item label="开启状态">
<el-switch
v-model="limitDialogFormData.limitStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
<el-form-item label="排序" prop="orderIndex">
<el-input-number v-model="limitDialogFormData.orderIndex" controls-position="right" :min="0" />
<el-tooltip class="item" content="值小优先执行" placement="top">
<i class="el-icon-question limit-tip"></i>
</el-tooltip>
</el-form-item>
<el-form-item v-show="isWindowType()" label="请求数" prop="execCountPerSecond" :rules="isWindowType() ? rulesLimit.execCountPerSecond : []">
<el-input-number v-model="limitDialogFormData.durationSeconds" controls-position="right" :min="1" /> 秒可处理
<el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" /> 个请求
</el-form-item>
<el-form-item v-show="isTokenType()" label="令牌桶容量" prop="tokenBucketCount" :rules="isTokenType() ? rulesLimit.tokenBucketCount : []">
<el-input-number v-model="limitDialogFormData.tokenBucketCount" controls-position="right" :min="1" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="limitDialogFormData.remark" type="textarea" :rows="2" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="limitDialogVisible = false"> </el-button>
<el-button type="primary" @click="onLimitDialogSave"> </el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</div> </div>
</template> </template>
<style> <style>
@@ -242,6 +228,8 @@
export default { export default {
data() { data() {
return { return {
tabsData: [],
tabsActive: '',
serviceTextLimitSize: 20, serviceTextLimitSize: 20,
filterText: '', filterText: '',
treeData: [], treeData: [],
@@ -320,9 +308,20 @@ export default {
} }
}, },
created() { created() {
this.loadTree() this.loadTabs()
}, },
methods: { methods: {
loadTabs() {
this.post('registry.service.list', {}, function(resp) {
this.tabsData = resp.data
this.$nextTick(() => {
if (this.tabsData.length > 0) {
this.tabsActive = this.tabsData[0]
this.loadLimitData()
}
})
})
},
// 加载树 // 加载树
loadTree: function() { loadTree: function() {
this.post('registry.service.list', {}, function(resp) { this.post('registry.service.list', {}, function(resp) {
@@ -344,6 +343,15 @@ export default {
this.loadRouteList(this.serviceId) this.loadRouteList(this.serviceId)
} }
}, },
selectTab() {
this.loadLimitData()
},
loadLimitData() {
this.serviceId = this.tabsActive
this.searchFormData.serviceId = this.serviceId
this.loadTable()
this.loadRouteList(this.serviceId)
},
/** /**
* 数组转成树状结构 * 数组转成树状结构
* @param data 数据结构 [{ * @param data 数据结构 [{
@@ -506,9 +514,3 @@ export default {
} }
} }
</script> </script>
<style scoped>
.limit-tip {
cursor: pointer;
margin-left: 10px;
}
</style>

View File

@@ -54,11 +54,9 @@
> >
<template slot="header"> <template slot="header">
最大耗时(ms) 最大耗时(ms)
<i <el-tooltip effect="dark" content="耗时计算:签名验证成功后开始,微服务返回结果后结束" placement="top">
class="el-icon-question" <i class="el-icon-question" style="cursor: pointer"></i>
style="cursor: pointer" </el-tooltip>
@click="$alert('耗时计算:签名验证成功后开始,微服务返回结果后结束')"
></i>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@@ -88,11 +86,9 @@
> >
<template slot="header"> <template slot="header">
失败次数 失败次数
<i <el-tooltip effect="dark" content="只统计微服务返回的未知错误JSR-303验证错误算作成功" placement="top-end">
class="el-icon-question" <i class="el-icon-question" style="cursor: pointer"></i>
style="cursor: pointer" </el-tooltip>
@click="$alert('只统计微服务返回的未知错误JSR-303验证错误算作成功')"
></i>
</template> </template>
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link

View File

@@ -1,176 +1,121 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-container> <div v-if="tabsData.length === 0">
<el-aside style="min-height: 300px;width: 250px;"> 无服务
<el-button </div>
type="primary" <div v-else>
plain <el-tabs v-model="tabsActive" type="card" @tab-click="selectTab">
size="mini" <el-tab-pane v-for="tabName in tabsData" :key="tabName" :label="tabName" :name="tabName" />
icon="el-icon-plus" </el-tabs>
style="display: none;" <el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini" @submit.native.prevent>
@click.stop="addService" <el-form-item label="路由名称">
<el-input v-model="searchFormData.id" :clearable="true" placeholder="输入接口名或版本号" />
</el-form-item>
<el-form-item>
<el-checkbox v-model="searchFormData.permission">授权接口</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="searchFormData.needToken">需要token</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" native-type="submit" @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="pageInfo.rows"
border
highlight-current-row
style="margin-top: 10px;"
>
<el-table-column
prop="name"
label="接口名 (版本号)"
> >
新建服务 <template slot-scope="scope">
</el-button> {{ getNameVersion(scope.row) }}
<el-input </template>
v-model="filterText" </el-table-column>
prefix-icon="el-icon-search" <el-table-column
placeholder="搜索服务..." prop="roles"
style="margin-bottom:10px;margin-top:10px;" label="访问权限"
size="mini" width="150"
clearable :show-overflow-tooltip="true"
/>
<el-tree
ref="serviceTree"
:data="treeData"
:props="defaultProps"
:filter-node-method="filterNode"
:highlight-current="true"
:expand-on-click-node="false"
empty-text="无数据"
node-key="serviceId"
class="filter-tree"
default-expand-all
@node-click="onNodeClick"
> >
<span slot-scope="{ node, data }" class="custom-tree-node"> <template slot-scope="scope">
<div> <span v-if="!scope.row.permission">
<el-tooltip v-show="data.custom" content="自定义服务" class="item" effect="light" placement="left"> 公开
<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>
</span> <span v-else class="roles-content" @click="onTableAuth(scope.row)" v-html="roleRender(scope.row)"></span>
</el-tree> </template>
</el-aside> </el-table-column>
<el-main style="padding-top:0"> <el-table-column
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini"> prop="ignoreValidate"
<el-form-item label="路由名称"> label="签名校验"
<el-input v-model="searchFormData.id" :clearable="true" placeholder="输入接口名或版本号" /> width="120"
</el-form-item>
<el-form-item>
<el-checkbox v-model="searchFormData.permission">授权接口</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="searchFormData.needToken">需要token</el-checkbox>
</el-form-item>
<el-form-item>
<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"
> >
新建路由 <template slot-scope="scope">
</el-button> <span v-if="scope.row.ignoreValidate === 0">校验</span>
<el-table <span v-if="scope.row.ignoreValidate === 1" style="color:#E6A23C">不校验</span>
:data="pageInfo.rows" </template>
border </el-table-column>
highlight-current-row <el-table-column
style="margin-top: 10px;" prop="mergeResult"
label="统一格式输出"
width="120"
> >
<el-table-column <template slot-scope="scope">
prop="name" <span v-if="scope.row.mergeResult === 1"></span>
label="接口名 (版本号)" <span v-if="scope.row.mergeResult === 0" style="color:#E6A23C"></span>
width="350" </template>
> </el-table-column>
<template slot-scope="scope"> <el-table-column
{{ scope.row.name + (scope.row.version ? ' (' + scope.row.version + ')' : '') }} prop="needToken"
</template> label="需要token"
</el-table-column> width="120"
<el-table-column >
prop="roles" <template slot-scope="scope">
label="访问权限" <span v-if="scope.row.needToken === 1" style="font-weight: bold;color: #303133;"></span>
width="150" <span v-if="scope.row.needToken === 0"></span>
> </template>
<template slot-scope="scope"> </el-table-column>
<span v-html="roleRender(scope.row)"></span> <el-table-column
</template> prop="status"
</el-table-column> label="状态"
<el-table-column width="80"
prop="ignoreValidate" >
label="签名校验" <template slot-scope="scope">
width="80" <el-switch
> v-model="scope.row.status"
<template slot-scope="scope"> active-color="#13ce66"
<span v-if="scope.row.ignoreValidate === 0">校验</span> inactive-color="#ff4949"
<span v-if="scope.row.ignoreValidate === 1" style="color:#E6A23C">不校验</span> :active-value="1"
</template> :inactive-value="2"
</el-table-column> @change="onChangeStatus(scope.row)"
<el-table-column />
prop="mergeResult" </template>
label="统一格式输出" </el-table-column>
width="120" </el-table>
> <el-pagination
<template slot-scope="scope"> background
<span v-if="scope.row.mergeResult === 1"></span> style="margin-top: 5px"
<span v-if="scope.row.mergeResult === 0" style="color:#E6A23C"></span> :current-page="searchFormData.pageIndex"
</template> :page-size="searchFormData.pageSize"
</el-table-column> :page-sizes="[10, 20, 40]"
<el-table-column :total="pageInfo.total"
prop="needToken" layout="total, sizes, prev, pager, next"
label="需要token" @size-change="onSizeChange"
width="100" @current-change="onPageIndexChange"
> />
<template slot-scope="scope"> </div>
<span v-if="scope.row.needToken === 1" style="font-weight: bold;color: #303133;"></span>
<span v-if="scope.row.needToken === 0"></span>
</template>
</el-table-column>
<el-table-column
prop="status"
label="状态"
width="80"
>
<template slot-scope="scope">
<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>
</el-table-column>
<el-table-column
label="操作"
width="100"
>
<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>
<el-pagination
background
style="margin-top: 5px"
:current-page="searchFormData.pageIndex"
:page-size="searchFormData.pageSize"
:page-sizes="[10, 20, 40]"
:total="pageInfo.total"
layout="total, sizes, prev, pager, next"
@size-change="onSizeChange"
@current-change="onPageIndexChange"
/>
</el-main>
</el-container>
<!-- route dialog --> <!-- route dialog -->
<el-dialog <el-dialog
:title="routeDialogTitle" :title="routeDialogTitle"
@@ -270,11 +215,14 @@
} }
.el-input.is-disabled .el-input__inner {color: #909399;} .el-input.is-disabled .el-input__inner {color: #909399;}
.el-radio__input.is-disabled+span.el-radio__label {color: #909399;} .el-radio__input.is-disabled+span.el-radio__label {color: #909399;}
.roles-content { cursor: pointer;color: #20a0ff }
</style> </style>
<script> <script>
export default { export default {
data() { data() {
return { return {
tabsData: [],
tabsActive: '',
serviceTextLimitSize: 20, serviceTextLimitSize: 20,
filterText: '', filterText: '',
treeData: [], treeData: [],
@@ -352,10 +300,21 @@ export default {
} }
}, },
created() { created() {
this.loadTree() this.loadTabs()
this.loadRouteRole() this.loadRouteRole()
}, },
methods: { methods: {
loadTabs() {
this.post('registry.service.list', {}, function(resp) {
this.tabsData = resp.data
this.$nextTick(() => {
if (this.tabsData.length > 0) {
this.tabsActive = this.tabsData[0]
this.loadRouteData()
}
})
})
},
// 加载树 // 加载树
loadTree: function() { loadTree: function() {
this.post('registry.service.list', {}, function(resp) { this.post('registry.service.list', {}, function(resp) {
@@ -383,6 +342,14 @@ export default {
this.loadTable() this.loadTable()
} }
}, },
selectTab() {
this.loadRouteData()
},
loadRouteData() {
this.serviceId = this.tabsActive
this.searchFormData.serviceId = this.serviceId
this.loadTable()
},
/** /**
* 数组转成树状结构 * 数组转成树状结构
* @param data 数据结构 [{ * @param data 数据结构 [{
@@ -421,6 +388,9 @@ export default {
result.push(root) result.push(root)
return result return result
}, },
getNameVersion(row) {
return row.name + (row.version ? ' (' + row.version + ')' : '')
},
// table // table
loadTable: function(param) { loadTable: function(param) {
if (!this.searchFormData.serviceId) { if (!this.searchFormData.serviceId) {
@@ -469,6 +439,30 @@ export default {
}) })
}) })
}, },
// element-ui switch开关 点击按钮后,弹窗确认后再改变开关状态
// https://blog.csdn.net/Gomeer/article/details/103697593
onChangeStatus: function(row) {
const newStatus = row.status
const oldStatus = newStatus === 1 ? 2 : 1
// 先将状态改成原来的值
row.status = oldStatus
const nameVersion = this.getNameVersion(row)
const msg = oldStatus === 1 ? `确认要禁用 ${nameVersion} 吗?` : `确认要启用 ${nameVersion} 吗?`
this.confirm(msg, function(done) {
const data = {
id: row.id,
status: newStatus
}
// 'route.role.update', this.authDialogFormData
this.post('route.status.update', data, function() {
done()
row.status = newStatus
})
}, (done) => {
row.status = oldStatus
done()
})
},
onCloseRouteDialog: function() { onCloseRouteDialog: function() {
this.resetForm('routeDialogFormRef') this.resetForm('routeDialogFormRef')
}, },
@@ -495,20 +489,17 @@ export default {
}) })
}, },
roleRender: function(row) { roleRender: function(row) {
if (!row.permission) {
return '(公开)'
}
const html = [] const html = []
const roles = row.roles const roles = row.roles
for (let i = 0; i < roles.length; i++) { for (let i = 0; i < roles.length; i++) {
html.push(roles[i].description) html.push(roles[i].description)
} }
return html.length > 0 ? html.join(', ') : '<span class="x-red">未授权</span>' return html.length > 0 ? html.join(', ') : '点击授权'
}, },
onRouteDialogSave: function() { onRouteDialogSave: function() {
this.$refs.routeDialogFormRef.validate((valid) => { this.$refs.routeDialogFormRef.validate((valid) => {
if (valid) { if (valid) {
const uri = this.routeDialogFormData.id ? 'route.update' : 'route.add' const uri = this.routeDialogFormData.id ? 'route.status.update' : 'route.add'
this.routeDialogFormData.serviceId = this.serviceId this.routeDialogFormData.serviceId = this.serviceId
this.post(uri, this.routeDialogFormData, function() { this.post(uri, this.routeDialogFormData, function() {
this.routeDialogVisible = false this.routeDialogVisible = false

View File

@@ -106,7 +106,7 @@
<el-tabs v-model="tabsActiveName" type="card"> <el-tabs v-model="tabsActiveName" type="card">
<el-tab-pane label="灰度用户" name="first"> <el-tab-pane label="灰度用户" name="first">
<el-alert <el-alert
title="可以是appId或IP地址多个用英文逗号隔开" title="可以是AppId或IP地址多个用英文逗号隔开"
type="info" type="info"
:closable="false" :closable="false"
style="margin-bottom: 20px;" style="margin-bottom: 20px;"
@@ -114,13 +114,18 @@
<el-form-item prop="userKeyContent"> <el-form-item prop="userKeyContent">
<el-input <el-input
v-model="grayForm.userKeyContent" v-model="grayForm.userKeyContent"
placeholder="可以是appId或IP地址多个用英文逗号隔开" placeholder="可以是AppId或IP地址多个用英文逗号隔开"
type="textarea" type="textarea"
:rows="6" :rows="6"
/> />
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="接口配置" name="second"> <el-tab-pane label="接口配置" name="second">
<el-alert
title="灰度接口:接口名相同,版本号不同"
type="info"
:closable="false"
/>
<el-form-item> <el-form-item>
<el-button type="text" @click="addNameVersion">新增灰度接口</el-button> <el-button type="text" @click="addNameVersion">新增灰度接口</el-button>
</el-form-item> </el-form-item>
@@ -138,7 +143,8 @@
老接口 老接口
<el-select <el-select
v-model="grayRouteConfig.oldRouteId" v-model="grayRouteConfig.oldRouteId"
style="margin-right: 10px;" filterable
style="margin-right: 10px;width: 250px"
@change="onChangeOldRoute(grayRouteConfig)" @change="onChangeOldRoute(grayRouteConfig)"
> >
<el-option <el-option
@@ -159,7 +165,9 @@
灰度接口 灰度接口
<el-select <el-select
v-model="grayRouteConfig.newVersion" v-model="grayRouteConfig.newVersion"
filterable
no-data-text="无数据" no-data-text="无数据"
style="width: 250px"
> >
<el-option <el-option
v-for="routeNew in getGraySelectData(grayRouteConfig.oldRouteId)" v-for="routeNew in getGraySelectData(grayRouteConfig.oldRouteId)"

View File

@@ -1,7 +1,9 @@
package com.gitee.sop.storyweb.controller; package com.gitee.sop.storyweb.controller;
import com.gitee.sop.servercommon.annotation.Open; import com.gitee.sop.servercommon.annotation.Open;
import com.gitee.sop.servercommon.bean.OpenContext;
import com.gitee.sop.servercommon.bean.ParamNames; import com.gitee.sop.servercommon.bean.ParamNames;
import com.gitee.sop.servercommon.bean.ServiceContext;
import com.gitee.sop.storyweb.controller.param.StoryParam; import com.gitee.sop.storyweb.controller.param.StoryParam;
import com.gitee.sop.storyweb.controller.result.StoryResult; import com.gitee.sop.storyweb.controller.result.StoryResult;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@@ -24,9 +26,9 @@ public class Example1007_TokenController {
@Open(value = "story.get.token", needToken = true/* 设置true网关会校验token是否存在 */) @Open(value = "story.get.token", needToken = true/* 设置true网关会校验token是否存在 */)
@RequestMapping("token") @RequestMapping("token")
public StoryResult token(StoryParam story, HttpServletRequest request) { public StoryResult token(StoryParam story, HttpServletRequest request) {
String appAuthToken = request.getParameter(ParamNames.APP_AUTH_TOKEN_NAME); OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
StoryResult result = new StoryResult(); StoryResult result = new StoryResult();
result.setName("appAuthToken:" + appAuthToken); result.setName("appAuthToken:" + openContext.getAppAuthToken());
return result; return result;
} }
} }

View File

@@ -0,0 +1,23 @@
package com.gitee.sop.gateway.config;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class MyConfig {
@PostConstruct
public void after() {
ApiConfig.getInstance().setTokenValidator(apiParam -> {
// 获取客户端传递过来的token
String token = apiParam.fetchAccessToken();
return !StringUtils.isBlank(token);
// TODO: 校验token有效性可以从redis中读取
// 返回true表示这个token真实、有效
});
}
}

View File

@@ -102,7 +102,7 @@ public class AllInOneTest extends TestBase {
.version("1.0") .version("1.0")
.ignoreSign(true) .ignoreSign(true)
.bizContent(new BizContent().add("id", "222").add("name", "忽略222")) .bizContent(new BizContent().add("id", "222").add("name", "忽略222"))
.httpMethod(HttpTool.HTTPMethod.GET); .httpMethod(HttpTool.HTTPMethod.POST);
client.execute(requestBuilder); client.execute(requestBuilder);
} }