From 12fc71dd57c7fc6cd42c65c06b2bd454f9d91f90 Mon Sep 17 00:00:00 2001 From: tanghc Date: Thu, 6 Jun 2019 15:44:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=9B=91=E6=8E=A7=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 4 + doc/docs/_sidebar.md | 51 ++-- doc/docs/files/10093_监控日志.md | 44 ++++ doc/docs/files/images/10093_1.png | Bin 0 -> 29102 bytes doc/docs/files/images/10093_2.png | Bin 0 -> 42180 bytes sop-1.10.0.sql | 13 + .../adminserver/api/service/LimitNewApi.java | 14 +- .../sop/adminserver/api/service/LogApi.java | 169 +++++++++++++ .../sop/adminserver/api/service/RouteApi.java | 2 +- .../adminserver/api/service/ServiceApi.java | 2 +- .../param/LogMonitorInstanceAddParam.java | 19 ++ .../service/result/LogMonitorInstanceVO.java | 22 ++ .../sop/adminserver/common/QueryUtil.java | 28 +++ .../sop/adminserver/config/WebConfig.java | 7 +- .../sop/adminserver/entity/ConfigCommon.java | 45 ++++ .../mapper/ConfigCommonMapper.java | 12 + .../src/main/resources/application-dev.yml | 4 + .../src/main/resources/public/index.html | 2 +- .../js/{app.bc31495f.js => app.36a3cb86.js} | 2 +- .../static/js/chunk-2d0aa95b.447435b3.js | 1 + .../static/js/chunk-2d0aa95b.5ba26eff.js | 1 - ...0cbb7297.js => chunk-2d22c2e3.46f37153.js} | 2 +- .../static/js/chunk-4a59cbe4.3309de97.js | 1 + sop-admin/sop-admin-vue/src/router/index.js | 6 + sop-admin/sop-admin-vue/src/utils/global.js | 7 +- .../src/views/service/list/index.vue | 2 +- .../src/views/service/log/index.vue | 234 ++++++++++++++++++ .../src/views/service/route/index.vue | 8 +- .../sop/gatewaycommon/bean/ApiConfig.java | 12 + .../gatewaycommon/bean/ErrorDefinition.java | 15 ++ .../sop/gatewaycommon/bean/ErrorEntity.java | 32 +++ .../sop/gatewaycommon/bean/SopConstants.java | 4 +- .../gateway/result/GatewayResultExecutor.java | 15 +- .../manager/DefaultServiceErrorManager.java | 51 ++++ .../manager/ServiceErrorManager.java | 29 +++ .../sop/gatewaycommon/param/ApiParam.java | 8 + .../result/BaseExecutorAdapter.java | 99 ++++++-- .../sop/gatewaycommon/result/JsonResult.java | 13 + .../zuul/result/ZuulResultExecutor.java | 38 ++- .../DefaultGlobalExceptionHandler.java | 37 +++ .../result/DefaultServiceResultBuilder.java | 6 - .../gitee/sop/gateway/config/ZuulConfig.java | 3 + .../controller/ErrorLogController.java | 68 +++++ .../{ => controller}/RedirectController.java | 2 +- .../src/main/resources/application-dev.yml | 2 + .../websiteserver/config/WebsiteConfig.java | 2 +- sop.sql | 12 + 47 files changed, 1055 insertions(+), 95 deletions(-) create mode 100644 doc/docs/files/10093_监控日志.md create mode 100644 doc/docs/files/images/10093_1.png create mode 100644 doc/docs/files/images/10093_2.png create mode 100644 sop-1.10.0.sql create mode 100644 sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java create mode 100644 sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LogMonitorInstanceAddParam.java create mode 100644 sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LogMonitorInstanceVO.java create mode 100644 sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/QueryUtil.java create mode 100644 sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigCommon.java create mode 100644 sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/ConfigCommonMapper.java rename sop-admin/sop-admin-server/src/main/resources/public/static/js/{app.bc31495f.js => app.36a3cb86.js} (51%) create mode 100644 sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.447435b3.js delete mode 100644 sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.5ba26eff.js rename sop-admin/sop-admin-server/src/main/resources/public/static/js/{chunk-2d22c2e3.0cbb7297.js => chunk-2d22c2e3.46f37153.js} (79%) create mode 100644 sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-4a59cbe4.3309de97.js create mode 100644 sop-admin/sop-admin-vue/src/views/service/log/index.vue create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java create mode 100644 sop-gateway/src/main/java/com/gitee/sop/gateway/controller/ErrorLogController.java rename sop-gateway/src/main/java/com/gitee/sop/gateway/{ => controller}/RedirectController.java (95%) diff --git a/changelog.md b/changelog.md index ef98b6e5..ca5e3fe7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # changelog +## 1.10.0 + +- 新增监控日志 + ## 1.9.0 - 改造限流 diff --git a/doc/docs/_sidebar.md b/doc/docs/_sidebar.md index 66f4775d..bfce503d 100644 --- a/doc/docs/_sidebar.md +++ b/doc/docs/_sidebar.md @@ -1,27 +1,28 @@ -* [首页](/?t=1559633402239) +* [首页](/?t=1559803778913) * 开发文档 - * [快速体验](files/10010_快速体验.md?t=1559633402241) - * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1559633402260) - * [新增接口](files/10020_新增接口.md?t=1559633402260) - * [业务参数校验](files/10030_业务参数校验.md?t=1559633402260) - * [错误处理](files/10040_错误处理.md?t=1559633402260) - * [编写文档](files/10041_编写文档.md?t=1559633402260) - * [接口交互详解](files/10050_接口交互详解.md?t=1559633402261) - * [easyopen支持](files/10070_easyopen支持.md?t=1559633402261) - * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1559633402261) - * [ISV管理](files/10085_ISV管理.md?t=1559633402261) - * [路由授权](files/10090_路由授权.md?t=1559633402261) - * [接口限流](files/10092_接口限流.md?t=1559633402261) - * [SDK开发](files/10095_SDK开发.md?t=1559633402261) - * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1559633402261) - * [应用授权](files/10097_应用授权.md?t=1559633402261) - * [更改数据节点名称](files/10099_更改数据节点名称.md?t=1559633402262) - * [对接前端](files/10100_对接前端.md?t=1559633402262) - * [自定义过滤器](files/10102_自定义过滤器.md?t=1559633402262) - * [文件上传](files/10104_文件上传.md?t=1559633402262) + * [快速体验](files/10010_快速体验.md?t=1559803778920) + * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1559803778940) + * [新增接口](files/10020_新增接口.md?t=1559803778940) + * [业务参数校验](files/10030_业务参数校验.md?t=1559803778940) + * [错误处理](files/10040_错误处理.md?t=1559803778940) + * [编写文档](files/10041_编写文档.md?t=1559803778941) + * [接口交互详解](files/10050_接口交互详解.md?t=1559803778941) + * [easyopen支持](files/10070_easyopen支持.md?t=1559803778941) + * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1559803778941) + * [ISV管理](files/10085_ISV管理.md?t=1559803778941) + * [路由授权](files/10090_路由授权.md?t=1559803778941) + * [接口限流](files/10092_接口限流.md?t=1559803778941) + * [监控日志](files/10093_监控日志.md?t=1559803778941) + * [SDK开发](files/10095_SDK开发.md?t=1559803778941) + * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1559803778942) + * [应用授权](files/10097_应用授权.md?t=1559803778942) + * [更改数据节点名称](files/10099_更改数据节点名称.md?t=1559803778942) + * [对接前端](files/10100_对接前端.md?t=1559803778942) + * [自定义过滤器](files/10102_自定义过滤器.md?t=1559803778942) + * [文件上传](files/10104_文件上传.md?t=1559803778942) * 原理分析 - * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1559633402262) - * [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1559633402262) - * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1559633402262) - * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1559633402262) - * [常见问题](files/90100_常见问题.md?t=1559633402262) + * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1559803778942) + * [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1559803778942) + * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1559803778942) + * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1559803778942) + * [常见问题](files/90100_常见问题.md?t=1559803778942) diff --git a/doc/docs/files/10093_监控日志.md b/doc/docs/files/10093_监控日志.md new file mode 100644 index 00000000..47941706 --- /dev/null +++ b/doc/docs/files/10093_监控日志.md @@ -0,0 +1,44 @@ +# 监控日志 + +1.10.0开始sop-admin提供了简单的监控日志查询,方便在线排查问题。 + +- 错误日志统一在网关负责收集 +- 只收集未知类型的错误日志,开发人员主动throw的异常不收集 +- sop-admin通过网关提供的restful接口获取日志内容,然后在后台展示 +- 收集的日志存放在内存中,重启网关日志会消失 +- 只会收集20条不同的日志内容,相同内容会count+1。可设置`ApiConfig.storeErrorCapacity`属性扩大容量,默认容量20 + +第一次使用需要添加网关服务器实例,前往:`服务管理--监控日志--添加监控服务器` + +## 永久保存日志 + +默认收集的日志存放在内存中,重启网关日志会消失(见:`DefaultServiceErrorManager.java`)。如果要永久保存日志内容,需要自己实现`ServiceErrorManager`接口 + +```java +public class MyServiceErrorManager implements ServiceErrorManager { + ... +} +``` + +然后在ApiConfig中配置 + +```java +@Configuration +public class ZuulConfig extends AlipayZuulConfiguration { + + static { + ... + ApiConfig.getInstance().setServiceErrorManager(new MyServiceErrorManager()); + // 日志收集容量,默认20。只会收集20条不同内容的日志 + ApiConfig.getInstance().setStoreErrorCapacity(20); + ... + } + +} +``` + +- 后台预览 + +![监控日志](images/10093_1.png "10093_1.png") + +![监控日志](images/10093_2.png "10093_2.png") \ No newline at end of file diff --git a/doc/docs/files/images/10093_1.png b/doc/docs/files/images/10093_1.png new file mode 100644 index 0000000000000000000000000000000000000000..74c70779bdfedc623333d4001f46c3ae9ce2d596 GIT binary patch literal 29102 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 YrxX+GhPDP#i}!TMNGeK{ix~#}FChgv-2eap literal 0 HcmV?d00001 diff --git a/doc/docs/files/images/10093_2.png b/doc/docs/files/images/10093_2.png new file mode 100644 index 0000000000000000000000000000000000000000..997081cf487cb530b9fc87e066126cca0974fb25 GIT binary patch literal 42180 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 literal 0 HcmV?d00001 diff --git a/sop-1.10.0.sql b/sop-1.10.0.sql new file mode 100644 index 00000000..8a525835 --- /dev/null +++ b/sop-1.10.0.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS `config_common`; + +CREATE TABLE `config_common` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `config_group` varchar(64) NOT NULL DEFAULT '' COMMENT '配置分组', + `config_key` varchar(64) NOT NULL DEFAULT '' COMMENT '配置key', + `content` varchar(128) NOT NULL DEFAULT '' COMMENT '内容', + `remark` varchar(128) DEFAULT NULL COMMENT '备注', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_groupkey` (`config_group`,`config_key`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='通用配置表'; diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitNewApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitNewApi.java index e665bbb6..e31fe409 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitNewApi.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitNewApi.java @@ -9,39 +9,27 @@ import com.gitee.easyopen.util.CopyUtil; import com.gitee.fastmybatis.core.PageInfo; import com.gitee.fastmybatis.core.query.Query; import com.gitee.fastmybatis.core.query.Sort; -import com.gitee.fastmybatis.core.query.expression.ValueExpression; import com.gitee.fastmybatis.core.util.MapperUtil; -import com.gitee.fastmybatis.core.util.MyBeanUtil; import com.gitee.sop.adminserver.api.service.param.LimitNewAddParam; import com.gitee.sop.adminserver.api.service.param.LimitNewParam; import com.gitee.sop.adminserver.api.service.param.LimitNewUpdateParam; -import com.gitee.sop.adminserver.api.service.param.RouteSearchParam; import com.gitee.sop.adminserver.api.service.result.LimitNewVO; import com.gitee.sop.adminserver.bean.ConfigLimitDto; -import com.gitee.sop.adminserver.bean.GatewayRouteDefinition; -import com.gitee.sop.adminserver.common.LimitEnum; import com.gitee.sop.adminserver.entity.ConfigLimit; import com.gitee.sop.adminserver.mapper.ConfigLimitMapper; import com.gitee.sop.adminserver.service.RouteConfigService; import com.gitee.sop.adminserver.service.RouteService; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.BooleanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - /** * 限流 * * @author tanghc */ @ApiService -@ApiDoc("服务管理") +@ApiDoc("服务管理-限流管理") @Slf4j public class LimitNewApi { diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java new file mode 100644 index 00000000..ad037c42 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LogApi.java @@ -0,0 +1,169 @@ +package com.gitee.sop.adminserver.api.service; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.gitee.easyopen.annotation.Api; +import com.gitee.easyopen.annotation.ApiService; +import com.gitee.easyopen.doc.annotation.ApiDoc; +import com.gitee.easyopen.doc.annotation.ApiDocMethod; +import com.gitee.easyopen.exception.ApiException; +import com.gitee.easyopen.verify.DefaultMd5Verifier; +import com.gitee.fastmybatis.core.query.Query; +import com.gitee.sop.adminserver.api.service.param.LogMonitorInstanceAddParam; +import com.gitee.sop.adminserver.api.service.result.LogMonitorInstanceVO; +import com.gitee.sop.adminserver.common.QueryUtil; +import com.gitee.sop.adminserver.entity.ConfigCommon; +import com.gitee.sop.adminserver.mapper.ConfigCommonMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author tanghc + */ +@ApiService +@ApiDoc("服务管理-日志监控") +@Slf4j +public class LogApi { + + public static final String LOG_MONITOR_INSTANCE = "log.monitor.instance"; + public static final String CODE_SUCCESS = "10000"; + + @Autowired + ConfigCommonMapper configCommonMapper; + + RestTemplate restTemplate = new RestTemplate(); + + @Value("${zuul.secret}") + private String secret; + + @Api(name = "monitor.log.list") + @ApiDocMethod(description = "获取监控日志") + List listLog() { + List configCommonList = configCommonMapper.listByColumn("config_group", LOG_MONITOR_INSTANCE); + List ret = new ArrayList<>(); + int id = 1; + for (ConfigCommon configCommon : configCommonList) { + int pid = id++; + String ipPort = configCommon.getConfigKey(); + // 父节点 + LogMonitorInstanceVO logMonitorInstanceVOParent = new LogMonitorInstanceVO(); + logMonitorInstanceVOParent.setRawId(configCommon.getId()); + logMonitorInstanceVOParent.setTreeId(pid); + logMonitorInstanceVOParent.setMonitorName(configCommon.getContent()); + ret.add(logMonitorInstanceVOParent); + try { + String logData = this.requestLogServer(ipPort, "listErrors"); + JSONObject jsonObject = JSON.parseObject(logData); + if (CODE_SUCCESS.equals(jsonObject.getString("code"))) { + int errorTotal = 0; + List data = JSON.parseArray(jsonObject.getString("data"), LogMonitorInstanceVO.class); + for (LogMonitorInstanceVO instanceVO : data) { + instanceVO.setTreeId(id++); + instanceVO.setParentId(pid); + errorTotal += instanceVO.getCount(); + } + ret.addAll(data); + logMonitorInstanceVOParent.setCount(errorTotal); + } + } catch (Exception e) { + log.error("获取日志信息出错", e); + throw new ApiException("获取日志信息出错"); + } + } + Collections.sort(ret, Comparator.comparing(LogMonitorInstanceVO::getCount)); + return ret; + } + + @Api(name = "monitor.log.clear") + @ApiDocMethod(description = "清空日志") + void clearLog(@NotNull(message = "id不能为空") Long id) { + ConfigCommon configCommon = configCommonMapper.getById(id); + if (configCommon == null) { + return; + } + try { + String ipPort = configCommon.getConfigKey(); + this.requestLogServer(ipPort, "clearErrors"); + } catch (Exception e) { + throw new ApiException("清除失败"); + } + } + + @Api(name = "monitor.instance.list") + @ApiDocMethod(description = "获取已添加的监控实例") + List listServiceInstance() { + List configCommonList = configCommonMapper.listByColumn("config_group", LOG_MONITOR_INSTANCE); + return configCommonList.stream() + .map(ConfigCommon::getConfigKey) + .collect(Collectors.toList()); + } + + @Api(name = "monitor.instance.add") + @ApiDocMethod(description = "添加监控实例") + void addServiceInstance(LogMonitorInstanceAddParam param) { + String ipPort = param.getIp() + ":" + param.getPort(); + this.checkInstance(ipPort); + + Query query = new Query(); + query.eq("config_group", LOG_MONITOR_INSTANCE) + .eq("config_key", ipPort); + ConfigCommon rec = configCommonMapper.getByQuery(query); + if (rec != null) { + throw new ApiException("该实例已添加"); + } + ConfigCommon configCommon = new ConfigCommon(); + configCommon.setConfigGroup(LOG_MONITOR_INSTANCE); + configCommon.setConfigKey(ipPort); + configCommon.setContent(param.getServiceId() + "(" + ipPort + ")"); + configCommonMapper.saveIgnoreNull(configCommon); + } + + private void checkInstance(String ipPort) { + try { + String json = this.requestLogServer(ipPort, "listErrors"); + JSONObject jsonObject = JSON.parseObject(json); + if (!CODE_SUCCESS.equals(jsonObject.getString("code"))) { + log.error("请求结果:{}", json); + throw new ApiException("添加失败"); + } + } catch (Exception e) { + log.error("添加失败", e); + throw new ApiException("添加失败"); + } + } + + private String requestLogServer(String ipPort, String path) throws Exception { + DefaultMd5Verifier md5Verifier = new DefaultMd5Verifier(); + Map params = new HashMap<>(); + params.put("time", System.currentTimeMillis()); + String sign = md5Verifier.buildSign(params, secret); + params.put("sign", sign); + String query = QueryUtil.buildQueryString(params); + String url = "http://" + ipPort + "/" + path + "?" + query; + ResponseEntity entity = restTemplate.getForEntity(url, String.class); + if (entity.getStatusCode() != HttpStatus.OK) { + throw new IllegalAccessException("无权访问"); + } + return entity.getBody(); + } + + @Api(name = "monitor.instance.del") + @ApiDocMethod(description = "删除监控实例") + void delServiceInstance(@NotNull(message = "id不能为空") Long id) { + configCommonMapper.deleteById(id); + } + +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/RouteApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/RouteApi.java index d0dab1d1..433a0b00 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/RouteApi.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/RouteApi.java @@ -43,7 +43,7 @@ import java.util.stream.Collectors; * @author tanghc */ @ApiService -@ApiDoc("服务管理") +@ApiDoc("服务管理-路由管理") @Slf4j public class RouteApi { diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java index aab9df9c..93653a3b 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ServiceApi.java @@ -36,7 +36,7 @@ import java.util.stream.Collectors; * @author tanghc */ @ApiService -@ApiDoc("服务管理") +@ApiDoc("服务管理-服务列表") @Slf4j public class ServiceApi { diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LogMonitorInstanceAddParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LogMonitorInstanceAddParam.java new file mode 100644 index 00000000..bc618268 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LogMonitorInstanceAddParam.java @@ -0,0 +1,19 @@ +package com.gitee.sop.adminserver.api.service.param; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author tanghc + */ +@Data +public class LogMonitorInstanceAddParam { + @NotBlank(message = "serviceId不能为空") + private String serviceId; + + @NotBlank(message = "ip不能为空") + private String ip; + + private int port; +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LogMonitorInstanceVO.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LogMonitorInstanceVO.java new file mode 100644 index 00000000..ab20e814 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LogMonitorInstanceVO.java @@ -0,0 +1,22 @@ +package com.gitee.sop.adminserver.api.service.result; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class LogMonitorInstanceVO { + private String id; + private int treeId; + // 表主键 + private long rawId; + private String name; + private String version; + private String serviceId; + private String errorMsg; + private long count; + + private int parentId; + private String monitorName; +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/QueryUtil.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/QueryUtil.java new file mode 100644 index 00000000..1f00e5c7 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/QueryUtil.java @@ -0,0 +1,28 @@ +package com.gitee.sop.adminserver.common; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map; + +/** + * @author tanghc + */ +public class QueryUtil { + + public static String buildQueryString(Map params) throws UnsupportedEncodingException { + if (params == null || params.size() == 0) { + return ""; + } + StringBuilder query = new StringBuilder(); + int i = 0; + for (Map.Entry entry : params.entrySet()) { + String name = entry.getKey(); + String value = String.valueOf(entry.getValue()); + if (i++ > 0) { + query.append("&"); + } + query.append(name).append("=").append(URLEncoder.encode(value, "UTF-8")); + } + return query.toString(); + } +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/config/WebConfig.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/config/WebConfig.java index fe565750..7c8be1f3 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/config/WebConfig.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/config/WebConfig.java @@ -32,9 +32,10 @@ public class WebConfig { @Bean ApiConfig apiConfig() { ApiConfig apiConfig = new ApiConfig(); - apiConfig.setJsonResultSerializer(obj -> { - return JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat); - }); + apiConfig.setJsonResultSerializer(obj -> JSON.toJSONString(obj, + SerializerFeature.WriteDateUseDateFormat + , SerializerFeature.WriteNullStringAsEmpty + , SerializerFeature.WriteMapNullValue)); ApiSessionManager apiSessionManager = new ApiSessionManager(); // session有效期 int timeout = NumberUtils.toInt(accessTokenTimeout, 30); diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigCommon.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigCommon.java new file mode 100644 index 00000000..cf85d185 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigCommon.java @@ -0,0 +1,45 @@ +package com.gitee.sop.adminserver.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 表名:config_common + * 备注:通用配置表 + * + * @author tanghc + */ +@Table(name = "config_common") +@Data +public class ConfigCommon { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + /** 数据库字段:id */ + private Long id; + + /** 配置分组, 数据库字段:config_group */ + private String configGroup; + + /** 配置key, 数据库字段:config_key */ + private String configKey; + + /** 内容, 数据库字段:content */ + private String content; + + /** 备注, 数据库字段:remark */ + private String remark; + + /** 数据库字段:gmt_create */ + private Date gmtCreate; + + /** 数据库字段:gmt_modified */ + private Date gmtModified; +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/ConfigCommonMapper.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/ConfigCommonMapper.java new file mode 100644 index 00000000..5a854616 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/ConfigCommonMapper.java @@ -0,0 +1,12 @@ +package com.gitee.sop.adminserver.mapper; + +import com.gitee.fastmybatis.core.mapper.CrudMapper; + +import com.gitee.sop.adminserver.entity.ConfigCommon; + + +/** + * @author tanghc + */ +public interface ConfigCommonMapper extends CrudMapper { +} diff --git a/sop-admin/sop-admin-server/src/main/resources/application-dev.yml b/sop-admin/sop-admin-server/src/main/resources/application-dev.yml index f9ff1e38..e33f7e29 100644 --- a/sop-admin/sop-admin-server/src/main/resources/application-dev.yml +++ b/sop-admin/sop-admin-server/src/main/resources/application-dev.yml @@ -48,3 +48,7 @@ logging: mybatis: fill: {com.gitee.fastmybatis.core.support.DateFillInsert: gmt_create, com.gitee.fastmybatis.core.support.DateFillUpdate: gmt_modified} + +# 不用改,如果要改,建议全局替换修改 +zuul: + secret: MZZOUSTua6LzApIWXCwEgbBmxSzpzC \ No newline at end of file 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 3e09c066..3cb31bb6 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/app.bc31495f.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.36a3cb86.js similarity index 51% rename from sop-admin/sop-admin-server/src/main/resources/public/static/js/app.bc31495f.js rename to sop-admin/sop-admin-server/src/main/resources/public/static/js/app.36a3cb86.js index 70c85c97..d0df45df 100644 --- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.bc31495f.js +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.36a3cb86.js @@ -1 +1 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["app"],{0:function(e,t,n){e.exports=n("56d7")},"0cb8":function(e,t,n){},"186a":function(e,t,n){"use strict";var a=n("dc52"),i=n.n(a);i.a},"18f0":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-link",use:"icon-link-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"1f59":function(e,t,n){},2536:function(e,t,n){},2635:function(e,t,n){},"2a3d":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-password",use:"icon-password-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"30c3":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-example",use:"icon-example-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"34c8":function(e,t,n){"use strict";var a=n("5332"),i=n.n(a);i.a},"405a":function(e,t,n){"use strict";var a=n("1f59"),i=n.n(a);i.a},"47f1":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-table",use:"icon-table-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"4df5":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-eye",use:"icon-eye-usage",viewBox:"0 0 128 64",content:''});r.a.add(s);t["default"]=s},"50be":function(e,t,n){"use strict";var a=n("2536"),i=n.n(a);i.a},"51ff":function(e,t,n){var a={"./dashboard.svg":"f782","./example.svg":"30c3","./eye-open.svg":"d7ec","./eye.svg":"4df5","./form.svg":"eb1b","./link.svg":"18f0","./nested.svg":"dcf8","./password.svg":"2a3d","./table.svg":"47f1","./tree.svg":"93cd","./user.svg":"b3b5"};function i(e){var t=o(e);return n(t)}function o(e){var t=a[e];if(!(t+1)){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}return t}i.keys=function(){return Object.keys(a)},i.resolve=o,e.exports=i,i.id="51ff"},5332:function(e,t,n){},"56d7":function(e,t,n){"use strict";n.r(t);n("cadf"),n("551c"),n("f751"),n("097d");var a=n("2b0e"),i=(n("f5df"),n("5c96")),o=n.n(i),r=(n("0fae"),n("f0d9")),s=n.n(r),c=(n("b20f"),function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("router-view")],1)}),u=[],l={name:"App"},d=l,h=n("2877"),m=Object(h["a"])(d,c,u,!1,null,null,null),f=m.exports,p=n("2f62"),v=(n("7f7f"),{sidebar:function(e){return e.app.sidebar},device:function(e){return e.app.device},token:function(e){return e.user.token},avatar:function(e){return e.user.avatar},name:function(e){return e.user.name}}),b=v,g=n("a78e"),w=n.n(g),x={sidebar:{opened:!w.a.get("sidebarStatus")||!!+w.a.get("sidebarStatus"),withoutAnimation:!1},device:"desktop"},y={TOGGLE_SIDEBAR:function(e){e.sidebar.opened=!e.sidebar.opened,e.sidebar.withoutAnimation=!1,e.sidebar.opened?w.a.set("sidebarStatus",1):w.a.set("sidebarStatus",0)},CLOSE_SIDEBAR:function(e,t){w.a.set("sidebarStatus",0),e.sidebar.opened=!1,e.sidebar.withoutAnimation=t},TOGGLE_DEVICE:function(e,t){e.device=t}},_={toggleSideBar:function(e){var t=e.commit;t("TOGGLE_SIDEBAR")},closeSideBar:function(e,t){var n=e.commit,a=t.withoutAnimation;n("CLOSE_SIDEBAR",a)},toggleDevice:function(e,t){var n=e.commit;n("TOGGLE_DEVICE",t)}},C={namespaced:!0,state:x,mutations:y,actions:_},k=n("83d6"),O=n.n(k),S=O.a.showSettings,z=O.a.fixedHeader,B=O.a.sidebarLogo,H={showSettings:S,fixedHeader:z,sidebarLogo:B},M={CHANGE_SETTING:function(e,t){var n=t.key,a=t.value;e.hasOwnProperty(n)&&(e[n]=a)}},T={changeSetting:function(e,t){var n=e.commit;n("CHANGE_SETTING",t)}},E={namespaced:!0,state:H,mutations:M,actions:T},L=n("bc3a"),$=n.n(L),V=n("5f87"),A=$.a.create({baseURL:"/api",withCredentials:!0,timeout:5e3});A.interceptors.request.use(function(e){return bt.getters.token&&(e.headers["X-Token"]=Object(V["a"])()),e},function(e){return console.log(e),Promise.reject(e)}),A.interceptors.response.use(function(e){var t=e.data;return 2e4!==t.code?(Object(i["Message"])({message:t.message||"error",type:"error",duration:5e3}),50008!==t.code&&50012!==t.code&&50014!==t.code||i["MessageBox"].confirm("You have been logged out, you can cancel to stay on this page, or log in again","Confirm logout",{confirmButtonText:"Re-Login",cancelButtonText:"Cancel",type:"warning"}).then(function(){bt.dispatch("user/resetToken").then(function(){location.reload()})}),Promise.reject(t.message||"error")):t},function(e){return console.log("err"+e),Object(i["Message"])({message:e.message,type:"error",duration:5e3}),Promise.reject(e)});var j=A;function P(e){return j({url:"/user/login",method:"post",data:e})}function I(e){return j({url:"/user/info",method:"get",params:{token:e}})}function N(){return j({url:"/user/logout",method:"post"})}var D=n("8c4f"),q=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"app-wrapper",class:e.classObj},["mobile"===e.device&&e.sidebar.opened?n("div",{staticClass:"drawer-bg",on:{click:e.handleClickOutside}}):e._e(),e._v(" "),n("sidebar",{staticClass:"sidebar-container"}),e._v(" "),n("div",{staticClass:"main-container"},[n("div",{class:{"fixed-header":e.fixedHeader}},[n("navbar")],1),e._v(" "),n("app-main")],1)],1)},R=[],G=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"navbar"},[n("hamburger",{staticClass:"hamburger-container",attrs:{"is-active":e.sidebar.opened},on:{toggleClick:e.toggleSideBar}}),e._v(" "),n("breadcrumb",{staticClass:"breadcrumb-container"}),e._v(" "),n("div",{staticClass:"right-menu"},[n("el-button",{staticStyle:{"margin-right":"10px"},attrs:{type:"text"},on:{click:e.doLogout}},[e._v("退出")])],1)],1)},F=[],U=n("cebc"),K=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("el-breadcrumb",{staticClass:"app-breadcrumb",attrs:{separator:"/"}},[n("transition-group",{attrs:{name:"breadcrumb"}},e._l(e.levelList,function(t,a){return n("el-breadcrumb-item",{key:t.path},["noRedirect"===t.redirect||a==e.levelList.length-1?n("span",{staticClass:"no-redirect"},[e._v(e._s(t.meta.title))]):n("a",{on:{click:function(n){return n.preventDefault(),e.handleLink(t)}}},[e._v(e._s(t.meta.title))])])}),1)],1)},W=[],J=n("bd11"),X=n.n(J),Y={data:function(){return{levelList:null}},watch:{$route:function(){this.getBreadcrumb()}},created:function(){this.getBreadcrumb()},methods:{getBreadcrumb:function(){var e=this.$route.matched.filter(function(e){return e.meta&&e.meta.title}),t=e[0];this.isDashboard(t)||(e=[{path:"/dashboard",meta:{title:"Dashboard"}}].concat(e)),this.levelList=e.filter(function(e){return e.meta&&e.meta.title&&!1!==e.meta.breadcrumb})},isDashboard:function(e){var t=e&&e.name;return!!t&&t.trim().toLocaleLowerCase()==="Dashboard".toLocaleLowerCase()},pathCompile:function(e){var t=this.$route.params,n=X.a.compile(e);return n(t)},handleLink:function(e){var t=e.redirect,n=e.path;t?this.$router.push(t):this.$router.push(this.pathCompile(n))}}},Q=Y,Z=(n("34c8"),Object(h["a"])(Q,K,W,!1,null,"62cc9144",null)),ee=Z.exports,te=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticStyle:{padding:"0 15px"},on:{click:e.toggleClick}},[n("svg",{staticClass:"hamburger",class:{"is-active":e.isActive},attrs:{viewBox:"0 0 1024 1024",xmlns:"http://www.w3.org/2000/svg",width:"64",height:"64"}},[n("path",{attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"}})])])},ne=[],ae={name:"Hamburger",props:{isActive:{type:Boolean,default:!1}},methods:{toggleClick:function(){this.$emit("toggleClick")}}},ie=ae,oe=(n("186a"),Object(h["a"])(ie,te,ne,!1,null,"49e15297",null)),re=oe.exports,se={components:{Breadcrumb:ee,Hamburger:re},computed:Object(U["a"])({},Object(p["b"])(["sidebar","avatar"])),methods:{toggleSideBar:function(){this.$store.dispatch("app/toggleSideBar")},doLogout:function(){this.logout()}}},ce=se,ue=(n("405a"),Object(h["a"])(ce,G,F,!1,null,"1eff5c58",null)),le=ue.exports,de=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:{"has-logo":e.showLogo}},[e.showLogo?n("logo",{attrs:{collapse:e.isCollapse}}):e._e(),e._v(" "),n("el-scrollbar",{attrs:{"wrap-class":"scrollbar-wrapper"}},[n("el-menu",{attrs:{"default-active":e.activeMenu,collapse:e.isCollapse,"background-color":e.variables.menuBg,"text-color":e.variables.menuText,"unique-opened":!1,"active-text-color":e.variables.menuActiveText,"collapse-transition":!1,mode:"vertical"}},e._l(e.routes,function(e){return n("sidebar-item",{key:e.path,attrs:{item:e,"base-path":e.path}})}),1)],1)],1)},he=[],me=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"sidebar-logo-container",class:{collapse:e.collapse}},[n("transition",{attrs:{name:"sidebarLogoFade"}},[e.collapse?n("router-link",{key:"collapse",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])]):n("router-link",{key:"expand",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):e._e(),e._v(" "),n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])])],1)],1)},fe=[],pe={name:"SidebarLogo",props:{collapse:{type:Boolean,required:!0}},data:function(){return{title:"SOP Admin",logo:"https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png"}}},ve=pe,be=(n("7c27"),Object(h["a"])(ve,me,fe,!1,null,"b905289c",null)),ge=be.exports,we=function(){var e=this,t=e.$createElement,n=e._self._c||t;return e.item.hidden?e._e():n("div",{staticClass:"menu-wrapper"},[!e.hasOneShowingChild(e.item.children,e.item)||e.onlyOneChild.children&&!e.onlyOneChild.noShowingChildren||e.item.alwaysShow?n("el-submenu",{ref:"subMenu",attrs:{index:e.resolvePath(e.item.path),"popper-append-to-body":""}},[n("template",{slot:"title"},[e.item.meta?n("item",{attrs:{icon:e.item.meta&&e.item.meta.icon,title:e.item.meta.title}}):e._e()],1),e._v(" "),e._l(e.item.children,function(t){return n("sidebar-item",{key:t.path,staticClass:"nest-menu",attrs:{"is-nest":!0,item:t,"base-path":e.resolvePath(t.path)}})})],2):[e.onlyOneChild.meta?n("app-link",{attrs:{to:e.resolvePath(e.onlyOneChild.path)}},[n("el-menu-item",{class:{"submenu-title-noDropdown":!e.isNest},attrs:{index:e.resolvePath(e.onlyOneChild.path)}},[n("item",{attrs:{icon:e.onlyOneChild.meta.icon||e.item.meta&&e.item.meta.icon,title:e.onlyOneChild.meta.title}})],1)],1):e._e()]],2)},xe=[],ye=n("df7c"),_e=n.n(ye);function Ce(e){return/^(https?:|mailto:|tel:)/.test(e)}var ke,Oe,Se={name:"MenuItem",functional:!0,props:{icon:{type:String,default:""},title:{type:String,default:""}},render:function(e,t){var n=t.props,a=n.icon,i=n.title,o=[];return a&&o.push(e("svg-icon",{attrs:{"icon-class":a}})),i&&o.push(e("span",{slot:"title"},[i])),o}},ze=Se,Be=Object(h["a"])(ze,ke,Oe,!1,null,null,null),He=Be.exports,Me=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("component",e._b({},"component",e.linkProps(e.to),!1),[e._t("default")],2)},Te=[],Ee={props:{to:{type:String,required:!0}},methods:{linkProps:function(e){return Ce(e)?{is:"a",href:e,target:"_blank",rel:"noopener"}:{is:"router-link",to:e}}}},Le=Ee,$e=Object(h["a"])(Le,Me,Te,!1,null,null,null),Ve=$e.exports,Ae={computed:{device:function(){return this.$store.state.app.device}},mounted:function(){this.fixBugIniOS()},methods:{fixBugIniOS:function(){var e=this,t=this.$refs.subMenu;if(t){var n=t.handleMouseleave;t.handleMouseleave=function(t){"mobile"!==e.device&&n(t)}}}}},je={name:"SidebarItem",components:{Item:He,AppLink:Ve},mixins:[Ae],props:{item:{type:Object,required:!0},isNest:{type:Boolean,default:!1},basePath:{type:String,default:""}},data:function(){return this.onlyOneChild=null,{}},methods:{hasOneShowingChild:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,a=t.filter(function(t){return!t.hidden&&(e.onlyOneChild=t,!0)});return 1===a.length||0===a.length&&(this.onlyOneChild=Object(U["a"])({},n,{path:"",noShowingChildren:!0}),!0)},resolvePath:function(e){return Ce(e)?e:Ce(this.basePath)?this.basePath:_e.a.resolve(this.basePath,e)}}},Pe=je,Ie=Object(h["a"])(Pe,we,xe,!1,null,null,null),Ne=Ie.exports,De=n("cf1e"),qe=n.n(De),Re={components:{SidebarItem:Ne,Logo:ge},computed:Object(U["a"])({},Object(p["b"])(["sidebar"]),{routes:function(){return this.$router.options.routes},activeMenu:function(){var e=this.$route,t=e.meta,n=e.path;return t.activeMenu?t.activeMenu:n},showLogo:function(){return this.$store.state.settings.sidebarLogo},variables:function(){return qe.a},isCollapse:function(){return!this.sidebar.opened}})},Ge=Re,Fe=Object(h["a"])(Ge,de,he,!1,null,null,null),Ue=Fe.exports,Ke=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",{staticClass:"app-main"},[n("transition",{attrs:{name:"fade-transform",mode:"out-in"}},[n("router-view",{key:e.key})],1)],1)},We=[],Je={name:"AppMain",computed:{key:function(){return this.$route.fullPath}}},Xe=Je,Ye=(n("50be"),Object(h["a"])(Xe,Ke,We,!1,null,"43c24f68",null)),Qe=Ye.exports,Ze=document,et=Ze.body,tt=992,nt={watch:{$route:function(e){"mobile"===this.device&&this.sidebar.opened&&bt.dispatch("app/closeSideBar",{withoutAnimation:!1})}},beforeMount:function(){window.addEventListener("resize",this.$_resizeHandler)},beforeDestroy:function(){window.removeEventListener("resize",this.$_resizeHandler)},mounted:function(){var e=this.$_isMobile();e&&(bt.dispatch("app/toggleDevice","mobile"),bt.dispatch("app/closeSideBar",{withoutAnimation:!0}))},methods:{$_isMobile:function(){var e=et.getBoundingClientRect();return e.width-1'});r.a.add(s);t["default"]=s},"9f2b":function(e,t,n){"use strict";var a=n("5bb9"),i=n.n(a);i.a},b20f:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},b3b5:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-user",use:"icon-user-usage",viewBox:"0 0 130 130",content:''});r.a.add(s);t["default"]=s},cf1e:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},d7ec:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-eye-open",use:"icon-eye-open-usage",viewBox:"0 0 1024 1024",content:''});r.a.add(s);t["default"]=s},dc52:function(e,t,n){},dcf8:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-nested",use:"icon-nested-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},eb1b:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-form",use:"icon-form-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},f782:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-dashboard",use:"icon-dashboard-usage",viewBox:"0 0 128 100",content:''});r.a.add(s);t["default"]=s}},[[0,"runtime","chunk-elementUI","chunk-libs"]]]); \ No newline at end of file +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["app"],{0:function(e,t,n){e.exports=n("56d7")},"0cb8":function(e,t,n){},"186a":function(e,t,n){"use strict";var a=n("dc52"),i=n.n(a);i.a},"18f0":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-link",use:"icon-link-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"1f59":function(e,t,n){},2536:function(e,t,n){},2635:function(e,t,n){},"2a3d":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-password",use:"icon-password-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"30c3":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-example",use:"icon-example-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"34c8":function(e,t,n){"use strict";var a=n("5332"),i=n.n(a);i.a},"405a":function(e,t,n){"use strict";var a=n("1f59"),i=n.n(a);i.a},"47f1":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-table",use:"icon-table-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},"4df5":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-eye",use:"icon-eye-usage",viewBox:"0 0 128 64",content:''});r.a.add(s);t["default"]=s},"50be":function(e,t,n){"use strict";var a=n("2536"),i=n.n(a);i.a},"51ff":function(e,t,n){var a={"./dashboard.svg":"f782","./example.svg":"30c3","./eye-open.svg":"d7ec","./eye.svg":"4df5","./form.svg":"eb1b","./link.svg":"18f0","./nested.svg":"dcf8","./password.svg":"2a3d","./table.svg":"47f1","./tree.svg":"93cd","./user.svg":"b3b5"};function i(e){var t=o(e);return n(t)}function o(e){var t=a[e];if(!(t+1)){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}return t}i.keys=function(){return Object.keys(a)},i.resolve=o,e.exports=i,i.id="51ff"},5332:function(e,t,n){},"56d7":function(e,t,n){"use strict";n.r(t);n("cadf"),n("551c"),n("f751"),n("097d");var a=n("2b0e"),i=(n("f5df"),n("5c96")),o=n.n(i),r=(n("0fae"),n("f0d9")),s=n.n(r),c=(n("b20f"),function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("router-view")],1)}),u=[],l={name:"App"},d=l,m=n("2877"),h=Object(m["a"])(d,c,u,!1,null,null,null),f=h.exports,p=n("2f62"),v=(n("7f7f"),{sidebar:function(e){return e.app.sidebar},device:function(e){return e.app.device},token:function(e){return e.user.token},avatar:function(e){return e.user.avatar},name:function(e){return e.user.name}}),b=v,g=n("a78e"),w=n.n(g),x={sidebar:{opened:!w.a.get("sidebarStatus")||!!+w.a.get("sidebarStatus"),withoutAnimation:!1},device:"desktop"},y={TOGGLE_SIDEBAR:function(e){e.sidebar.opened=!e.sidebar.opened,e.sidebar.withoutAnimation=!1,e.sidebar.opened?w.a.set("sidebarStatus",1):w.a.set("sidebarStatus",0)},CLOSE_SIDEBAR:function(e,t){w.a.set("sidebarStatus",0),e.sidebar.opened=!1,e.sidebar.withoutAnimation=t},TOGGLE_DEVICE:function(e,t){e.device=t}},_={toggleSideBar:function(e){var t=e.commit;t("TOGGLE_SIDEBAR")},closeSideBar:function(e,t){var n=e.commit,a=t.withoutAnimation;n("CLOSE_SIDEBAR",a)},toggleDevice:function(e,t){var n=e.commit;n("TOGGLE_DEVICE",t)}},C={namespaced:!0,state:x,mutations:y,actions:_},k=n("83d6"),O=n.n(k),S=O.a.showSettings,z=O.a.fixedHeader,B=O.a.sidebarLogo,H={showSettings:S,fixedHeader:z,sidebarLogo:B},M={CHANGE_SETTING:function(e,t){var n=t.key,a=t.value;e.hasOwnProperty(n)&&(e[n]=a)}},T={changeSetting:function(e,t){var n=e.commit;n("CHANGE_SETTING",t)}},E={namespaced:!0,state:H,mutations:M,actions:T},L=n("bc3a"),$=n.n(L),V=n("5f87"),A=$.a.create({baseURL:"/api",withCredentials:!0,timeout:5e3});A.interceptors.request.use(function(e){return bt.getters.token&&(e.headers["X-Token"]=Object(V["a"])()),e},function(e){return console.log(e),Promise.reject(e)}),A.interceptors.response.use(function(e){var t=e.data;return 2e4!==t.code?(Object(i["Message"])({message:t.message||"error",type:"error",duration:5e3}),50008!==t.code&&50012!==t.code&&50014!==t.code||i["MessageBox"].confirm("You have been logged out, you can cancel to stay on this page, or log in again","Confirm logout",{confirmButtonText:"Re-Login",cancelButtonText:"Cancel",type:"warning"}).then(function(){bt.dispatch("user/resetToken").then(function(){location.reload()})}),Promise.reject(t.message||"error")):t},function(e){return console.log("err"+e),Object(i["Message"])({message:e.message,type:"error",duration:5e3}),Promise.reject(e)});var j=A;function P(e){return j({url:"/user/login",method:"post",data:e})}function I(e){return j({url:"/user/info",method:"get",params:{token:e}})}function N(){return j({url:"/user/logout",method:"post"})}var D=n("8c4f"),q=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"app-wrapper",class:e.classObj},["mobile"===e.device&&e.sidebar.opened?n("div",{staticClass:"drawer-bg",on:{click:e.handleClickOutside}}):e._e(),e._v(" "),n("sidebar",{staticClass:"sidebar-container"}),e._v(" "),n("div",{staticClass:"main-container"},[n("div",{class:{"fixed-header":e.fixedHeader}},[n("navbar")],1),e._v(" "),n("app-main")],1)],1)},R=[],G=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"navbar"},[n("hamburger",{staticClass:"hamburger-container",attrs:{"is-active":e.sidebar.opened},on:{toggleClick:e.toggleSideBar}}),e._v(" "),n("breadcrumb",{staticClass:"breadcrumb-container"}),e._v(" "),n("div",{staticClass:"right-menu"},[n("el-button",{staticStyle:{"margin-right":"10px"},attrs:{type:"text"},on:{click:e.doLogout}},[e._v("退出")])],1)],1)},F=[],U=n("cebc"),K=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("el-breadcrumb",{staticClass:"app-breadcrumb",attrs:{separator:"/"}},[n("transition-group",{attrs:{name:"breadcrumb"}},e._l(e.levelList,function(t,a){return n("el-breadcrumb-item",{key:t.path},["noRedirect"===t.redirect||a==e.levelList.length-1?n("span",{staticClass:"no-redirect"},[e._v(e._s(t.meta.title))]):n("a",{on:{click:function(n){return n.preventDefault(),e.handleLink(t)}}},[e._v(e._s(t.meta.title))])])}),1)],1)},W=[],J=n("bd11"),X=n.n(J),Y={data:function(){return{levelList:null}},watch:{$route:function(){this.getBreadcrumb()}},created:function(){this.getBreadcrumb()},methods:{getBreadcrumb:function(){var e=this.$route.matched.filter(function(e){return e.meta&&e.meta.title}),t=e[0];this.isDashboard(t)||(e=[{path:"/dashboard",meta:{title:"Dashboard"}}].concat(e)),this.levelList=e.filter(function(e){return e.meta&&e.meta.title&&!1!==e.meta.breadcrumb})},isDashboard:function(e){var t=e&&e.name;return!!t&&t.trim().toLocaleLowerCase()==="Dashboard".toLocaleLowerCase()},pathCompile:function(e){var t=this.$route.params,n=X.a.compile(e);return n(t)},handleLink:function(e){var t=e.redirect,n=e.path;t?this.$router.push(t):this.$router.push(this.pathCompile(n))}}},Q=Y,Z=(n("34c8"),Object(m["a"])(Q,K,W,!1,null,"62cc9144",null)),ee=Z.exports,te=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticStyle:{padding:"0 15px"},on:{click:e.toggleClick}},[n("svg",{staticClass:"hamburger",class:{"is-active":e.isActive},attrs:{viewBox:"0 0 1024 1024",xmlns:"http://www.w3.org/2000/svg",width:"64",height:"64"}},[n("path",{attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"}})])])},ne=[],ae={name:"Hamburger",props:{isActive:{type:Boolean,default:!1}},methods:{toggleClick:function(){this.$emit("toggleClick")}}},ie=ae,oe=(n("186a"),Object(m["a"])(ie,te,ne,!1,null,"49e15297",null)),re=oe.exports,se={components:{Breadcrumb:ee,Hamburger:re},computed:Object(U["a"])({},Object(p["b"])(["sidebar","avatar"])),methods:{toggleSideBar:function(){this.$store.dispatch("app/toggleSideBar")},doLogout:function(){this.logout()}}},ce=se,ue=(n("405a"),Object(m["a"])(ce,G,F,!1,null,"1eff5c58",null)),le=ue.exports,de=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:{"has-logo":e.showLogo}},[e.showLogo?n("logo",{attrs:{collapse:e.isCollapse}}):e._e(),e._v(" "),n("el-scrollbar",{attrs:{"wrap-class":"scrollbar-wrapper"}},[n("el-menu",{attrs:{"default-active":e.activeMenu,collapse:e.isCollapse,"background-color":e.variables.menuBg,"text-color":e.variables.menuText,"unique-opened":!1,"active-text-color":e.variables.menuActiveText,"collapse-transition":!1,mode:"vertical"}},e._l(e.routes,function(e){return n("sidebar-item",{key:e.path,attrs:{item:e,"base-path":e.path}})}),1)],1)],1)},me=[],he=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"sidebar-logo-container",class:{collapse:e.collapse}},[n("transition",{attrs:{name:"sidebarLogoFade"}},[e.collapse?n("router-link",{key:"collapse",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])]):n("router-link",{key:"expand",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):e._e(),e._v(" "),n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])])],1)],1)},fe=[],pe={name:"SidebarLogo",props:{collapse:{type:Boolean,required:!0}},data:function(){return{title:"SOP Admin",logo:"https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png"}}},ve=pe,be=(n("7c27"),Object(m["a"])(ve,he,fe,!1,null,"b905289c",null)),ge=be.exports,we=function(){var e=this,t=e.$createElement,n=e._self._c||t;return e.item.hidden?e._e():n("div",{staticClass:"menu-wrapper"},[!e.hasOneShowingChild(e.item.children,e.item)||e.onlyOneChild.children&&!e.onlyOneChild.noShowingChildren||e.item.alwaysShow?n("el-submenu",{ref:"subMenu",attrs:{index:e.resolvePath(e.item.path),"popper-append-to-body":""}},[n("template",{slot:"title"},[e.item.meta?n("item",{attrs:{icon:e.item.meta&&e.item.meta.icon,title:e.item.meta.title}}):e._e()],1),e._v(" "),e._l(e.item.children,function(t){return n("sidebar-item",{key:t.path,staticClass:"nest-menu",attrs:{"is-nest":!0,item:t,"base-path":e.resolvePath(t.path)}})})],2):[e.onlyOneChild.meta?n("app-link",{attrs:{to:e.resolvePath(e.onlyOneChild.path)}},[n("el-menu-item",{class:{"submenu-title-noDropdown":!e.isNest},attrs:{index:e.resolvePath(e.onlyOneChild.path)}},[n("item",{attrs:{icon:e.onlyOneChild.meta.icon||e.item.meta&&e.item.meta.icon,title:e.onlyOneChild.meta.title}})],1)],1):e._e()]],2)},xe=[],ye=n("df7c"),_e=n.n(ye);function Ce(e){return/^(https?:|mailto:|tel:)/.test(e)}var ke,Oe,Se={name:"MenuItem",functional:!0,props:{icon:{type:String,default:""},title:{type:String,default:""}},render:function(e,t){var n=t.props,a=n.icon,i=n.title,o=[];return a&&o.push(e("svg-icon",{attrs:{"icon-class":a}})),i&&o.push(e("span",{slot:"title"},[i])),o}},ze=Se,Be=Object(m["a"])(ze,ke,Oe,!1,null,null,null),He=Be.exports,Me=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("component",e._b({},"component",e.linkProps(e.to),!1),[e._t("default")],2)},Te=[],Ee={props:{to:{type:String,required:!0}},methods:{linkProps:function(e){return Ce(e)?{is:"a",href:e,target:"_blank",rel:"noopener"}:{is:"router-link",to:e}}}},Le=Ee,$e=Object(m["a"])(Le,Me,Te,!1,null,null,null),Ve=$e.exports,Ae={computed:{device:function(){return this.$store.state.app.device}},mounted:function(){this.fixBugIniOS()},methods:{fixBugIniOS:function(){var e=this,t=this.$refs.subMenu;if(t){var n=t.handleMouseleave;t.handleMouseleave=function(t){"mobile"!==e.device&&n(t)}}}}},je={name:"SidebarItem",components:{Item:He,AppLink:Ve},mixins:[Ae],props:{item:{type:Object,required:!0},isNest:{type:Boolean,default:!1},basePath:{type:String,default:""}},data:function(){return this.onlyOneChild=null,{}},methods:{hasOneShowingChild:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,a=t.filter(function(t){return!t.hidden&&(e.onlyOneChild=t,!0)});return 1===a.length||0===a.length&&(this.onlyOneChild=Object(U["a"])({},n,{path:"",noShowingChildren:!0}),!0)},resolvePath:function(e){return Ce(e)?e:Ce(this.basePath)?this.basePath:_e.a.resolve(this.basePath,e)}}},Pe=je,Ie=Object(m["a"])(Pe,we,xe,!1,null,null,null),Ne=Ie.exports,De=n("cf1e"),qe=n.n(De),Re={components:{SidebarItem:Ne,Logo:ge},computed:Object(U["a"])({},Object(p["b"])(["sidebar"]),{routes:function(){return this.$router.options.routes},activeMenu:function(){var e=this.$route,t=e.meta,n=e.path;return t.activeMenu?t.activeMenu:n},showLogo:function(){return this.$store.state.settings.sidebarLogo},variables:function(){return qe.a},isCollapse:function(){return!this.sidebar.opened}})},Ge=Re,Fe=Object(m["a"])(Ge,de,me,!1,null,null,null),Ue=Fe.exports,Ke=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",{staticClass:"app-main"},[n("transition",{attrs:{name:"fade-transform",mode:"out-in"}},[n("router-view",{key:e.key})],1)],1)},We=[],Je={name:"AppMain",computed:{key:function(){return this.$route.fullPath}}},Xe=Je,Ye=(n("50be"),Object(m["a"])(Xe,Ke,We,!1,null,"43c24f68",null)),Qe=Ye.exports,Ze=document,et=Ze.body,tt=992,nt={watch:{$route:function(e){"mobile"===this.device&&this.sidebar.opened&&bt.dispatch("app/closeSideBar",{withoutAnimation:!1})}},beforeMount:function(){window.addEventListener("resize",this.$_resizeHandler)},beforeDestroy:function(){window.removeEventListener("resize",this.$_resizeHandler)},mounted:function(){var e=this.$_isMobile();e&&(bt.dispatch("app/toggleDevice","mobile"),bt.dispatch("app/closeSideBar",{withoutAnimation:!0}))},methods:{$_isMobile:function(){var e=et.getBoundingClientRect();return e.width-1'});r.a.add(s);t["default"]=s},"9f2b":function(e,t,n){"use strict";var a=n("5bb9"),i=n.n(a);i.a},b20f:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},b3b5:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-user",use:"icon-user-usage",viewBox:"0 0 130 130",content:''});r.a.add(s);t["default"]=s},cf1e:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},d7ec:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-eye-open",use:"icon-eye-open-usage",viewBox:"0 0 1024 1024",content:''});r.a.add(s);t["default"]=s},dc52:function(e,t,n){},dcf8:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-nested",use:"icon-nested-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},eb1b:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-form",use:"icon-form-usage",viewBox:"0 0 128 128",content:''});r.a.add(s);t["default"]=s},f782:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),s=new i.a({id:"icon-dashboard",use:"icon-dashboard-usage",viewBox:"0 0 128 100",content:''});r.a.add(s);t["default"]=s}},[[0,"runtime","chunk-elementUI","chunk-libs"]]]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.447435b3.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.447435b3.js new file mode 100644 index 00000000..da97f7e1 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.447435b3.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0aa95b"],{1248:function(t,e,a){"use strict";a.r(e);var o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-container",[a("el-aside",{staticStyle:{"min-height":"300px",width:"200px"}},[a("el-input",{staticStyle:{"margin-bottom":"20px"},attrs:{"prefix-icon":"el-icon-search",placeholder:"搜索服务...",size:"mini",clearable:""},model:{value:t.filterText,callback:function(e){t.filterText=e},expression:"filterText"}}),t._v(" "),a("el-tree",{ref:"tree2",staticClass:"filter-tree",attrs:{data:t.treeData,props:t.defaultProps,"filter-node-method":t.filterNode,"highlight-current":!0,"expand-on-click-node":!1,"empty-text":"无数据","node-key":"id","default-expand-all":""},on:{"node-click":t.onNodeClick},scopedSlots:t._u([{key:"default",fn:function(e){e.node;var o=e.data;return a("span",{staticClass:"custom-tree-node"},[o.label.length<15?a("span",[t._v(t._s(o.label))]):a("span",[a("el-tooltip",{staticClass:"item",attrs:{content:o.label,effect:"light",placement:"right"}},[a("span",[t._v(t._s(o.label.substring(0,15)+"..."))])])],1)])}}])})],1),t._v(" "),a("el-main",{staticStyle:{"padding-top":"0"}},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"路由名称"}},[a("el-input",{attrs:{placeholder:"输入接口名或版本号"},model:{value:t.searchFormData.id,callback:function(e){t.$set(t.searchFormData,"id",e)},expression:"searchFormData.id"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),a("el-table",{attrs:{data:t.tableData,border:"","max-height":"500"}},[a("el-table-column",{attrs:{prop:"name",label:"接口名 (版本号)",width:"200"},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:"uri",label:"LoadBalance",width:"350"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.uri+e.row.path)+"\n ")]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"roles",label:"访问权限",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[a("span",{domProps:{innerHTML:t._s(t.roleRender(e.row))}})]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"ignoreValidate",label:"忽略验证",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[1===e.row.ignoreValidate?a("span",{staticStyle:{color:"#67C23A"}},[t._v("是")]):t._e(),t._v(" "),0===e.row.ignoreValidate?a("span",{staticStyle:{color:"#909399"}},[t._v("否")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"mergeResult",label:"统一格式输出",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[1===e.row.mergeResult?a("span",{staticStyle:{color:"#67C23A"}},[t._v("是")]):t._e(),t._v(" "),0===e.row.mergeResult?a("span",{staticStyle:{color:"#E6A23C"}},[t._v("否")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"status",label:"状态",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[0===e.row.status?a("span",{staticStyle:{color:"#909399"}},[t._v("待审核")]):t._e(),t._v(" "),1===e.row.status?a("span",{staticStyle:{color:"#67C23A"}},[t._v("已启用")]):t._e(),t._v(" "),2===e.row.status?a("span",{staticStyle:{color:"#F56C6C"}},[t._v("已禁用")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{label:"操作",fixed:"right",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onTableUpdate(e.row)}}},[t._v("修改")]),t._v(" "),e.row.permission?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onTableAuth(e.row)}}},[t._v("授权")]):t._e()]}}])})],1),t._v(" "),a("el-dialog",{attrs:{title:"修改路由",visible:t.routeDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(e){t.routeDialogVisible=e}}},[a("el-form",{attrs:{model:t.routeDialogFormData,"label-width":"120px",size:"mini"}},[a("el-form-item",{attrs:{label:"id"}},[a("el-input",{attrs:{readonly:"readonly"},model:{value:t.routeDialogFormData.id,callback:function(e){t.$set(t.routeDialogFormData,"id",e)},expression:"routeDialogFormData.id"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"uri"}},[a("el-input",{model:{value:t.routeDialogFormData.uri,callback:function(e){t.$set(t.routeDialogFormData,"uri",e)},expression:"routeDialogFormData.uri"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"path"}},[a("el-input",{model:{value:t.routeDialogFormData.path,callback:function(e){t.$set(t.routeDialogFormData,"path",e)},expression:"routeDialogFormData.path"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"状态"}},[a("el-radio-group",{model:{value:t.routeDialogFormData.status,callback:function(e){t.$set(t.routeDialogFormData,"status",e)},expression:"routeDialogFormData.status"}},[a("el-radio",{attrs:{label:1,name:"status"}},[t._v("启用")]),t._v(" "),a("el-radio",{staticStyle:{color:"#F56C6C"},attrs:{label:2,name:"status"}},[t._v("禁用")])],1)],1)],1),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(e){t.routeDialogVisible=!1}}},[t._v("取 消")]),t._v(" "),a("el-button",{attrs:{type:"primary"},on:{click:t.onRouteDialogSave}},[t._v("保 存")])],1)],1),t._v(" "),a("el-dialog",{attrs:{title:"路由授权",visible:t.authDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(e){t.authDialogVisible=e}}},[a("el-form",{attrs:{model:t.authDialogFormData,"label-width":"120px",size:"mini"}},[a("el-form-item",{attrs:{label:"id"}},[a("el-input",{attrs:{readonly:"readonly"},model:{value:t.authDialogFormData.routeId,callback:function(e){t.$set(t.authDialogFormData,"routeId",e)},expression:"authDialogFormData.routeId"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"角色"}},[a("el-checkbox-group",{model:{value:t.authDialogFormData.roleCode,callback:function(e){t.$set(t.authDialogFormData,"roleCode",e)},expression:"authDialogFormData.roleCode"}},t._l(t.roles,function(e){return a("el-checkbox",{key:e.roleCode,attrs:{label:e.roleCode}},[t._v(t._s(e.description))])}),1)],1)],1),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(e){t.authDialogVisible=!1}}},[t._v("取 消")]),t._v(" "),a("el-button",{attrs:{type:"primary"},on:{click:t.onAuthDialogSave}},[t._v("保 存")])],1)],1)],1)],1)],1)},l=[],i={data:function(){return{filterText:"",treeData:[],tableData:[],serviceId:"",searchFormData:{},defaultProps:{children:"children",label:"label"},routeDialogFormData:{status:1},routeDialogVisible:!1,roles:[],authDialogFormData:{routeId:"",roleCode:[]},authDialogVisible:!1}},watch:{filterText:function(t){this.$refs.tree2.filter(t)}},created:function(){this.loadTree(),this.loadRouteRole()},methods:{loadTree:function(){this.post("service.list",{},function(t){var e=t.data;this.treeData=this.convertToTreeData(e,0)})},filterNode:function(t,e){return!t||-1!==e.label.indexOf(t)},onNodeClick:function(t,e,a){t.parentId&&(this.serviceId=t.label,this.searchFormData.serviceId=this.serviceId,this.loadTable())},convertToTreeData:function(t,e){for(var a=[],o={label:"服务列表",parentId:e},l=[],i=0;i0?e.join(", "):'未授权'},onRouteDialogSave:function(){this.routeDialogFormData.serviceId=this.serviceId,this.post("route.update",this.routeDialogFormData,function(){this.routeDialogVisible=!1,this.loadTable()})},onAuthDialogSave:function(){this.post("route.role.update",this.authDialogFormData,function(){console.log(this.authDialogFormData),this.authDialogVisible=!1,this.loadTable()})}}},r=i,s=a("2877"),n=Object(s["a"])(r,o,l,!1,null,null,null);e["default"]=n.exports}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.5ba26eff.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.5ba26eff.js deleted file mode 100644 index 046978b8..00000000 --- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0aa95b.5ba26eff.js +++ /dev/null @@ -1 +0,0 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0aa95b"],{1248:function(t,e,a){"use strict";a.r(e);var o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-container",[a("el-aside",{staticStyle:{"min-height":"300px",width:"200px"}},[a("el-input",{staticStyle:{"margin-bottom":"20px"},attrs:{"prefix-icon":"el-icon-search",placeholder:"搜索服务...",size:"mini",clearable:""},model:{value:t.filterText,callback:function(e){t.filterText=e},expression:"filterText"}}),t._v(" "),a("el-tree",{ref:"tree2",staticClass:"filter-tree",attrs:{data:t.treeData,props:t.defaultProps,"filter-node-method":t.filterNode,"highlight-current":!0,"expand-on-click-node":!1,"empty-text":"无数据","node-key":"id","default-expand-all":""},on:{"node-click":t.onNodeClick},scopedSlots:t._u([{key:"default",fn:function(e){e.node;var o=e.data;return a("span",{staticClass:"custom-tree-node"},[o.label.length<15?a("span",[t._v(t._s(o.label))]):a("span",[a("el-tooltip",{staticClass:"item",attrs:{content:o.label,effect:"light",placement:"right"}},[a("span",[t._v(t._s(o.label.substring(0,15)+"..."))])])],1)])}}])})],1),t._v(" "),a("el-main",{staticStyle:{"padding-top":"0"}},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"路由名称"}},[a("el-input",{attrs:{placeholder:"输入接口名或版本号"},model:{value:t.searchFormData.id,callback:function(e){t.$set(t.searchFormData,"id",e)},expression:"searchFormData.id"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),a("el-table",{attrs:{data:t.tableData,border:"","max-height":"500"}},[a("el-table-column",{attrs:{prop:"name",label:"接口名 (版本号)",width:"200"},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:"uri",label:"LoadBalance",width:"350"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v("\n "+t._s(e.row.uri+e.row.path)+"\n ")]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"roles",label:"访问权限",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[a("span",{domProps:{innerHTML:t._s(t.roleRender(e.row))}})]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"ignoreValidate",label:"忽略验证",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[1===e.row.ignoreValidate?a("span",{staticStyle:{color:"#67C23A"}},[t._v("是")]):t._e(),t._v(" "),0===e.row.ignoreValidate?a("span",{staticStyle:{color:"#909399"}},[t._v("否")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"mergeResult",label:"合并结果",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[1===e.row.mergeResult?a("span",{staticStyle:{color:"#67C23A"}},[t._v("合并")]):t._e(),t._v(" "),0===e.row.mergeResult?a("span",{staticStyle:{color:"#E6A23C"}},[t._v("不合并")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"status",label:"状态",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[0===e.row.status?a("span",{staticStyle:{color:"#909399"}},[t._v("待审核")]):t._e(),t._v(" "),1===e.row.status?a("span",{staticStyle:{color:"#67C23A"}},[t._v("已启用")]):t._e(),t._v(" "),2===e.row.status?a("span",{staticStyle:{color:"#F56C6C"}},[t._v("已禁用")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{label:"操作",fixed:"right",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onTableUpdate(e.row)}}},[t._v("修改")]),t._v(" "),e.row.permission?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onTableAuth(e.row)}}},[t._v("授权")]):t._e()]}}])})],1),t._v(" "),a("el-dialog",{attrs:{title:"修改路由",visible:t.routeDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(e){t.routeDialogVisible=e}}},[a("el-form",{attrs:{model:t.routeDialogFormData,"label-width":"120px",size:"mini"}},[a("el-form-item",{attrs:{label:"id"}},[a("el-input",{attrs:{readonly:"readonly"},model:{value:t.routeDialogFormData.id,callback:function(e){t.$set(t.routeDialogFormData,"id",e)},expression:"routeDialogFormData.id"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"uri"}},[a("el-input",{model:{value:t.routeDialogFormData.uri,callback:function(e){t.$set(t.routeDialogFormData,"uri",e)},expression:"routeDialogFormData.uri"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"path"}},[a("el-input",{model:{value:t.routeDialogFormData.path,callback:function(e){t.$set(t.routeDialogFormData,"path",e)},expression:"routeDialogFormData.path"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"状态"}},[a("el-radio-group",{model:{value:t.routeDialogFormData.status,callback:function(e){t.$set(t.routeDialogFormData,"status",e)},expression:"routeDialogFormData.status"}},[a("el-radio",{attrs:{label:1,name:"status"}},[t._v("启用")]),t._v(" "),a("el-radio",{staticStyle:{color:"#F56C6C"},attrs:{label:2,name:"status"}},[t._v("禁用")])],1)],1)],1),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(e){t.routeDialogVisible=!1}}},[t._v("取 消")]),t._v(" "),a("el-button",{attrs:{type:"primary"},on:{click:t.onRouteDialogSave}},[t._v("保 存")])],1)],1),t._v(" "),a("el-dialog",{attrs:{title:"路由授权",visible:t.authDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(e){t.authDialogVisible=e}}},[a("el-form",{attrs:{model:t.authDialogFormData,"label-width":"120px",size:"mini"}},[a("el-form-item",{attrs:{label:"id"}},[a("el-input",{attrs:{readonly:"readonly"},model:{value:t.authDialogFormData.routeId,callback:function(e){t.$set(t.authDialogFormData,"routeId",e)},expression:"authDialogFormData.routeId"}})],1),t._v(" "),a("el-form-item",{attrs:{label:"角色"}},[a("el-checkbox-group",{model:{value:t.authDialogFormData.roleCode,callback:function(e){t.$set(t.authDialogFormData,"roleCode",e)},expression:"authDialogFormData.roleCode"}},t._l(t.roles,function(e){return a("el-checkbox",{key:e.roleCode,attrs:{label:e.roleCode}},[t._v(t._s(e.description))])}),1)],1)],1),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(e){t.authDialogVisible=!1}}},[t._v("取 消")]),t._v(" "),a("el-button",{attrs:{type:"primary"},on:{click:t.onAuthDialogSave}},[t._v("保 存")])],1)],1)],1)],1)],1)},l=[],i={data:function(){return{filterText:"",treeData:[],tableData:[],serviceId:"",searchFormData:{},defaultProps:{children:"children",label:"label"},routeDialogFormData:{status:1},routeDialogVisible:!1,roles:[],authDialogFormData:{routeId:"",roleCode:[]},authDialogVisible:!1}},watch:{filterText:function(t){this.$refs.tree2.filter(t)}},created:function(){this.loadTree(),this.loadRouteRole()},methods:{loadTree:function(){this.post("service.list",{},function(t){var e=t.data;this.treeData=this.convertToTreeData(e,0)})},filterNode:function(t,e){return!t||-1!==e.label.indexOf(t)},onNodeClick:function(t,e,a){t.parentId&&(this.serviceId=t.label,this.searchFormData.serviceId=this.serviceId,this.loadTable())},convertToTreeData:function(t,e){for(var a=[],o={label:"服务列表",parentId:e},l=[],i=0;i0?e.join(", "):'未授权'},onRouteDialogSave:function(){this.routeDialogFormData.serviceId=this.serviceId,this.post("route.update",this.routeDialogFormData,function(){this.routeDialogVisible=!1,this.loadTable()})},onAuthDialogSave:function(){this.post("route.role.update",this.authDialogFormData,function(){console.log(this.authDialogFormData),this.authDialogVisible=!1,this.loadTable()})}}},r=i,s=a("2877"),n=Object(s["a"])(r,o,l,!1,null,null,null);e["default"]=n.exports}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d22c2e3.0cbb7297.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d22c2e3.46f37153.js similarity index 79% rename from sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d22c2e3.0cbb7297.js rename to sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d22c2e3.46f37153.js index b71661b0..ef15b9c1 100644 --- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d22c2e3.0cbb7297.js +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d22c2e3.46f37153.js @@ -1 +1 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22c2e3"],{f1ac:function(t,e,a){"use strict";a.r(e);var n=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:"serviceId"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),a("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[a("el-table-column",{attrs:{prop:"name",label:"服务名称(serviceId)",width:"200"}}),t._v(" "),a("el-table-column",{attrs:{prop:"instanceId",label:"instanceId",width:"250"}}),t._v(" "),a("el-table-column",{attrs:{prop:"ipAddr",label:"IP地址",width:"150"}}),t._v(" "),a("el-table-column",{attrs:{prop:"serverPort",label:"端口号",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?a("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?a("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?a("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),a("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(a("7f7f"),a("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var a=e.parentId;0===a||t.forEach(function(t){if(t.id===a){var n=t.child;n||(n=[]),n.push(e),t.children=n}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.offline",a,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.online",a,function(){this.tip("上线成功"),e()})})}}}),s=i,o=a("2877"),l=Object(o["a"])(s,n,r,!1,null,null,null);e["default"]=l.exports}}]); \ No newline at end of file +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22c2e3"],{f1ac:function(t,e,a){"use strict";a.r(e);var n=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:"serviceId"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),a("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[a("el-table-column",{attrs:{prop:"name",label:"服务名称(serviceId)",width:"200"}}),t._v(" "),a("el-table-column",{attrs:{prop:"instanceId",label:"instanceId",width:"250"}}),t._v(" "),a("el-table-column",{attrs:{prop:"ipAddr",label:"IP地址",width:"150"}}),t._v(" "),a("el-table-column",{attrs:{prop:"serverPort",label:"端口号",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?a("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?a("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?a("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),a("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(a("7f7f"),a("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var a=e.parentId;0===a||t.forEach(function(t){if(t.id===a){var n=t.children;n||(n=[]),n.push(e),t.children=n}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.offline",a,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.online",a,function(){this.tip("上线成功"),e()})})}}}),s=i,o=a("2877"),l=Object(o["a"])(s,n,r,!1,null,null,null);e["default"]=l.exports}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-4a59cbe4.3309de97.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-4a59cbe4.3309de97.js new file mode 100644 index 00000000..36b45c21 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-4a59cbe4.3309de97.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-4a59cbe4"],{"02f4":function(t,e,r){var n=r("4588"),i=r("be13");t.exports=function(t){return function(e,r){var o,a,l=String(i(e)),c=n(r),s=l.length;return c<0||c>=s?t?"":void 0:(o=l.charCodeAt(c),o<55296||o>56319||c+1===s||(a=l.charCodeAt(c+1))<56320||a>57343?t?l.charAt(c):o:t?l.slice(c,c+2):a-56320+(o-55296<<10)+65536)}}},"0390":function(t,e,r){"use strict";var n=r("02f4")(!0);t.exports=function(t,e,r){return e+(r?n(t,e).length:1)}},"0a49":function(t,e,r){var n=r("9b43"),i=r("626a"),o=r("4bf8"),a=r("9def"),l=r("cd1c");t.exports=function(t,e){var r=1==t,c=2==t,s=3==t,u=4==t,f=6==t,d=5==t||f,v=e||l;return function(e,l,p){for(var g,h,b=o(e),m=i(b),D=n(l,p,3),x=a(m.length),y=0,_=r?v(e,x):c?v(e,0):void 0;x>y;y++)if((d||y in m)&&(g=m[y],h=D(g,y,b),t))if(r)_[y]=h;else if(h)switch(t){case 3:return!0;case 5:return g;case 6:return y;case 2:_.push(g)}else if(u)return!1;return f?-1:s||u?u:_}}},"0bfb":function(t,e,r){"use strict";var n=r("cb7c");t.exports=function(){var t=n(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},1169:function(t,e,r){var n=r("2d95");t.exports=Array.isArray||function(t){return"Array"==n(t)}},"20d6":function(t,e,r){"use strict";var n=r("5ca1"),i=r("0a49")(6),o="findIndex",a=!0;o in[]&&Array(1)[o](function(){a=!1}),n(n.P+n.F*a,"Array",{findIndex:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r("9c6c")(o)},"214f":function(t,e,r){"use strict";r("b0c5");var n=r("2aba"),i=r("32e9"),o=r("79e5"),a=r("be13"),l=r("2b4c"),c=r("520a"),s=l("species"),u=!o(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$
")}),f=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var r="ab".split(t);return 2===r.length&&"a"===r[0]&&"b"===r[1]}();t.exports=function(t,e,r){var d=l(t),v=!o(function(){var e={};return e[d]=function(){return 7},7!=""[t](e)}),p=v?!o(function(){var e=!1,r=/a/;return r.exec=function(){return e=!0,null},"split"===t&&(r.constructor={},r.constructor[s]=function(){return r}),r[d](""),!e}):void 0;if(!v||!p||"replace"===t&&!u||"split"===t&&!f){var g=/./[d],h=r(a,d,""[t],function(t,e,r,n,i){return e.exec===c?v&&!i?{done:!0,value:g.call(e,r,n)}:{done:!0,value:t.call(r,e,n)}:{done:!1}}),b=h[0],m=h[1];n(String.prototype,t,b),i(RegExp.prototype,d,2==e?function(t,e){return m.call(t,this,e)}:function(t){return m.call(t,this)})}}},"520a":function(t,e,r){"use strict";var n=r("0bfb"),i=RegExp.prototype.exec,o=String.prototype.replace,a=i,l="lastIndex",c=function(){var t=/a/,e=/b*/g;return i.call(t,"a"),i.call(e,"a"),0!==t[l]||0!==e[l]}(),s=void 0!==/()??/.exec("")[1],u=c||s;u&&(a=function(t){var e,r,a,u,f=this;return s&&(r=new RegExp("^"+f.source+"$(?!\\s)",n.call(f))),c&&(e=f[l]),a=i.call(f,t),c&&a&&(f[l]=f.global?a.index+a[0].length:e),s&&a&&a.length>1&&o.call(a[0],r,function(){for(u=1;u]*>)/g,v=/\$([$&`']|\d\d?)/g,p=function(t){return void 0===t?t:String(t)};r("214f")("replace",2,function(t,e,r,g){return[function(n,i){var o=t(this),a=void 0==n?void 0:n[e];return void 0!==a?a.call(n,o,i):r.call(String(o),n,i)},function(t,e){var i=g(r,t,this,e);if(i.done)return i.value;var f=n(t),d=String(this),v="function"===typeof e;v||(e=String(e));var b=f.global;if(b){var m=f.unicode;f.lastIndex=0}var D=[];while(1){var x=c(f,d);if(null===x)break;if(D.push(x),!b)break;var y=String(x[0]);""===y&&(f.lastIndex=l(d,o(f.lastIndex),m))}for(var _="",w=0,I=0;I=w&&(_+=d.slice(w,k)+L,w=k+S.length)}return _+d.slice(w)}];function h(t,e,n,o,a,l){var c=n+t.length,s=o.length,u=v;return void 0!==a&&(a=i(a),u=d),r.call(l,u,function(r,i){var l;switch(i.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,n);case"'":return e.slice(c);case"<":l=a[i.slice(1,-1)];break;default:var u=+i;if(0===u)return r;if(u>s){var d=f(u/10);return 0===d?r:d<=s?void 0===o[d-1]?i.charAt(1):o[d-1]+i.charAt(1):r}l=o[u-1]}return void 0===l?"":l})}})},b0c5:function(t,e,r){"use strict";var n=r("520a");r("5ca1")({target:"RegExp",proto:!0,forced:n!==/./.exec},{exec:n})},cd1c:function(t,e,r){var n=r("e853");t.exports=function(t,e){return new(n(t))(e)}},e853:function(t,e,r){var n=r("d3f4"),i=r("1169"),o=r("2b4c")("species");t.exports=function(t){var e;return i(t)&&(e=t.constructor,"function"!=typeof e||e!==Array&&!i(e.prototype)||(e=void 0),n(e)&&(e=e[o],null===e&&(e=void 0))),void 0===e?Array:e}},ec2a:function(t,e,r){"use strict";r.r(e);var n=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"app-container"},[r("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[r("el-form-item",[r("el-button",{attrs:{type:"primary",icon:"el-icon-plus"},on:{click:t.onAddServer}},[t._v("添加监控服务器")])],1)],1),t._v(" "),r("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"treeId","empty-text":"请添加监控服务器"}},[r("el-table-column",{attrs:{prop:"monitorName",label:"网关实例",width:"300"},scopedSlots:t._u([{key:"default",fn:function(e){return[0===e.row.parentId?r("span",[t._v(t._s(e.row.monitorName))]):t._e()]}}])}),t._v(" "),r("el-table-column",{attrs:{prop:"serviceId",label:"serviceId",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0?r("span",[t._v(t._s(e.row.serviceId))]):t._e()]}}])}),t._v(" "),r("el-table-column",{attrs:{prop:"name",label:"接口名 (版本号)",width:"200"},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(" "),r("el-table-column",{attrs:{prop:"count",label:"出错次数",width:"100"}}),t._v(" "),r("el-table-column",{attrs:{prop:"errorMsg",label:"报错信息",width:"300"},scopedSlots:t._u([{key:"default",fn:function(e){return e.row.parentId>0?[r("div",{staticStyle:{display:"inline-block"},domProps:{innerHTML:t._s(t.showErrorMsg(e.row))}}),t._v(" "),r("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(r){return t.onShowErrorDetail(e.row)}}},[t._v("详情")])]:void 0}}],null,!0)}),t._v(" "),r("el-table-column",{attrs:{label:"操作",width:"180"},scopedSlots:t._u([{key:"default",fn:function(e){return[0===e.row.parentId&&e.row.children?r("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(r){return t.onClearLog(e.row)}}},[t._v("清空日志")]):t._e(),t._v(" "),0===e.row.parentId?r("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(r){return t.onDelete(e.row)}}},[t._v("删除实例")]):t._e()]}}])})],1),t._v(" "),r("el-dialog",{attrs:{title:"选择服务器实例",visible:t.logDialogInstanceVisible,"close-on-click-modal":!1},on:{"update:visible":function(e){t.logDialogInstanceVisible=e}}},[r("el-form",{ref:"logDialogForm",attrs:{model:t.logDialogFormData,rules:t.rulesLog,"label-width":"150px",size:"mini"}},[r("el-form-item",[r("p",{staticStyle:{color:"#878787"}},[t._v("只能选择网关实例,其它实例不支持")])]),t._v(" "),r("el-form-item",{attrs:{prop:"instanceData",label:"服务器实例"}},[r("el-select",{staticStyle:{width:"400px"},attrs:{"value-key":"id"},model:{value:t.logDialogFormData.instanceData,callback:function(e){t.$set(t.logDialogFormData,"instanceData",e)},expression:"logDialogFormData.instanceData"}},t._l(t.serviceData,function(e){return r("el-option",{key:e.id,attrs:{label:e.name+"("+e.ipAddr+":"+e.serverPort+")",value:e,disabled:t.isOptionDisabled(e)}},[r("span",{staticStyle:{float:"left"}},[t._v(t._s(e.name)+" "),t.isOptionDisabled(e)?r("span",[t._v("(已添加)")]):t._e()]),t._v(" "),r("span",{staticStyle:{float:"right",color:"#8492a6","font-size":"13px"}},[t._v(t._s(e.ipAddr+":"+e.serverPort))])])}),1)],1)],1),t._v(" "),r("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[r("el-button",{on:{click:function(e){t.logDialogInstanceVisible=!1}}},[t._v("取 消")]),t._v(" "),r("el-button",{attrs:{type:"primary"},on:{click:t.onLogDialogSave}},[t._v("保 存")])],1)],1),t._v(" "),r("el-dialog",{attrs:{title:"错误详情",visible:t.logDetailVisible,width:"60%"},on:{"update:visible":function(e){t.logDetailVisible=e}}},[r("div",{staticStyle:{"overflow-x":"auto"},domProps:{innerHTML:t._s(t.errorMsgDetail)}}),t._v(" "),r("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[r("el-button",{attrs:{type:"primary"},on:{click:function(e){t.logDetailVisible=!1}}},[t._v("关 闭")])],1)])],1)},i=[],o=(r("a481"),r("ac6a"),r("20d6"),{data:function(){return{searchFormData:{},tableData:[],serviceData:[],addedInstanceList:[],logDialogFormData:{instanceData:null},logDialogInstanceVisible:!1,logDetailVisible:!1,rulesLog:{instanceData:[{required:!0,message:"不能为空",trigger:"blur"}]},errorMsgDetail:""}},created:function(){this.loadServiceInstance(),this.loadTable()},methods:{loadServiceInstance:function(){this.post("service.instance.list",{},function(t){this.serviceData=t.data.filter(function(t){return t.instanceId&&t.instanceId.length>0})}),this.post("monitor.instance.list",{},function(t){this.addedInstanceList=t.data})},loadTable:function(){this.post("monitor.log.list",{},function(t){this.tableData=this.buildTreeData(t.data)})},isOptionDisabled:function(t){var e=t.ipAddr+":"+t.serverPort,r=this.addedInstanceList.findIndex(function(t,r,n){return t===e});return r>-1},buildTreeData:function(t){return t.forEach(function(e){var r=e.parentId;0===r||t.forEach(function(t){if(t.treeId===r){var n=t.children;n||(n=[]),n.push(e),t.children=n}})}),t=t.filter(function(t){return 0===t.parentId}),t},showErrorMsg:function(t){var e=t.errorMsg.replace(/\/g,"");return e.substring(0,30)+"..."},onAddServer:function(){this.logDialogInstanceVisible=!0},onDelete:function(t){this.confirm("确定要删除实例【"+t.monitorName+"】吗?",function(e){this.post("monitor.instance.del",{id:t.rawId},function(t){e(),this.tip("删除成功"),this.loadTable()})})},onClearLog:function(t){this.confirm("确定要清空日志吗?",function(e){this.post("monitor.log.clear",{id:t.rawId},function(t){e(),this.tip("清空成功"),this.loadTable()})})},onShowErrorDetail:function(t){this.errorMsgDetail=t.errorMsg,this.logDetailVisible=!0},onLogDialogSave:function(){var t=this;this.$refs["logDialogForm"].validate(function(e){if(e){var r=t.logDialogFormData.instanceData,n={serviceId:r.serviceId,ip:r.ipAddr,port:r.serverPort};t.post("monitor.instance.add",n,function(t){this.logDialogInstanceVisible=!1,this.loadTable()})}})}}}),a=o,l=r("2877"),c=Object(l["a"])(a,n,i,!1,null,null,null);e["default"]=c.exports}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-vue/src/router/index.js b/sop-admin/sop-admin-vue/src/router/index.js index c7556d55..c99d3982 100644 --- a/sop-admin/sop-admin-vue/src/router/index.js +++ b/sop-admin/sop-admin-vue/src/router/index.js @@ -78,6 +78,12 @@ export const constantRoutes = [ name: 'Limit', component: () => import('@/views/service/limit/index2'), meta: { title: '限流管理' } + }, + { + path: 'log', + name: 'Log', + component: () => import('@/views/service/log/index'), + meta: { title: '监控日志' } } ] }, diff --git a/sop-admin/sop-admin-vue/src/utils/global.js b/sop-admin/sop-admin-vue/src/utils/global.js index 35646766..1d3b1bca 100644 --- a/sop-admin/sop-admin-vue/src/utils/global.js +++ b/sop-admin/sop-admin-vue/src/utils/global.js @@ -35,7 +35,7 @@ Object.assign(Vue.prototype, { const resp = response.data const code = resp.code if (!code || code === '-9') { - that.$message.error('系统错误') + that.$message.error(resp.msg || '系统错误') return } if (code === '-100' || code === '18' || code === '21') { // 未登录 @@ -96,6 +96,9 @@ Object.assign(Vue.prototype, { }, logout: function() { removeToken() - this.$router.push({ path: `/login?redirect=${this.$route.fullPath}` }) + const fullPath = this.$route.fullPath + if (fullPath.indexOf('login?redirect') === -1) { + this.$router.push({ path: `/login?redirect=${fullPath}` }) + } } }) diff --git a/sop-admin/sop-admin-vue/src/views/service/list/index.vue b/sop-admin/sop-admin-vue/src/views/service/list/index.vue index e712e7d3..511c2052 100644 --- a/sop-admin/sop-admin-vue/src/views/service/list/index.vue +++ b/sop-admin/sop-admin-vue/src/views/service/list/index.vue @@ -92,7 +92,7 @@ export default { // 如果ele是子元素的话 ,把ele扔到他的父亲的child数组中. data.forEach(d => { if (d.id === parentId) { - let childArray = d.child + let childArray = d.children if (!childArray) { childArray = [] } diff --git a/sop-admin/sop-admin-vue/src/views/service/log/index.vue b/sop-admin/sop-admin-vue/src/views/service/log/index.vue new file mode 100644 index 00000000..1b829abd --- /dev/null +++ b/sop-admin/sop-admin-vue/src/views/service/log/index.vue @@ -0,0 +1,234 @@ + + + diff --git a/sop-admin/sop-admin-vue/src/views/service/route/index.vue b/sop-admin/sop-admin-vue/src/views/service/route/index.vue index 28d8cf26..8fbb4f74 100644 --- a/sop-admin/sop-admin-vue/src/views/service/route/index.vue +++ b/sop-admin/sop-admin-vue/src/views/service/route/index.vue @@ -79,12 +79,12 @@ appSecretPair) { for (Map.Entry entry : appSecretPair.entrySet()) { this.isvManager.update(new IsvDefinition(entry.getKey(), entry.getValue())); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java new file mode 100644 index 00000000..9b974a80 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorDefinition.java @@ -0,0 +1,15 @@ +package com.gitee.sop.gatewaycommon.bean; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class ErrorDefinition { + private String name; + private String version; + private String serviceId; + private String errorMsg; + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java new file mode 100644 index 00000000..df6b4640 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ErrorEntity.java @@ -0,0 +1,32 @@ +package com.gitee.sop.gatewaycommon.bean; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.Objects; + +@Getter +@Setter +@ToString +public class ErrorEntity { + private String id; + private String name; + private String version; + private String serviceId; + private String errorMsg; + private long count; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ErrorEntity that = (ErrorEntity) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java index 3ce20984..2f824745 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java @@ -33,7 +33,9 @@ public class SopConstants { public static final String CACHE_API_PARAM = "cacheApiParam"; - public static final String X_BIZ_ERROR_CODE = "x-biz-error-code"; + public static final String X_SERVICE_ERROR_CODE = "x-service-error-code"; + + public static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message"; public static final int BIZ_ERROR_STATUS = 4000; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java index be4bb9b9..884e013a 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java @@ -28,7 +28,7 @@ public class GatewayResultExecutor extends BaseExecutorAdapter errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_BIZ_ERROR_CODE); + List errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_SERVICE_ERROR_CODE); if (!CollectionUtils.isEmpty(errorCodeList)) { String errorCode = errorCodeList.get(0); responseStatus = Integer.valueOf(errorCode); @@ -37,7 +37,18 @@ public class GatewayResultExecutor extends BaseExecutorAdapter getApiParam(ServerWebExchange exchange) { + public String getResponseErrorMessage(ServerWebExchange exchange) { + String errorMsg = null; + List errorMessageList = exchange.getResponse().getHeaders().get(SopConstants.X_SERVICE_ERROR_MESSAGE); + if (!CollectionUtils.isEmpty(errorMessageList)) { + errorMsg = errorMessageList.get(0); + } + exchange.getResponse().getHeaders().remove(SopConstants.X_SERVICE_ERROR_MESSAGE); + return errorMsg; + } + + @Override + public Map getApiParam(ServerWebExchange exchange) { return exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java new file mode 100644 index 00000000..56d1bddf --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultServiceErrorManager.java @@ -0,0 +1,51 @@ +package com.gitee.sop.gatewaycommon.manager; + +import com.gitee.sop.gatewaycommon.bean.ApiConfig; +import com.gitee.sop.gatewaycommon.bean.ErrorDefinition; +import com.gitee.sop.gatewaycommon.bean.ErrorEntity; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.BeanUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author tanghc + */ +public class DefaultServiceErrorManager implements ServiceErrorManager { + + private static Map store = new ConcurrentHashMap<>(128); + + @Override + public Collection listAllErrors() { + return store.values(); + } + + @Override + public void saveError(ErrorDefinition errorDefinition) { + boolean hasCapacity = store.size() < ApiConfig.getInstance().getStoreErrorCapacity(); + // 这里还可以做其它事情,比如错误量到达一定数目后,自动发送邮件/微信给开发人员,方便及时获取异常情况 + String id = this.buildId(errorDefinition); + ErrorEntity errorEntity = store.get(id); + if (errorEntity == null && hasCapacity) { + errorEntity = new ErrorEntity(); + BeanUtils.copyProperties(errorDefinition, errorEntity); + errorEntity.setId(id); + store.put(id, errorEntity); + } + if (errorEntity != null) { + errorEntity.setCount(errorEntity.getCount() + 1); + } + } + + @Override + public void clear() { + store.clear(); + } + + protected String buildId(ErrorDefinition errorDefinition) { + return DigestUtils.md5Hex(errorDefinition.getServiceId() + errorDefinition.getErrorMsg()); + } + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java new file mode 100644 index 00000000..8528d4b7 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ServiceErrorManager.java @@ -0,0 +1,29 @@ +package com.gitee.sop.gatewaycommon.manager; + +import com.gitee.sop.gatewaycommon.bean.ErrorDefinition; +import com.gitee.sop.gatewaycommon.bean.ErrorEntity; + +import java.util.Collection; + +/** + * @author tanghc + */ +public interface ServiceErrorManager { + + /** + * 保存错误信息 + * @param errorDefinition + */ + void saveError(ErrorDefinition errorDefinition); + + /** + * 清除日志 + */ + void clear(); + + /** + * 获取所有错误信息 + * @return + */ + Collection listAllErrors(); +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java index 0ccd165c..50a5ea90 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java @@ -29,6 +29,14 @@ public class ApiParam extends JSONObject implements Param { private transient ApiUploadContext apiUploadContext; + public static ApiParam build(Map map) { + ApiParam apiParam = new ApiParam(); + for (Map.Entry entry : map.entrySet()) { + apiParam.put(entry.getKey(), entry.getValue()); + } + return apiParam; + } + @JSONField(serialize = false) public String getRouteId() { return this.fetchNameVersion(); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java index e360c493..780fc514 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java @@ -4,17 +4,24 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ApiContext; +import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition; +import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo; +import com.gitee.sop.gatewaycommon.bean.ErrorDefinition; import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext; import com.gitee.sop.gatewaycommon.message.ErrorEnum; import com.gitee.sop.gatewaycommon.message.ErrorMeta; import com.gitee.sop.gatewaycommon.param.ParamNames; +import lombok.Getter; +import lombok.Setter; import org.apache.commons.lang.BooleanUtils; +import org.springframework.beans.BeanUtils; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; import java.util.Map; +import java.util.Optional; /** * @author tanghc @@ -33,17 +40,27 @@ public abstract class BaseExecutorAdapter implements ResultExecutor /** * 获取业务方约定的返回码 + * * @param t * @return 返回返回码 */ public abstract int getResponseStatus(T t); + /** + * 获取微服务端的错误信息 + * + * @param t + * @return + */ + public abstract String getResponseErrorMessage(T t); + /** * 返回Api参数 + * * @param t * @return 返回api参数 */ - public abstract Map getApiParam(T t); + public abstract Map getApiParam(T t); @Override public String mergeResult(T request, String serviceResult) { @@ -65,6 +82,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode()); jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg()); } else { + this.storeError(request); // 微服务端有可能返回500错误 // {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500} jsonObjectService = new JSONObject(); @@ -74,8 +92,24 @@ public abstract class BaseExecutorAdapter implements ResultExecutor return this.merge(request, jsonObjectService); } + /** + * 保存错误信息 + * + * @param request + */ + protected void storeError(T request) { + ApiInfo apiInfo = this.getApiInfo(request); + String errorMsg = this.getResponseErrorMessage(request); + ErrorDefinition errorDefinition = new ErrorDefinition(); + BeanUtils.copyProperties(apiInfo, errorDefinition); + errorDefinition.setErrorMsg(errorMsg); + ApiConfig.getInstance().getServiceErrorManager().saveError(errorDefinition); + } + + /** * 该路由是否合并结果 + * * @param request * @return true:需要合并 */ @@ -85,22 +119,43 @@ public abstract class BaseExecutorAdapter implements ResultExecutor if (defaultSetting != null) { return defaultSetting; } - Map params = this.getApiParam(request); - if (params == null) { - return true; - } - Object name = params.get(ParamNames.API_NAME); - Object version = params.get(ParamNames.VERSION_NAME); - if(name == null || version == null) { - return true; - } - TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(String.valueOf(name) + version); - if (targetRoute == null) { - return true; - } else { - int mergeResult = targetRoute.getRouteDefinition().getMergeResult(); - return BooleanUtils.toBoolean(mergeResult); - } + ApiInfo apiInfo = this.getApiInfo(request); + BaseRouteDefinition baseRouteDefinition = apiInfo.baseRouteDefinition; + return Optional.ofNullable(baseRouteDefinition) + .map(routeDefinition -> { + int mergeResult = baseRouteDefinition.getMergeResult(); + return BooleanUtils.toBoolean(mergeResult); + }) + .orElse(true); + } + + protected ApiInfo getApiInfo(T request) { + Map params = this.getApiParam(request); + String name = Optional.ofNullable(params) + .map(map -> (String) map.get(ParamNames.API_NAME)) + .orElse("method.unknown"); + + String version = Optional.ofNullable(params) + .map(map -> (String) map.get(ParamNames.VERSION_NAME)) + .orElse("version.unknown"); + + TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(name + version); + + String serviceId = Optional.ofNullable(targetRoute) + .flatMap(route -> Optional.ofNullable(route.getServiceRouteInfo())) + .map(BaseServiceRouteInfo::getServiceId) + .orElse("serviceId.unknown"); + + BaseRouteDefinition baseRouteDefinition = Optional.ofNullable(targetRoute) + .map(route -> route.getRouteDefinition()) + .orElse(null); + + ApiInfo apiInfo = new ApiInfo(); + apiInfo.name = name; + apiInfo.version = version; + apiInfo.serviceId = serviceId; + apiInfo.baseRouteDefinition = baseRouteDefinition; + return apiInfo; } protected String wrapResult(String serviceResult) { @@ -150,6 +205,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor /** * 这里需要使用平台的私钥生成一个sign,需要配置两套公私钥。目前暂未实现 + * * @param appKey * @return */ @@ -158,4 +214,13 @@ public abstract class BaseExecutorAdapter implements ResultExecutor return null; } + @Getter + @Setter + protected static class ApiInfo { + private String name; + private String version; + private String serviceId; + private BaseRouteDefinition baseRouteDefinition; + } + } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java new file mode 100644 index 00000000..a5786b81 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/JsonResult.java @@ -0,0 +1,13 @@ +package com.gitee.sop.gatewaycommon.result; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author tanghc + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class JsonResult extends ApiResult { + private Object data; +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java index 233d0bae..aee316f7 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/result/ZuulResultExecutor.java @@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.Map; -import java.util.Optional; /** * @author tanghc @@ -26,23 +25,36 @@ public class ZuulResultExecutor extends BaseExecutorAdapter> bizHeaders = requestContext.getZuulResponseHeaders(); - Optional first = bizHeaders.stream() - .filter(header -> { - return SopConstants.X_BIZ_ERROR_CODE.equals(header.first()); - }).map(header -> { - return header.second(); - }).findFirst(); + int status = bizHeaders.stream() + .filter(header -> SopConstants.X_SERVICE_ERROR_CODE.equals(header.first())) + .map(header -> Integer.valueOf(header.second())) + .findFirst() + .orElse(requestContext.getResponseStatusCode()); - String status = first.orElseGet(() -> { - int respStatus = requestContext.getResponseStatusCode(); - return String.valueOf(respStatus); - }); + return status; + } - return Integer.valueOf(status); + @Override + public String getResponseErrorMessage(RequestContext requestContext) { + List> bizHeaders = requestContext.getZuulResponseHeaders(); + int index = -1; + String errorMsg = null; + for (int i = 0; i < bizHeaders.size(); i++) { + Pair header = bizHeaders.get(i); + if (SopConstants.X_SERVICE_ERROR_MESSAGE.equals(header.first())) { + errorMsg = header.second(); + index = i; + break; + } + } + if (index > -1) { + requestContext.getZuulResponseHeaders().remove(index); + } + return errorMsg; } @Override - public Map getApiParam(RequestContext requestContext) { + public Map getApiParam(RequestContext requestContext) { return ZuulContext.getApiParam(); } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java index 47b9e9be..c3103b69 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/DefaultGlobalExceptionHandler.java @@ -20,6 +20,17 @@ import javax.servlet.http.HttpServletResponse; @Slf4j public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { + /** + * 与网关约定好的状态码,表示业务出错 + */ + public static final int BIZ_ERROR_CODE = 4000; + /** + * 系统错误 + */ + public static final int SYSTEM_ERROR_CODE = 5050; + + public static final String X_SERVICE_ERROR_CODE = "x-service-error-code"; + @RequestMapping("/error") @ResponseBody public Object error(HttpServletRequest request, HttpServletResponse response) { @@ -27,16 +38,42 @@ public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { return serviceResultBuilder.buildError(request, response, new ServiceException("系统繁忙")); } + /** + * 捕获手动抛出的异常 + * @param request + * @param response + * @param exception + * @return + */ @ExceptionHandler(ServiceException.class) @ResponseBody public Object serviceExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { + response.addHeader(X_SERVICE_ERROR_CODE, String.valueOf(BIZ_ERROR_CODE)); return this.processError(request, response, exception); } + /** + * 捕获未知异常 + * @param request + * @param response + * @param exception + * @return + */ @ExceptionHandler(Exception.class) @ResponseBody public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) { + response.addHeader(X_SERVICE_ERROR_CODE, String.valueOf(SYSTEM_ERROR_CODE)); log.error("系统错误", exception); + StringBuilder msg = new StringBuilder(); + msg.append(exception.getMessage()); + StackTraceElement[] stackTrace = exception.getStackTrace(); + // 取5行错误内容 + int lineCount = 5; + for (int i = 0; i < stackTrace.length && i < lineCount ; i++) { + StackTraceElement stackTraceElement = stackTrace[i]; + msg.append("
at ").append(stackTraceElement.toString()); + } + response.setHeader("x-service-error-message", msg.toString()); return this.processError(request, response, new ServiceException("系统繁忙")); } diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java index cab2072d..a353f5fd 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java @@ -16,15 +16,9 @@ import javax.servlet.http.HttpServletResponse; public class DefaultServiceResultBuilder implements ServiceResultBuilder { public static final String ISP_UNKNOWN_ERROR = "isp.unknown-error"; - /** - * 与网关约定好的状态码,表示业务出错 - */ - public static final int BIZ_ERROR_CODE = 4000; - public static final String X_BIZ_ERROR_CODE = "x-biz-error-code"; @Override public Object buildError(HttpServletRequest request, HttpServletResponse response, Throwable throwable) { - response.addHeader(X_BIZ_ERROR_CODE, String.valueOf(BIZ_ERROR_CODE)); String subCode, subMsg; if (throwable instanceof ServiceException) { ServiceException ex = (ServiceException) throwable; diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java index e96fefe9..e87b22bf 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java @@ -41,6 +41,9 @@ public class ZuulConfig extends AlipayZuulConfiguration { //public class ZuulConfig extends EasyopenZuulConfiguration { // static { // new ManagerInitializer(); +// Map appSecretMap = new HashMap<>(); +// appSecretMap.put("easyopen_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA"); +// ApiConfig.getInstance().addAppSecret(appSecretMap); // } // //} diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/ErrorLogController.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/ErrorLogController.java new file mode 100644 index 00000000..5d7670fc --- /dev/null +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/ErrorLogController.java @@ -0,0 +1,68 @@ +package com.gitee.sop.gateway.controller; + +import com.gitee.sop.gatewaycommon.bean.ApiConfig; +import com.gitee.sop.gatewaycommon.bean.ErrorEntity; +import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.result.ApiResult; +import com.gitee.sop.gatewaycommon.result.JsonResult; +import com.gitee.sop.gatewaycommon.util.RequestUtil; +import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collection; +import java.util.Map; + +/** + * @author tanghc + */ +@RestController +public class ErrorLogController { + + TaobaoSigner signer = new TaobaoSigner(); + + @Value("${zuul.secret}") + private String secret; + + @GetMapping("listErrors") + public ApiResult listErrors(HttpServletRequest request) { + try { + this.check(request); + ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); + Collection allErrors = serviceErrorManager.listAllErrors(); + JsonResult apiResult = new JsonResult(); + apiResult.setData(allErrors); + return apiResult; + } catch (Exception e) { + ApiResult apiResult = new ApiResult(); + apiResult.setCode("505050"); + apiResult.setMsg(e.getMessage()); + return apiResult; + } + } + + @GetMapping("clearErrors") + public ApiResult clearErrors(HttpServletRequest request) { + try { + this.check(request); + ServiceErrorManager serviceErrorManager = ApiConfig.getInstance().getServiceErrorManager(); + serviceErrorManager.clear(); + return new ApiResult(); + } catch (Exception e) { + ApiResult apiResult = new ApiResult(); + apiResult.setCode("505050"); + apiResult.setMsg(e.getMessage()); + return apiResult; + } + } + + private void check(HttpServletRequest request) { + Map params = RequestUtil.convertRequestParamsToMap(request); + ApiParam apiParam = ApiParam.build(params); + signer.checkSign(apiParam, secret); + } + +} diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/RedirectController.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java similarity index 95% rename from sop-gateway/src/main/java/com/gitee/sop/gateway/RedirectController.java rename to sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java index 2c8c26c0..3127d28f 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/RedirectController.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/controller/RedirectController.java @@ -1,4 +1,4 @@ -package com.gitee.sop.gateway; +package com.gitee.sop.gateway.controller; import com.gitee.sop.gatewaycommon.bean.SopConstants; import org.springframework.beans.factory.annotation.Value; diff --git a/sop-gateway/src/main/resources/application-dev.yml b/sop-gateway/src/main/resources/application-dev.yml index 789eb0a4..cbb973a9 100644 --- a/sop-gateway/src/main/resources/application-dev.yml +++ b/sop-gateway/src/main/resources/application-dev.yml @@ -11,6 +11,8 @@ zuul: Servlet30WrapperFilter: pre: disable: true + # 不用改,如果要改,建议全局替换修改 + secret: MZZOUSTua6LzApIWXCwEgbBmxSzpzC # 注册中心,根据实际情况修改 eureka: diff --git a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java index dd5eb120..d4a3331e 100644 --- a/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java +++ b/sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java @@ -28,7 +28,7 @@ public class WebsiteConfig implements ApplicationRunner { public HttpMessageConverters fastJsonConfigure(){ FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); - fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullStringAsEmpty); + fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue); // 日期格式化 fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); converter.setFastJsonConfig(fastJsonConfig); diff --git a/sop.sql b/sop.sql index 59814eb8..15c2a6eb 100644 --- a/sop.sql +++ b/sop.sql @@ -12,6 +12,7 @@ DROP TABLE IF EXISTS `config_route_limit`; DROP TABLE IF EXISTS `config_route_base`; DROP TABLE IF EXISTS `config_limit`; DROP TABLE IF EXISTS `admin_user_info`; +DROP TABLE IF EXISTS `config_common`; CREATE TABLE `admin_user_info` ( @@ -134,6 +135,17 @@ CREATE TABLE `user_info` ( ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户信息表'; +CREATE TABLE `config_common` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `config_group` varchar(64) NOT NULL DEFAULT '' COMMENT '配置分组', + `config_key` varchar(64) NOT NULL DEFAULT '' COMMENT '配置key', + `content` varchar(128) NOT NULL DEFAULT '' COMMENT '内容', + `remark` varchar(128) DEFAULT NULL COMMENT '备注', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_groupkey` (`config_group`,`config_key`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='通用配置表'; SET FOREIGN_KEY_CHECKS = @PREVIOUS_FOREIGN_KEY_CHECKS;