diff --git a/changelog.md b/changelog.md index 402cd345..a0646c53 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # changelog +## 1.15.0 + +- 优化预发布、灰度 +- 网关动态修改请求参数 +- 支持swagger-bootstrap插件 +- admin服务列表在线/离线筛选(在线靠前) +- 文档刷新逻辑 +- 测试all in one +- 修复中文乱码问题 + ## 1.14.0 - 支持预发布、灰度发布环境 diff --git a/doc/docs/_sidebar.md b/doc/docs/_sidebar.md index 2afb8a41..5704f389 100644 --- a/doc/docs/_sidebar.md +++ b/doc/docs/_sidebar.md @@ -1,33 +1,34 @@ -* [首页](/?t=1565165921486) +* [首页](/?t=1565681110217) * 开发文档 - * [快速体验](files/10010_快速体验.md?t=1565165921490) - * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1565165921508) - * [新增接口](files/10020_新增接口.md?t=1565165921508) - * [业务参数校验](files/10030_业务参数校验.md?t=1565165921509) - * [错误处理](files/10040_错误处理.md?t=1565165921509) - * [编写文档](files/10041_编写文档.md?t=1565165921509) - * [接口交互详解](files/10050_接口交互详解.md?t=1565165921509) - * [easyopen支持](files/10070_easyopen支持.md?t=1565165921509) - * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1565165921509) - * [ISV管理](files/10085_ISV管理.md?t=1565165921509) - * [自定义路由](files/10086_自定义路由.md?t=1565165921509) - * [自定义返回结果](files/10087_自定义返回结果.md?t=1565165921510) - * [自定义过滤器](files/10088_自定义过滤器.md?t=1565165921510) - * [路由授权](files/10090_路由授权.md?t=1565165921510) - * [接口限流](files/10092_接口限流.md?t=1565165921510) - * [监控日志](files/10093_监控日志.md?t=1565165921510) - * [SDK开发](files/10095_SDK开发.md?t=1565165921510) - * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1565165921510) - * [应用授权](files/10097_应用授权.md?t=1565165921510) - * [传统web开发](files/10100_传统web开发.md?t=1565165921510) - * [文件上传](files/10104_文件上传.md?t=1565165921510) - * [nacos注册中心](files/10106_nacos注册中心.md?t=1565165921510) - * [扩展其它注册中心](files/10107_扩展其它注册中心.md?t=1565165921511) - * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1565165921511) - * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1565165921511) + * [快速体验](files/10010_快速体验.md?t=1565681110219) + * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1565681110236) + * [新增接口](files/10020_新增接口.md?t=1565681110236) + * [业务参数校验](files/10030_业务参数校验.md?t=1565681110236) + * [错误处理](files/10040_错误处理.md?t=1565681110236) + * [编写文档](files/10041_编写文档.md?t=1565681110236) + * [接口交互详解](files/10050_接口交互详解.md?t=1565681110236) + * [easyopen支持](files/10070_easyopen支持.md?t=1565681110236) + * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1565681110237) + * [ISV管理](files/10085_ISV管理.md?t=1565681110237) + * [自定义路由](files/10086_自定义路由.md?t=1565681110237) + * [自定义返回结果](files/10087_自定义返回结果.md?t=1565681110237) + * [自定义过滤器](files/10088_自定义过滤器.md?t=1565681110237) + * [路由授权](files/10090_路由授权.md?t=1565681110237) + * [接口限流](files/10092_接口限流.md?t=1565681110237) + * [监控日志](files/10093_监控日志.md?t=1565681110237) + * [SDK开发](files/10095_SDK开发.md?t=1565681110237) + * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1565681110237) + * [应用授权](files/10097_应用授权.md?t=1565681110238) + * [传统web开发](files/10100_传统web开发.md?t=1565681110238) + * [文件上传](files/10104_文件上传.md?t=1565681110238) + * [nacos注册中心](files/10106_nacos注册中心.md?t=1565681110238) + * [扩展其它注册中心](files/10107_扩展其它注册中心.md?t=1565681110238) + * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1565681110238) + * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1565681110238) + * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1565681110238) * 原理分析 - * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1565165921511) - * [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1565165921511) - * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1565165921511) - * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1565165921511) - * [常见问题](files/90100_常见问题.md?t=1565165921511) + * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1565681110238) + * [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1565681110238) + * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1565681110238) + * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1565681110239) + * [常见问题](files/90100_常见问题.md?t=1565681110239) diff --git a/doc/docs/files/10010_快速体验.md b/doc/docs/files/10010_快速体验.md index 53cf1ce7..af68270a 100644 --- a/doc/docs/files/10010_快速体验.md +++ b/doc/docs/files/10010_快速体验.md @@ -13,7 +13,7 @@ - 启动微服务:打开sop-story-web下的`application-dev.properties`文件 1. 指定zookeeper地址,如果zookeeper安装在本机则不用改 2. 运行`SopStoryApplication.java` -- 找到sop-test,打开测试用例,进行接口调用测试,运行com.gitee.sop.AlipayClientPostTest.testPost() +- 找到sop-test,打开`AllInOneTest.java`进行接口调用测试 确保注册中心先启动 diff --git a/doc/docs/files/10111_动态修改请求参数.md b/doc/docs/files/10111_动态修改请求参数.md new file mode 100644 index 00000000..90f2f7f4 --- /dev/null +++ b/doc/docs/files/10111_动态修改请求参数.md @@ -0,0 +1,33 @@ +# 动态修改请求参数 + +自1.14.0开始,zuul网关支持动态修改请求参数。即在网关修改客户端传递过来的参数,然后发送到微服务端。 + +``` +客户端参数{"name": "jim"} --> zuul中修改为{"name": "Lucy"} --> 微服务端将收到{"name": "Lucy"} +``` + +使用场景:客户端请求参数经过加密,在网关解密后,再次发送明文参数给微服务端 + +- 如何使用 + +在网关springboot启动函数中添加如下代码 + +```java +public static void main(String[] args) { + ApiConfig.getInstance().setZuulParameterFormatter(requestParams -> { + // 获取biz_content + JSONObject jsonObject = requestParams.getJSONObject(ParamNames.BIZ_CONTENT_NAME); + // 修改biz_content中的值 + jsonObject.put("name", "name修改了111"); + jsonObject.put("remark", "remark修改了222"); + // 重新设置biz_content + requestParams.put(ParamNames.BIZ_CONTENT_NAME, jsonObject); + }); + SpringApplication.run(SopGatewayApplication.class, args); +} + +``` + +其中requestParams是客户端传递过来的参数,直接修改其中的值即可。 + +更多参考:com.gitee.sop.gatewaycommon.zuul.filter.PreParameterFormatterFilter.java diff --git a/sop-admin/sop-admin-server/pom.xml b/sop-admin/sop-admin-server/pom.xml index 774bb777..0f992bb7 100644 --- a/sop-admin/sop-admin-server/pom.xml +++ b/sop-admin/sop-admin-server/pom.xml @@ -30,7 +30,7 @@ com.gitee.sop sop-registry-api - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ZookeeperContext.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ZookeeperContext.java index bbe5f788..76f814b3 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ZookeeperContext.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ZookeeperContext.java @@ -288,7 +288,7 @@ public class ZookeeperContext { } /** - * 监听一个节点 + * 监听一个临时节点 * * @param path * @param listenCallback 回调 diff --git a/sop-admin/sop-admin-server/src/main/resources/public/index.html b/sop-admin/sop-admin-server/src/main/resources/public/index.html index f98e3700..86191448 100644 --- a/sop-admin/sop-admin-server/src/main/resources/public/index.html +++ b/sop-admin/sop-admin-server/src/main/resources/public/index.html @@ -1 +1 @@ -SOP Admin
\ No newline at end of file +SOP Admin
\ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9f479afe.2db777db.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9f479afe.2db777db.js deleted file mode 100644 index 160472ba..00000000 --- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9f479afe.2db777db.js +++ /dev/null @@ -1 +0,0 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9f479afe"],{"02f4":function(e,t,n){var r=n("4588"),i=n("be13");e.exports=function(e){return function(t,n){var o,a,s=String(i(t)),l=r(n),c=s.length;return l<0||l>=c?e?"":void 0:(o=s.charCodeAt(l),o<55296||o>56319||l+1===c||(a=s.charCodeAt(l+1))<56320||a>57343?e?s.charAt(l):o:e?s.slice(l,l+2):a-56320+(o-55296<<10)+65536)}}},"0390":function(e,t,n){"use strict";var r=n("02f4")(!0);e.exports=function(e,t,n){return t+(n?r(e,t).length:1)}},"0bfb":function(e,t,n){"use strict";var r=n("cb7c");e.exports=function(){var e=r(this),t="";return e.global&&(t+="g"),e.ignoreCase&&(t+="i"),e.multiline&&(t+="m"),e.unicode&&(t+="u"),e.sticky&&(t+="y"),t}},"214f":function(e,t,n){"use strict";n("b0c5");var r=n("2aba"),i=n("32e9"),o=n("79e5"),a=n("be13"),s=n("2b4c"),l=n("520a"),c=s("species"),u=!o(function(){var e=/./;return e.exec=function(){var e=[];return e.groups={a:"7"},e},"7"!=="".replace(e,"$")}),f=function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();e.exports=function(e,t,n){var d=s(e),v=!o(function(){var t={};return t[d]=function(){return 7},7!=""[e](t)}),p=v?!o(function(){var t=!1,n=/a/;return n.exec=function(){return t=!0,null},"split"===e&&(n.constructor={},n.constructor[c]=function(){return n}),n[d](""),!t}):void 0;if(!v||!p||"replace"===e&&!u||"split"===e&&!f){var g=/./[d],h=n(a,d,""[e],function(e,t,n,r,i){return t.exec===l?v&&!i?{done:!0,value:g.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}}),m=h[0],y=h[1];r(String.prototype,e,m),i(RegExp.prototype,d,2==t?function(e,t){return y.call(e,this,t)}:function(e){return y.call(e,this)})}}},"28a5":function(e,t,n){"use strict";var r=n("aae3"),i=n("cb7c"),o=n("ebd6"),a=n("0390"),s=n("9def"),l=n("5f1b"),c=n("520a"),u=n("79e5"),f=Math.min,d=[].push,v="split",p="length",g="lastIndex",h=4294967295,m=!u(function(){RegExp(h,"y")});n("214f")("split",2,function(e,t,n,u){var y;return y="c"=="abbc"[v](/(b)*/)[1]||4!="test"[v](/(?:)/,-1)[p]||2!="ab"[v](/(?:ab)*/)[p]||4!="."[v](/(.?)(.?)/)[p]||"."[v](/()()/)[p]>1||""[v](/.?/)[p]?function(e,t){var i=String(this);if(void 0===e&&0===t)return[];if(!r(e))return n.call(i,e,t);var o,a,s,l=[],u=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(e.sticky?"y":""),f=0,v=void 0===t?h:t>>>0,m=new RegExp(e.source,u+"g");while(o=c.call(m,i)){if(a=m[g],a>f&&(l.push(i.slice(f,o.index)),o[p]>1&&o.index=v))break;m[g]===o.index&&m[g]++}return f===i[p]?!s&&m.test("")||l.push(""):l.push(i.slice(f)),l[p]>v?l.slice(0,v):l}:"0"[v](void 0,0)[p]?function(e,t){return void 0===e&&0===t?[]:n.call(this,e,t)}:n,[function(n,r){var i=e(this),o=void 0==n?void 0:n[t];return void 0!==o?o.call(n,i,r):y.call(String(i),n,r)},function(e,t){var r=u(y,e,this,t,y!==n);if(r.done)return r.value;var c=i(e),d=String(this),v=o(c,RegExp),p=c.unicode,g=(c.ignoreCase?"i":"")+(c.multiline?"m":"")+(c.unicode?"u":"")+(m?"y":"g"),b=new v(m?c:"^(?:"+c.source+")",g),_=void 0===t?h:t>>>0;if(0===_)return[];if(0===d.length)return null===l(b,d)?[d]:[];var w=0,x=0,I=[];while(x1&&o.call(a[0],n,function(){for(u=1;u0&&"pre"===t.row.metadata.env?n("el-tag",{attrs:{type:"warning"}},[e._v("预发布")]):e._e(),e._v(" "),t.row.parentId>0&&"gray"===t.row.metadata.env?n("el-tag",{attrs:{type:"info"}},[e._v("灰度")]):e._e(),e._v(" "),t.row.parentId>0&&!t.row.metadata.env?n("el-tag",{attrs:{type:"success"}},[e._v("线上")]):e._e()],1):e._e()]}}])}),e._v(" "),n("el-table-column",{attrs:{prop:"metadata",label:"metadata",width:"250"},scopedSlots:e._u([{key:"default",fn:function(t){return[t.row.parentId>0?n("span",[e._v(e._s(JSON.stringify(t.row.metadata)))]):e._e()]}}])}),e._v(" "),n("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:e._u([{key:"default",fn:function(t){return[t.row.parentId>0&&"UP"===t.row.status?n("el-tag",{attrs:{type:"success"}},[e._v("正常")]):e._e(),e._v(" "),t.row.parentId>0&&"STARTING"===t.row.status?n("el-tag",{attrs:{type:"info"}},[e._v("正在启动")]):e._e(),e._v(" "),t.row.parentId>0&&"UNKNOWN"===t.row.status?n("el-tag",[e._v("未知")]):e._e(),e._v(" "),t.row.parentId>0&&("OUT_OF_SERVICE"===t.row.status||"DOWN"===t.row.status)?n("el-tag",{attrs:{type:"danger"}},[e._v("已禁用")]):e._e()]}}])}),e._v(" "),n("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),e._v(" "),n("el-table-column",{attrs:{label:"操作",width:"250"},scopedSlots:e._u([{key:"default",fn:function(t){return[0===t.row.parentId?n("div",[n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onGrayConfigUpdate(t.row)}}},[e._v("设置灰度参数")])],1):e._e(),e._v(" "),t.row.parentId>0?n("div",["UP"===t.row.status?n("span",["pre"===t.row.metadata.env?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvPreClose(t.row)}}},[e._v("结束预发布")]):e._e(),e._v(" "),"gray"===t.row.metadata.env?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvGrayClose(t.row)}}},[e._v("结束灰度")]):e._e(),e._v(" "),t.row.metadata.env?e._e():n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvPreOpen(t.row)}}},[e._v("开启预发布")]),e._v(" "),t.row.metadata.env?e._e():n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvGrayOpen(t.row)}}},[e._v("开启灰度")])],1):e._e(),e._v(" "),"UP"===t.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onDisable(t.row)}}},[e._v("禁用")]):e._e(),e._v(" "),"OUT_OF_SERVICE"===t.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnable(t.row)}}},[e._v("启用")]):e._e()],1):e._e()]}}])})],1),e._v(" "),n("el-dialog",{attrs:{title:"灰度设置",visible:e.grayDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(t){e.grayDialogVisible=t},close:function(t){return e.resetForm("grayForm")}}},[n("el-form",{ref:"grayForm",attrs:{model:e.grayForm,rules:e.grayFormRules,size:"mini"}},[n("el-form-item",{attrs:{label:"serviceId"}},[e._v("\n "+e._s(e.grayForm.serviceId)+"\n ")]),e._v(" "),n("el-tabs",{attrs:{type:"card"},model:{value:e.tabsActiveName,callback:function(t){e.tabsActiveName=t},expression:"tabsActiveName"}},[n("el-tab-pane",{attrs:{label:"灰度用户",name:"first"}},[n("el-alert",{staticStyle:{"margin-bottom":"20px"},attrs:{title:"可以是appId,或userId,多个用英文逗号隔开",type:"info",closable:!1}}),e._v(" "),n("el-form-item",{attrs:{prop:"userKeyContent"}},[n("el-input",{attrs:{placeholder:"可以是appId,或userId,多个用英文逗号隔开",type:"textarea",rows:6},model:{value:e.grayForm.userKeyContent,callback:function(t){e.$set(e.grayForm,"userKeyContent",t)},expression:"grayForm.userKeyContent"}})],1)],1),e._v(" "),n("el-tab-pane",{attrs:{label:"接口配置",name:"second"}},[n("el-form-item",[n("el-button",{attrs:{type:"text"},on:{click:e.addNameVersion}},[e._v("新增灰度接口")])],1),e._v(" "),n("table",{attrs:{cellpadding:"0",cellspacing:"0"}},e._l(e.grayForm.grayRouteConfigList,function(t,r){return n("tr",{key:t.key},[n("td",[n("el-form-item",{key:t.key,attrs:{prop:"grayRouteConfigList."+r+".oldRouteId",rules:{required:!0,message:"不能为空",trigger:["blur","change"]}}},[e._v("\n 老接口:\n "),n("el-select",{staticStyle:{"margin-right":"10px"},on:{change:function(n){return e.onChangeOldRoute(t)}},model:{value:t.oldRouteId,callback:function(n){e.$set(t,"oldRouteId",n)},expression:"grayRouteConfig.oldRouteId"}},e._l(e.routeList,function(e){return n("el-option",{key:e.id,attrs:{label:e.name+"("+e.version+")",value:e.id}})}),1)],1)],1),e._v(" "),n("td",[n("el-form-item",{key:t.key+1,attrs:{prop:"grayRouteConfigList."+r+".newVersion",rules:{required:!0,message:"不能为空",trigger:["blur","change"]}}},[e._v("\n 灰度接口:\n "),n("el-select",{attrs:{"no-data-text":"无数据"},model:{value:t.newVersion,callback:function(n){e.$set(t,"newVersion",n)},expression:"grayRouteConfig.newVersion"}},e._l(e.getGraySelectData(t.oldRouteId),function(e){return n("el-option",{key:e.id,attrs:{label:e.name+"("+e.version+")",value:e.version}})}),1)],1)],1),e._v(" "),n("td",{staticStyle:{"vertical-align":"baseline"}},[n("el-button",{directives:[{name:"show",rawName:"v-show",value:e.grayForm.grayRouteConfigList.length>1,expression:"grayForm.grayRouteConfigList.length > 1"}],attrs:{type:"text"},on:{click:function(n){return n.preventDefault(),e.removeNameVersion(t)}}},[e._v("删除")])],1)])}),0)],1)],1)],1),e._v(" "),n("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[n("el-button",{on:{click:function(t){e.grayDialogVisible=!1}}},[e._v("取 消")]),e._v(" "),n("el-button",{attrs:{type:"primary"},on:{click:e.onGrayConfigSave}},[e._v("确 定")])],1)],1)],1)},i=[],o=(n("28a5"),n("ac6a"),n("7f7f"),{data:function(){var e=/^\w+(,\w+)*$/,t=function(t,n,r){""===n?r(new Error("不能为空")):(e.test(n)||r(new Error("格式不正确")),r())};return{searchFormData:{serviceId:""},grayDialogVisible:!1,grayForm:{serviceId:"",userKeyContent:"",onlyUpdateGrayUserkey:!1,grayRouteConfigList:[]},tabsActiveName:"first",routeList:[],selectNameVersion:[],grayFormRules:{userKeyContent:[{required:!0,message:"不能为空",trigger:"blur"},{validator:t,trigger:"blur"}]},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(e){this.tableData=this.buildTreeData(e.data)})},loadRouteList:function(e){0===this.routeList.length&&this.post("route.list/1.2",{serviceId:e.toLowerCase()},function(e){this.routeList=e.data})},getGraySelectData:function(e){return this.routeList.filter(function(t){return e!==t.id&&e.indexOf(t.name)>-1})},buildTreeData:function(e){return e.forEach(function(t){var n=t.parentId;0===n||e.forEach(function(e){if(e.id===n){var r=e.children;r||(r=[]),r.push(t),e.children=r}})}),e=e.filter(function(e){return 0===e.parentId}),e},onSearchTable:function(){this.loadTable()},onDisable:function(e){this.confirm("确定要禁用【"+e.serviceId+"】吗?",function(t){this.post("service.instance.offline",e,function(){this.tip("禁用成功"),t()})})},onEnable:function(e){this.confirm("确定要启用【"+e.serviceId+"】吗?",function(t){this.post("service.instance.online",e,function(){this.tip("启用成功"),t()})})},doEnvOnline:function(e,t){this.post("service.instance.env.online",e,function(){t&&t.call(this)})},onEnvPreOpen:function(e){this.confirm("确定要开启 ".concat(e.instanceId," 预发布吗?"),function(t){this.post("service.instance.env.pre.open",e,function(){this.tip("预发布成功"),t()})})},onEnvPreClose:function(e){this.confirm("确定要结束 ".concat(e.instanceId," 预发布吗?"),function(t){this.doEnvOnline(e,function(){this.tip("操作成功"),t()})})},onEnvGrayOpen:function(e){this.confirm("确定要开启 ".concat(e.instanceId," 灰度吗?"),function(t){this.post("service.instance.env.gray.open",e,function(){this.tip("开启成功"),t()})})},onEnvGrayClose:function(e){this.confirm("确定要结束 ".concat(e.instanceId," 灰度吗?"),function(t){this.doEnvOnline(e,function(){this.tip("操作成功"),t()})})},onGrayConfigUpdate:function(e){var t=e.serviceId;this.loadRouteList(t),this.post("service.gray.config.get",{serviceId:t},function(e){this.grayDialogVisible=!0;var n=e.data;Object.assign(this.grayForm,{serviceId:t,userKeyContent:n.userKeyContent||"",grayRouteConfigList:this.createGrayRouteConfigList(n.nameVersionContent)})})},onGrayConfigSave:function(){var e=this;this.$refs.grayForm.validate(function(t){if(t){for(var n=[],r=e.grayForm.grayRouteConfigList,i=0;i0){var n=e.children.filter(function(e){return"UP"===e.status}).length;t=" (".concat(n,"/").concat(e.children.length,")")}return e.serviceId+t}}}),a=o,s=n("2877"),l=Object(s["a"])(a,r,i,!1,null,null,null);t["default"]=l.exports},b0c5:function(e,t,n){"use strict";var r=n("520a");n("5ca1")({target:"RegExp",proto:!0,forced:r!==/./.exec},{exec:r})}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9f479afe.53fe8d4e.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9f479afe.53fe8d4e.js new file mode 100644 index 00000000..913e398e --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9f479afe.53fe8d4e.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9f479afe"],{"02f4":function(e,t,n){var r=n("4588"),i=n("be13");e.exports=function(e){return function(t,n){var o,a,s=String(i(t)),l=r(n),c=s.length;return l<0||l>=c?e?"":void 0:(o=s.charCodeAt(l),o<55296||o>56319||l+1===c||(a=s.charCodeAt(l+1))<56320||a>57343?e?s.charAt(l):o:e?s.slice(l,l+2):a-56320+(o-55296<<10)+65536)}}},"0390":function(e,t,n){"use strict";var r=n("02f4")(!0);e.exports=function(e,t,n){return t+(n?r(e,t).length:1)}},"0bfb":function(e,t,n){"use strict";var r=n("cb7c");e.exports=function(){var e=r(this),t="";return e.global&&(t+="g"),e.ignoreCase&&(t+="i"),e.multiline&&(t+="m"),e.unicode&&(t+="u"),e.sticky&&(t+="y"),t}},"214f":function(e,t,n){"use strict";n("b0c5");var r=n("2aba"),i=n("32e9"),o=n("79e5"),a=n("be13"),s=n("2b4c"),l=n("520a"),c=s("species"),u=!o(function(){var e=/./;return e.exec=function(){var e=[];return e.groups={a:"7"},e},"7"!=="".replace(e,"$")}),f=function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();e.exports=function(e,t,n){var d=s(e),v=!o(function(){var t={};return t[d]=function(){return 7},7!=""[e](t)}),p=v?!o(function(){var t=!1,n=/a/;return n.exec=function(){return t=!0,null},"split"===e&&(n.constructor={},n.constructor[c]=function(){return n}),n[d](""),!t}):void 0;if(!v||!p||"replace"===e&&!u||"split"===e&&!f){var g=/./[d],m=n(a,d,""[e],function(e,t,n,r,i){return t.exec===l?v&&!i?{done:!0,value:g.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}}),y=m[0],h=m[1];r(String.prototype,e,y),i(RegExp.prototype,d,2==t?function(e,t){return h.call(e,this,t)}:function(e){return h.call(e,this)})}}},"28a5":function(e,t,n){"use strict";var r=n("aae3"),i=n("cb7c"),o=n("ebd6"),a=n("0390"),s=n("9def"),l=n("5f1b"),c=n("520a"),u=n("79e5"),f=Math.min,d=[].push,v="split",p="length",g="lastIndex",m=4294967295,y=!u(function(){RegExp(m,"y")});n("214f")("split",2,function(e,t,n,u){var h;return h="c"=="abbc"[v](/(b)*/)[1]||4!="test"[v](/(?:)/,-1)[p]||2!="ab"[v](/(?:ab)*/)[p]||4!="."[v](/(.?)(.?)/)[p]||"."[v](/()()/)[p]>1||""[v](/.?/)[p]?function(e,t){var i=String(this);if(void 0===e&&0===t)return[];if(!r(e))return n.call(i,e,t);var o,a,s,l=[],u=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(e.sticky?"y":""),f=0,v=void 0===t?m:t>>>0,y=new RegExp(e.source,u+"g");while(o=c.call(y,i)){if(a=y[g],a>f&&(l.push(i.slice(f,o.index)),o[p]>1&&o.index=v))break;y[g]===o.index&&y[g]++}return f===i[p]?!s&&y.test("")||l.push(""):l.push(i.slice(f)),l[p]>v?l.slice(0,v):l}:"0"[v](void 0,0)[p]?function(e,t){return void 0===e&&0===t?[]:n.call(this,e,t)}:n,[function(n,r){var i=e(this),o=void 0==n?void 0:n[t];return void 0!==o?o.call(n,i,r):h.call(String(i),n,r)},function(e,t){var r=u(h,e,this,t,h!==n);if(r.done)return r.value;var c=i(e),d=String(this),v=o(c,RegExp),p=c.unicode,g=(c.ignoreCase?"i":"")+(c.multiline?"m":"")+(c.unicode?"u":"")+(y?"y":"g"),b=new v(y?c:"^(?:"+c.source+")",g),_=void 0===t?m:t>>>0;if(0===_)return[];if(0===d.length)return null===l(b,d)?[d]:[];var w=0,x=0,I=[];while(x1&&o.call(a[0],n,function(){for(u=1;u0&&"pre"===t.row.metadata.env?n("el-tag",{attrs:{type:"warning"}},[e._v("预发布")]):e._e(),e._v(" "),t.row.parentId>0&&"gray"===t.row.metadata.env?n("el-tag",{attrs:{type:"info"}},[e._v("灰度")]):e._e(),e._v(" "),t.row.parentId>0&&!t.row.metadata.env?n("el-tag",{attrs:{type:"success"}},[e._v("线上")]):e._e()],1):e._e()]}}])}),e._v(" "),n("el-table-column",{attrs:{prop:"metadata",label:"metadata",width:"250"},scopedSlots:e._u([{key:"default",fn:function(t){return[t.row.parentId>0?n("span",[e._v(e._s(JSON.stringify(t.row.metadata)))]):e._e()]}}])}),e._v(" "),n("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:e._u([{key:"default",fn:function(t){return[t.row.parentId>0&&"UP"===t.row.status?n("el-tag",{attrs:{type:"success"}},[e._v("正常")]):e._e(),e._v(" "),t.row.parentId>0&&"STARTING"===t.row.status?n("el-tag",{attrs:{type:"info"}},[e._v("正在启动")]):e._e(),e._v(" "),t.row.parentId>0&&"UNKNOWN"===t.row.status?n("el-tag",[e._v("未知")]):e._e(),e._v(" "),t.row.parentId>0&&("OUT_OF_SERVICE"===t.row.status||"DOWN"===t.row.status)?n("el-tag",{attrs:{type:"danger"}},[e._v("已禁用")]):e._e()]}}])}),e._v(" "),n("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),e._v(" "),n("el-table-column",{attrs:{label:"操作",width:"250"},scopedSlots:e._u([{key:"default",fn:function(t){return[0===t.row.parentId?n("div",[n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onGrayConfigUpdate(t.row)}}},[e._v("设置灰度参数")])],1):e._e(),e._v(" "),t.row.parentId>0?n("div",["UP"===t.row.status?n("span",["pre"===t.row.metadata.env?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvPreClose(t.row)}}},[e._v("结束预发布")]):e._e(),e._v(" "),"gray"===t.row.metadata.env?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvGrayClose(t.row)}}},[e._v("结束灰度")]):e._e(),e._v(" "),t.row.metadata.env?e._e():n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvPreOpen(t.row)}}},[e._v("开启预发布")]),e._v(" "),t.row.metadata.env?e._e():n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnvGrayOpen(t.row)}}},[e._v("开启灰度")])],1):e._e(),e._v(" "),n("span",{staticStyle:{"margin-left":"10px"}},["UP"===t.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onDisable(t.row)}}},[e._v("禁用")]):e._e(),e._v(" "),"OUT_OF_SERVICE"===t.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return e.onEnable(t.row)}}},[e._v("启用")]):e._e()],1)]):e._e()]}}])})],1),e._v(" "),n("el-dialog",{attrs:{title:"灰度设置",visible:e.grayDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(t){e.grayDialogVisible=t},close:function(t){return e.resetForm("grayForm")}}},[n("el-form",{ref:"grayForm",attrs:{model:e.grayForm,rules:e.grayFormRules,size:"mini"}},[n("el-form-item",{attrs:{label:"serviceId"}},[e._v("\n "+e._s(e.grayForm.serviceId)+"\n ")]),e._v(" "),n("el-tabs",{attrs:{type:"card"},model:{value:e.tabsActiveName,callback:function(t){e.tabsActiveName=t},expression:"tabsActiveName"}},[n("el-tab-pane",{attrs:{label:"灰度用户",name:"first"}},[n("el-alert",{staticStyle:{"margin-bottom":"20px"},attrs:{title:"可以是appId,或userId,多个用英文逗号隔开",type:"info",closable:!1}}),e._v(" "),n("el-form-item",{attrs:{prop:"userKeyContent"}},[n("el-input",{attrs:{placeholder:"可以是appId,或userId,多个用英文逗号隔开",type:"textarea",rows:6},model:{value:e.grayForm.userKeyContent,callback:function(t){e.$set(e.grayForm,"userKeyContent",t)},expression:"grayForm.userKeyContent"}})],1)],1),e._v(" "),n("el-tab-pane",{attrs:{label:"接口配置",name:"second"}},[n("el-form-item",[n("el-button",{attrs:{type:"text"},on:{click:e.addNameVersion}},[e._v("新增灰度接口")])],1),e._v(" "),n("table",{attrs:{cellpadding:"0",cellspacing:"0"}},e._l(e.grayForm.grayRouteConfigList,function(t,r){return n("tr",{key:t.key},[n("td",[n("el-form-item",{key:t.key,attrs:{prop:"grayRouteConfigList."+r+".oldRouteId",rules:{required:!0,message:"不能为空",trigger:["blur","change"]}}},[e._v("\n 老接口:\n "),n("el-select",{staticStyle:{"margin-right":"10px"},on:{change:function(n){return e.onChangeOldRoute(t)}},model:{value:t.oldRouteId,callback:function(n){e.$set(t,"oldRouteId",n)},expression:"grayRouteConfig.oldRouteId"}},e._l(e.routeList,function(e){return n("el-option",{key:e.id,attrs:{label:e.name+"("+e.version+")",value:e.id}})}),1)],1)],1),e._v(" "),n("td",[n("el-form-item",{key:t.key+1,attrs:{prop:"grayRouteConfigList."+r+".newVersion",rules:{required:!0,message:"不能为空",trigger:["blur","change"]}}},[e._v("\n 灰度接口:\n "),n("el-select",{attrs:{"no-data-text":"无数据"},model:{value:t.newVersion,callback:function(n){e.$set(t,"newVersion",n)},expression:"grayRouteConfig.newVersion"}},e._l(e.getGraySelectData(t.oldRouteId),function(e){return n("el-option",{key:e.id,attrs:{label:e.name+"("+e.version+")",value:e.version}})}),1)],1)],1),e._v(" "),n("td",{staticStyle:{"vertical-align":"baseline"}},[n("el-button",{directives:[{name:"show",rawName:"v-show",value:e.grayForm.grayRouteConfigList.length>1,expression:"grayForm.grayRouteConfigList.length > 1"}],attrs:{type:"text"},on:{click:function(n){return n.preventDefault(),e.removeNameVersion(t)}}},[e._v("删除")])],1)])}),0)],1)],1)],1),e._v(" "),n("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[n("el-button",{on:{click:function(t){e.grayDialogVisible=!1}}},[e._v("取 消")]),e._v(" "),n("el-button",{attrs:{type:"primary"},on:{click:e.onGrayConfigSave}},[e._v("确 定")])],1)],1)],1)},i=[],o=(n("28a5"),n("ac6a"),n("7f7f"),{data:function(){var e=/^\w+(,\w+)*$/,t=function(t,n,r){""===n?r(new Error("不能为空")):(e.test(n)||r(new Error("格式不正确")),r())};return{searchFormData:{serviceId:""},grayDialogVisible:!1,grayForm:{serviceId:"",userKeyContent:"",onlyUpdateGrayUserkey:!1,grayRouteConfigList:[]},tabsActiveName:"first",routeList:[],selectNameVersion:[],grayFormRules:{userKeyContent:[{required:!0,message:"不能为空",trigger:"blur"},{validator:t,trigger:"blur"}]},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(e){this.tableData=this.buildTreeData(e.data)})},loadRouteList:function(e){this.post("route.list/1.2",{serviceId:e.toLowerCase()},function(e){this.routeList=e.data})},getGraySelectData:function(e){return this.routeList.filter(function(t){return e!==t.id&&e.indexOf(t.name)>-1})},buildTreeData:function(e){return e.forEach(function(t){var n=t.parentId;0===n||e.forEach(function(e){if(e.id===n){var r=e.children;r||(r=[]),r.push(t),e.children=r}})}),e=e.filter(function(e){return 0===e.parentId}),e},onSearchTable:function(){this.loadTable()},onDisable:function(e){this.confirm("确定要禁用【"+e.serviceId+"】吗?",function(t){this.post("service.instance.offline",e,function(){this.tip("禁用成功"),t()})})},onEnable:function(e){this.confirm("确定要启用【"+e.serviceId+"】吗?",function(t){this.post("service.instance.online",e,function(){this.tip("启用成功"),t()})})},doEnvOnline:function(e,t){this.post("service.instance.env.online",e,function(){t&&t.call(this)})},onEnvPreOpen:function(e){this.confirm("确定要开启 ".concat(e.instanceId," 预发布吗?"),function(t){this.post("service.instance.env.pre.open",e,function(){this.tip("预发布成功"),t()})})},onEnvPreClose:function(e){this.confirm("确定要结束 ".concat(e.instanceId," 预发布吗?"),function(t){this.doEnvOnline(e,function(){this.tip("操作成功"),t()})})},onEnvGrayOpen:function(e){this.confirm("确定要开启 ".concat(e.instanceId," 灰度吗?"),function(t){this.post("service.instance.env.gray.open",e,function(){this.tip("开启成功"),t()})})},onEnvGrayClose:function(e){this.confirm("确定要结束 ".concat(e.instanceId," 灰度吗?"),function(t){this.doEnvOnline(e,function(){this.tip("操作成功"),t()})})},onGrayConfigUpdate:function(e){var t=e.serviceId;this.loadRouteList(t),this.post("service.gray.config.get",{serviceId:t},function(e){this.grayDialogVisible=!0;var n=e.data;Object.assign(this.grayForm,{serviceId:t,userKeyContent:n.userKeyContent||"",grayRouteConfigList:this.createGrayRouteConfigList(n.nameVersionContent)})})},onGrayConfigSave:function(){var e=this;this.$refs.grayForm.validate(function(t){if(t){for(var n=[],r=e.grayForm.grayRouteConfigList,i=0;i开启预发布 开启灰度 - 禁用 - 启用 + + 禁用 + 启用 + @@ -234,11 +236,9 @@ export default { }) }, loadRouteList: function(serviceId) { - if (this.routeList.length === 0) { - this.post('route.list/1.2', { serviceId: serviceId.toLowerCase() }, function(resp) { - this.routeList = resp.data - }) - } + this.post('route.list/1.2', { serviceId: serviceId.toLowerCase() }, function(resp) { + this.routeList = resp.data + }) }, getGraySelectData: function(oldRouteId) { return this.routeList.filter(routeNew => { @@ -393,11 +393,14 @@ export default { }, renderServiceName: function(row) { let instanceCount = '' - if (row.children && row.children.length > 0) { - const onlineCount = row.children.filter(el => { + // 如果是父节点 + if (row.parentId === 0) { + const children = row.children || [] + const childCount = children.length + const onlineCount = children.filter(el => { return el.status === 'UP' }).length - instanceCount = ` (${onlineCount}/${row.children.length})` + instanceCount = `(${onlineCount}/${childCount})` } return row.serviceId + instanceCount } diff --git a/sop-common/pom.xml b/sop-common/pom.xml index 9a6dbde8..97fbd1be 100644 --- a/sop-common/pom.xml +++ b/sop-common/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.gitee.sop sop-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT pom diff --git a/sop-common/sop-gateway-common/pom.xml b/sop-common/sop-gateway-common/pom.xml index 41eff995..48962395 100644 --- a/sop-common/sop-gateway-common/pom.xml +++ b/sop-common/sop-gateway-common/pom.xml @@ -5,11 +5,11 @@ com.gitee.sop sop-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT ../pom.xml sop-gateway-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT jar sop-gateway-common diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java index f76cd5ea..ab6fb813 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java @@ -5,18 +5,18 @@ import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult; import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor; import com.gitee.sop.gatewaycommon.limit.DefaultLimitManager; import com.gitee.sop.gatewaycommon.limit.LimitManager; +import com.gitee.sop.gatewaycommon.manager.DefaultEnvGrayManager; import com.gitee.sop.gatewaycommon.manager.DefaultIPBlacklistManager; import com.gitee.sop.gatewaycommon.manager.DefaultIsvRoutePermissionManager; import com.gitee.sop.gatewaycommon.manager.DefaultLimitConfigManager; import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager; import com.gitee.sop.gatewaycommon.manager.DefaultServiceErrorManager; -import com.gitee.sop.gatewaycommon.manager.DefaultEnvGrayManager; +import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager; import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager; import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; import com.gitee.sop.gatewaycommon.manager.RouteConfigManager; import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager; -import com.gitee.sop.gatewaycommon.manager.EnvGrayManager; import com.gitee.sop.gatewaycommon.param.ParamBuilder; import com.gitee.sop.gatewaycommon.result.DataNameBuilder; import com.gitee.sop.gatewaycommon.result.DefaultDataNameBuilder; @@ -34,6 +34,7 @@ import com.gitee.sop.gatewaycommon.validate.Signer; import com.gitee.sop.gatewaycommon.validate.Validator; import com.gitee.sop.gatewaycommon.zuul.configuration.ZuulErrorController; import com.gitee.sop.gatewaycommon.zuul.param.ZuulParamBuilder; +import com.gitee.sop.gatewaycommon.zuul.param.ZuulParameterFormatter; import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor; import com.netflix.zuul.context.RequestContext; import lombok.Data; @@ -149,6 +150,8 @@ public class ApiConfig { */ private ZuulErrorController zuulErrorController = new ZuulErrorController(); + private ZuulParameterFormatter zuulParameterFormatter; + // -------- fields --------- /** diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java index 3fa8561f..bdfc8760 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultEnvGrayManager.java @@ -40,7 +40,7 @@ public class DefaultEnvGrayManager implements EnvGrayManager { if (serviceId == null || nameVersion == null) { return null; } - boolean opened = instanceIdServiceIdMap.values().contains(serviceId); + boolean opened = instanceIdServiceIdMap.containsValue(serviceId); // 没有开启灰度 if (!opened) { return null; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ParameterFormatter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ParameterFormatter.java new file mode 100644 index 00000000..8fd2caf7 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ParameterFormatter.java @@ -0,0 +1,18 @@ +package com.gitee.sop.gatewaycommon.manager; + +import java.util.Map; + +/** + * 参数格式化 + * + * @author tanghc + */ +public interface ParameterFormatter> { + + /** + * 参数格式化,即动态修改请求参数 + * + * @param requestParams 原始请求参数,在此基础上追加或修改参数 + */ + void format(T requestParams); +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java index 31463a1d..fccd5c23 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java @@ -93,6 +93,14 @@ public class ApiValidator implements Validator { * @param param 接口参数 */ protected void checkEnable(ApiParam param) { + String name = param.fetchName(); + if (name == null) { + throw ErrorEnum.ISV_MISSING_METHOD.getErrorMeta().getException(); + } + String version = param.fetchVersion(); + if (version == null) { + throw ErrorEnum.ISV_MISSING_VERSION.getErrorMeta().getException(); + } String routeId = param.fetchNameVersion(); // 检查路由是否存在 RouteRepositoryContext.checkExist(routeId, ErrorEnum.ISV_INVALID_METHOD); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java index 8f0287f6..e93c5a33 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java @@ -1,8 +1,10 @@ package com.gitee.sop.gatewaycommon.zuul.configuration; +import com.alibaba.fastjson.JSONObject; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ApiContext; import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration; +import com.gitee.sop.gatewaycommon.manager.ParameterFormatter; import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; import com.gitee.sop.gatewaycommon.param.ParamBuilder; import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter; @@ -10,6 +12,7 @@ import com.gitee.sop.gatewaycommon.zuul.filter.FormBodyWrapperFilterExt; import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter; import com.gitee.sop.gatewaycommon.zuul.filter.PreHttpServletRequestWrapperFilter; import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter; +import com.gitee.sop.gatewaycommon.zuul.filter.PreParameterFormatterFilter; import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter; import com.gitee.sop.gatewaycommon.zuul.filter.PreVersionDecisionFilter; import com.gitee.sop.gatewaycommon.zuul.filter.Servlet30WrapperFilterExt; @@ -44,6 +47,12 @@ public class BaseZuulConfiguration extends AbstractConfiguration { return ApiConfig.getInstance().getZuulParamBuilder(); } + @Bean + @ConditionalOnMissingBean + ParameterFormatter preParamFilter(){ + return ApiConfig.getInstance().getZuulParameterFormatter(); + } + /** * 路由存储 * @return @@ -104,6 +113,11 @@ public class BaseZuulConfiguration extends AbstractConfiguration { return new PreValidateFilter(); } + @Bean + PreParameterFormatterFilter preParameterFormatterFilter() { + return new PreParameterFormatterFilter(); + } + /** * 开启限流 */ @@ -120,6 +134,8 @@ public class BaseZuulConfiguration extends AbstractConfiguration { return new PreVersionDecisionFilter(); } + + /** * 错误处理扩展 */ diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java index 29b97e4f..2147c1f3 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java @@ -23,11 +23,14 @@ public abstract class BaseZuulFilter extends ZuulFilter { /** 签名验证过滤 */ public static final int PRE_VALIDATE_FILTER_ORDER = -1000; + /** 参数格式化过滤器 */ + public static final int PRE_PARAMETER_FORMATTER_FILTER_ORDER = PRE_VALIDATE_FILTER_ORDER + 1; + /** 权限验证过滤 */ - public static final int PRE_ROUTE_PERMISSION_FILTER_ORDER = PRE_VALIDATE_FILTER_ORDER + 1; + public static final int PRE_ROUTE_PERMISSION_FILTER_ORDER = PRE_VALIDATE_FILTER_ORDER + 100; /** 限流过滤 */ - public static final int PRE_LIMIT_FILTER_ORDER = PRE_ROUTE_PERMISSION_FILTER_ORDER + 1; + public static final int PRE_LIMIT_FILTER_ORDER = PRE_ROUTE_PERMISSION_FILTER_ORDER + 100; private Integer filterOrder; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreParameterFormatterFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreParameterFormatterFilter.java new file mode 100644 index 00000000..9f88470c --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreParameterFormatterFilter.java @@ -0,0 +1,42 @@ +package com.gitee.sop.gatewaycommon.zuul.filter; + +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.param.ParamNames; +import com.gitee.sop.gatewaycommon.zuul.ZuulContext; +import com.gitee.sop.gatewaycommon.zuul.param.ZuulParameterFormatter; +import com.gitee.sop.gatewaycommon.zuul.param.ZuulParameterUtil; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * 参数格式化过滤器,动态修改参数,此过滤器放在前面校验后面 + * + * @author tanghc + */ +public class PreParameterFormatterFilter extends BaseZuulFilter { + + @Autowired(required = false) + private ZuulParameterFormatter zuulParameterFormatter; + + @Override + protected FilterType getFilterType() { + return FilterType.PRE; + } + + @Override + protected int getFilterOrder() { + return PRE_PARAMETER_FORMATTER_FILTER_ORDER; + } + + @Override + protected Object doRun(RequestContext requestContext) throws ZuulException { + ApiParam apiParam = ZuulContext.getApiParam(); + // 校验成功后进行参数转换 + if (zuulParameterFormatter != null) { + ZuulParameterUtil.format(apiParam, zuulParameterFormatter::format); + requestContext.addZuulRequestHeader(ParamNames.HEADER_VERSION_NAME, apiParam.fetchVersion()); + } + return null; + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterFormatter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterFormatter.java new file mode 100644 index 00000000..23885acd --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterFormatter.java @@ -0,0 +1,10 @@ +package com.gitee.sop.gatewaycommon.zuul.param; + +import com.alibaba.fastjson.JSONObject; +import com.gitee.sop.gatewaycommon.manager.ParameterFormatter; + +/** + * @author tanghc + */ +public interface ZuulParameterFormatter extends ParameterFormatter { +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterUtil.java new file mode 100644 index 00000000..d8e29c8d --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParameterUtil.java @@ -0,0 +1,183 @@ +package com.gitee.sop.gatewaycommon.zuul.param; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.util.RequestUtil; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.http.HttpServletRequestWrapper; +import com.netflix.zuul.http.ServletInputStreamWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.springframework.cloud.netflix.zuul.util.RequestContentDataExtractor; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.util.MultiValueMap; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * zuul参数工具 + * @author tanghc + */ +@Slf4j +public class ZuulParameterUtil { + + private static FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter(); + + /** + * 格式化参数 + * @param apiParam 请求的参数 + * @param consumer 修改参数 + * @param 参数类型 + */ + public static > void format(T apiParam, Consumer consumer) { + RequestContext requestContext = RequestContext.getCurrentContext(); + consumer.accept(apiParam); + HttpServletRequest request = requestContext.getRequest(); + String contentType = request.getContentType(); + if (StringUtils.containsIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)) { + String json = (apiParam instanceof JSONObject) ? + ((JSONObject) apiParam).toJSONString() + : JSON.toJSONString(apiParam); + byte[] bytes = json.getBytes(StandardCharsets.UTF_8); + requestContext.setRequest(new ChangeParamsHttpServletRequestWrapper(request, bytes)); + } else if(StringUtils.containsIgnoreCase(contentType, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { + List list = new ArrayList<>(apiParam.size()); + try { + for (Map.Entry entry : apiParam.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Collection) { + Collection collection = (Collection) value; + for (Object el : collection) { + list.add(key + "=" + URLEncoder.encode(String.valueOf(el), SopConstants.UTF8)); + } + } else { + list.add(key + "=" + URLEncoder.encode(String.valueOf(value), SopConstants.UTF8)); + } + } + } catch (UnsupportedEncodingException e) { + log.error("字符集不支持", e); + } + String paramsStr = StringUtils.join(list, "&"); + byte[] data = paramsStr.getBytes(StandardCharsets.UTF_8); + requestContext.setRequest(new ChangeParamsHttpServletRequestWrapper(request, data)); + } else if(RequestUtil.isMultipart(request)) { + FormHttpOutputMessage outputMessage = new FormHttpOutputMessage(); + try { + // 转成MultipartRequest + if (!(request instanceof MultipartHttpServletRequest)) { + CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(request.getServletContext()); + request = commonsMultipartResolver.resolveMultipart(request); + } + // 重写新的值 + MultiValueMap builder = RequestContentDataExtractor.extract(request); + for (Map.Entry entry : apiParam.entrySet()) { + Object value = entry.getValue(); + if (value instanceof List) { + builder.put(entry.getKey(), (List)value); + } else { + builder.put(entry.getKey(), Collections.singletonList(String.valueOf(value))); + } + } + MediaType mediaType = MediaType.valueOf(request.getContentType()); + // 将字段以及上传文件重写写入到流中 + formHttpMessageConverter.write(builder, mediaType, outputMessage); + // 获取新的上传文件流 + byte[] data = outputMessage.getInput(); + + requestContext.setRequest(new ChangeParamsHttpServletRequestWrapper(request, data)); + // 必须要重新指定content-type,因为此时的boundary已经发生改变 + requestContext.getZuulRequestHeaders().put("content-type", outputMessage.getHeaders().getContentType().toString()); + } catch (Exception e) { + log.error("修改上传文件请求参数失败, apiParam:{}", apiParam, e); + } + } else if(HttpMethod.GET.name().equalsIgnoreCase(request.getMethod())) { + Map> newParams = new HashMap<>(); + for (Map.Entry entry : apiParam.entrySet()) { + Object value = entry.getValue(); + if (value instanceof List) { + List valueList = ((List) value).stream().map(String::valueOf).collect(Collectors.toList()); + newParams.put(entry.getKey(), valueList); + } else { + newParams.put(entry.getKey(), Collections.singletonList(String.valueOf(value))); + } + } + requestContext.setRequestQueryParams(newParams); + } + } + + + private static class FormHttpOutputMessage implements HttpOutputMessage { + + private HttpHeaders headers = new HttpHeaders(); + private ByteArrayOutputStream output = new ByteArrayOutputStream(); + + @Override + public HttpHeaders getHeaders() { + return this.headers; + } + + @Override + public OutputStream getBody() throws IOException { + return this.output; + } + + public byte[] getInput() throws IOException { + this.output.flush(); + return this.output.toByteArray(); + } + + } + + private static class ChangeParamsHttpServletRequestWrapper extends HttpServletRequestWrapper { + private byte[] data; + + public ChangeParamsHttpServletRequestWrapper(HttpServletRequest request, byte[] data) { + super(request); + this.data = data; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return new ServletInputStreamWrapper(data); + } + + @Override + public byte[] getContentData() { + return data; + } + + @Override + public int getContentLength() { + return data.length; + } + + @Override + public long getContentLengthLong() { + return data.length; + } + } + +} diff --git a/sop-common/sop-registry-api/pom.xml b/sop-common/sop-registry-api/pom.xml index 4e98940b..a3179a33 100644 --- a/sop-common/sop-registry-api/pom.xml +++ b/sop-common/sop-registry-api/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.gitee.sop sop-registry-api - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT UTF-8 diff --git a/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/bean/ServiceInfo.java b/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/bean/ServiceInfo.java index 2d19dd58..45417810 100644 --- a/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/bean/ServiceInfo.java +++ b/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/bean/ServiceInfo.java @@ -1,24 +1,16 @@ package com.gitee.sop.registryapi.bean; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; -import java.util.Collections; import java.util.List; /** * @author tanghc */ -@Getter -@Setter +@Data public class ServiceInfo { /** 服务名称 */ private String serviceId; /** 实例列表 */ - private List instances = Collections.emptyList(); - - @Override - public String toString() { - return "服务名称: " + serviceId + ", 实例数:" + instances.size(); - } + private List instances; } diff --git a/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/service/impl/RegistryServiceNacos.java b/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/service/impl/RegistryServiceNacos.java index 6f7746d2..00b31d85 100644 --- a/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/service/impl/RegistryServiceNacos.java +++ b/sop-common/sop-registry-api/src/main/java/com/gitee/sop/registryapi/service/impl/RegistryServiceNacos.java @@ -18,6 +18,8 @@ import org.springframework.util.CollectionUtils; import javax.annotation.PostConstruct; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,7 +53,9 @@ public class RegistryServiceNacos implements RegistryService { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setServiceId(serviceName); List instanceList = namingService.getAllInstances(serviceName); - if (!CollectionUtils.isEmpty(instanceList)) { + if (CollectionUtils.isEmpty(instanceList)) { + serviceInfo.setInstances(Collections.emptyList()); + } else { serviceInfo.setInstances(new ArrayList<>(instanceList.size())); for (Instance instance : instanceList) { ServiceInstance serviceInstance = new ServiceInstance(); @@ -68,6 +72,7 @@ public class RegistryServiceNacos implements RegistryService { } serviceInfoList.add(serviceInfo); } + serviceInfoList.sort(Comparator.comparingInt(o -> o.getInstances().size())); return serviceInfoList; } diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml index 8b7c7da6..bfaec2de 100644 --- a/sop-common/sop-service-common/pom.xml +++ b/sop-common/sop-service-common/pom.xml @@ -6,11 +6,11 @@ com.gitee.sop sop-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT ../pom.xml sop-service-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT jar sop-service-common diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConstants.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConstants.java index 9b596b12..7d0fe3dd 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConstants.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConstants.java @@ -8,4 +8,9 @@ public class ServiceConstants { * zookeeper存放接口路由信息的根目录 */ public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route"; + + /** + * 服务临时节点 + */ + public static final String SOP_SERVICE_TEMP_PATH = "/com.gitee.sop.service.tmp"; } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ZookeeperTool.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ZookeeperTool.java index dbf4615b..8130d7f8 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ZookeeperTool.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ZookeeperTool.java @@ -9,6 +9,7 @@ import org.apache.commons.lang.math.NumberUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.zookeeper.CreateMode; import org.springframework.core.env.Environment; import java.io.Closeable; @@ -94,6 +95,23 @@ public class ZookeeperTool implements Closeable { .forPath(path, data.getBytes()); } + /** + * 创建临时序列节点 + * @param path 节点路径 + * @param data 数据 + * @return 返回节点路径 + * @throws Exception + */ + public String createOrUpdateEphemeralSequentialPath(String path, String data) throws Exception { + return getClient().create() + // 如果节点存在则Curator将会使用给出的数据设置这个节点的值 + .orSetData() + // 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点 + .creatingParentContainersIfNeeded() + .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) + .forPath(path, data.getBytes()); + } + /** * 新建或保存节点 * diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java index 2dbd418e..6648e81e 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java @@ -14,11 +14,15 @@ import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.annotation.PostConstruct; +import java.nio.charset.StandardCharsets; +import java.util.List; /** * @author tanghc @@ -44,6 +48,12 @@ public class BaseServiceConfiguration extends WebMvcConfigurationSupport super.addInterceptors(registry); } + @Override + protected void configureMessageConverters(List> converters) { + // 解决controller返回字符串中文乱码问题 + converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); + } + /** * 自定义Mapping,详见@ApiMapping * @return 返回RequestMappingHandlerMapping diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java index 6f71ec7a..10ff3503 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java @@ -10,7 +10,6 @@ import com.gitee.sop.servercommon.route.ServiceRouteInfo; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; import org.springframework.beans.BeanUtils; import org.springframework.core.env.Environment; import org.springframework.util.DigestUtils; @@ -81,6 +80,7 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { String nodeData = JSON.toJSONString(serviceRouteInfo); log.info("serviceId:{}, zookeeper保存路径:{}", serviceId, savePath); this.zookeeperTool.createPath(savePath, nodeData); + this.zookeeperTool.createPath(serviceRouteInfo.getZookeeperTempServiceIdPath(), ""); } catch (Exception e) { throw new IllegalStateException("zookeeper操作失败"); } @@ -91,8 +91,11 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { try { ServiceRouteInfo serviceRouteInfo = this.buildServiceGatewayInfo(serviceApiInfo); this.uploadServiceRouteInfoToZookeeper(serviceRouteInfo); - } finally { - IOUtils.closeQuietly(zookeeperTool); + // 同时上传一个临时节点 + String tempPath = serviceRouteInfo.getZookeeperTempServiceIdChildPath(); + this.zookeeperTool.createOrUpdateEphemeralSequentialPath(tempPath, JSON.toJSONString(serviceRouteInfo)); + } catch (Exception e) { + log.error("上传一个临时节点失败, serviceApiInfo:{}", serviceApiInfo, e); } } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java index 80a3bd65..31ca519f 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ServiceParamValidator.java @@ -22,11 +22,10 @@ public class ServiceParamValidator implements ParamValidator { public static final String COMMA = ","; private static Object[] EMPTY_OBJ_ARRAY = {}; - private static ValidatorFactory factory; private static javax.validation.Validator validator; static { - factory = Validation.buildDefaultValidatorFactory(); + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java index 78c920a9..5041000d 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java @@ -13,6 +13,7 @@ import java.util.List; @Data public class ServiceRouteInfo { private static final String SOP_SERVICE_ROUTE_PATH = ServiceConstants.SOP_SERVICE_ROUTE_PATH; + private static final String SOP_SERVICE_TEMP_PATH = ServiceConstants.SOP_SERVICE_TEMP_PATH; /** 服务名称,对应spring.application.name */ private String serviceId; @@ -39,4 +40,22 @@ public class ServiceRouteInfo { public String getZookeeperPath() { return SOP_SERVICE_ROUTE_PATH + '/' + serviceId; } + + /** + * 返回zookeeper路径 + * @return 返回zookeeper路径 + */ + @JSONField(serialize = false) + public String getZookeeperTempServiceIdPath() { + return SOP_SERVICE_TEMP_PATH + '/' + serviceId; + } + + /** + * 返回zookeeper路径 + * @return 返回zookeeper路径 + */ + @JSONField(serialize = false) + public String getZookeeperTempServiceIdChildPath() { + return getZookeeperTempServiceIdPath() + "/" + serviceId; + } } \ No newline at end of file diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java index a5e2bd80..023c9c2a 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java @@ -29,6 +29,10 @@ public abstract class SwaggerSupport { @Bean public Docket createRestApi() { + return getDocket(); + } + + protected Docket getDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java index 61c1d527..a99813cd 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/OpenUtil.java @@ -67,12 +67,8 @@ public class OpenUtil { for (Map.Entry entry : entrySet) { String name = entry.getKey(); String[] values = entry.getValue(); - if (values.length == 1) { + if (values.length >= 1) { retMap.put(name, values[0]); - } else if (values.length > 1) { - retMap.put(name, values); - } else { - retMap.put(name, ""); } } diff --git a/sop-example/sop-auth/pom.xml b/sop-example/sop-auth/pom.xml index af511639..0d8c980b 100644 --- a/sop-example/sop-auth/pom.xml +++ b/sop-example/sop-auth/pom.xml @@ -26,7 +26,7 @@ com.gitee.sop sop-service-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT org.springframework.cloud diff --git a/sop-example/sop-book/sop-book-web/pom.xml b/sop-example/sop-book/sop-book-web/pom.xml index d7391a35..695145b3 100644 --- a/sop-example/sop-book/sop-book-web/pom.xml +++ b/sop-example/sop-book/sop-book-web/pom.xml @@ -24,7 +24,7 @@ com.gitee.sop sop-service-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT com.gitee.sop diff --git a/sop-example/sop-easyopen/pom.xml b/sop-example/sop-easyopen/pom.xml index 77630c70..45f84392 100644 --- a/sop-example/sop-easyopen/pom.xml +++ b/sop-example/sop-easyopen/pom.xml @@ -29,7 +29,7 @@ com.gitee.sop sop-service-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT org.springframework.cloud diff --git a/sop-example/sop-springmvc/pom.xml b/sop-example/sop-springmvc/pom.xml index 88f06728..82467950 100644 --- a/sop-example/sop-springmvc/pom.xml +++ b/sop-example/sop-springmvc/pom.xml @@ -20,7 +20,7 @@ com.gitee.sop sop-service-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT diff --git a/sop-example/sop-story/sop-story-web/pom.xml b/sop-example/sop-story/sop-story-web/pom.xml index e326ea9f..99177722 100644 --- a/sop-example/sop-story/sop-story-web/pom.xml +++ b/sop-example/sop-story/sop-story-web/pom.xml @@ -24,7 +24,7 @@ com.gitee.sop sop-service-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT com.gitee.sop @@ -79,22 +79,11 @@ io.springfox springfox-swagger2 2.9.2 - - - io.swagger - swagger-models - - - io.swagger - swagger-models - 1.5.21 - - - io.springfox - springfox-swagger-ui - 2.9.2 + com.github.xiaoymin + swagger-bootstrap-ui + 1.9.5 diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java index dd9a5f12..eb0c299f 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java @@ -22,15 +22,16 @@ public class OpenServiceConfig extends AlipayServiceConfiguration { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); - registry.addResourceHandler("swagger-ui.html") - .addResourceLocations("classpath:/META-INF/resources/"); - registry.addResourceHandler("/webjars/**") - .addResourceLocations("classpath:/META-INF/resources/webjars/"); + // 支持swagger-bootstrap-ui首页 + registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); + // 支持默认swagger + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); } /** - * 开启文档 + * 开启文档,本地微服务文档地址:http://localhost:2222/doc.html * http://ip:port/v2/api-docs */ @Configuration @@ -43,9 +44,10 @@ public class OpenServiceConfig extends AlipayServiceConfiguration { @Override protected boolean swaggerAccessProtected() { - return true; + return false; } } + } /** diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java index 563c1721..b5e1ea49 100644 --- a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java +++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java @@ -1,5 +1,6 @@ package com.gitee.sop.storyweb.controller; +import com.alibaba.fastjson.JSON; import com.gitee.sop.servercommon.annotation.ApiAbility; import com.gitee.sop.servercommon.annotation.ApiMapping; import com.gitee.sop.servercommon.bean.OpenContext; @@ -127,13 +128,39 @@ public class AlipayController { // http://localhost:2222/alipay.story.get/ @ApiMapping(value = "alipay.story.get") - public Story getStory() { + public Story getStory(Story param) { Story story = new Story(); story.setId(1); - story.setName("海底小纵队(alipay.story.get1.0)"); + story.setName("海底小纵队(alipay.story.get1.0), param:" + param); return story; } + /** + * + * @param param 对应biz_content中的内容,并自动JSR-303校验 + * @return + */ + @ApiMapping(value = "alipay.story.get", version = "1.2") + public Story getStory11(Story param) { + Story story2 = new Story(); + story2.setId(1); + story2.setName("海底小纵队(alipay.story.get1.2), param:" + param); + return story2; + } + + /** + * 验证字符串乱码问题 + * @param param + * @return + */ + @ApiMapping(value = "story.string.get", version = "1.0") + public String string(Story param) { + Story story2 = new Story(); + story2.setId(1); + story2.setName("海底小纵队"); + return JSON.toJSONString(story2); + } + /** * 参数绑定 * @param story 对应biz_content中的内容,并自动JSR-303校验 @@ -153,19 +180,6 @@ public class AlipayController { return storyVO; } - /** - * - * @param story 对应biz_content中的内容,并自动JSR-303校验 - * @return - */ - @ApiMapping(value = "alipay.story.get", version = "1.2") - public Story getStory11(Story story) { - Story story2 = new Story(); - story2.setId(1); - story2.setName("海底小纵队(alipay.story.get1.2)"); - return story2; - } - /** * 演示文档表格树 * @param story diff --git a/sop-gateway/pom.xml b/sop-gateway/pom.xml index 73837cc2..4a4dbeb8 100644 --- a/sop-gateway/pom.xml +++ b/sop-gateway/pom.xml @@ -23,7 +23,7 @@ com.gitee.sop sop-gateway-common - 1.14.0-SNAPSHOT + 1.15.0-SNAPSHOT