Compare commits

..

143 Commits

Author SHA1 Message Date
EricZeng
045f65204b Merge pull request #633 from didi/master
合并主分支
2022-09-29 13:09:19 +08:00
EricZeng
f414b47a78 1、补充升级至v3.0.0信息;2、增加v3.0.0变更内容;(#636)
1、补充升级至v3.0.0信息;2、增加v3.0.0变更内容;(#)
2022-09-29 13:08:32 +08:00
EricZeng
44f4e2f0f9 Merge pull request #635 from didi/dev
合并前端调整的内容
2022-09-29 11:50:25 +08:00
zengqiao
2361008bdf Merge branch 'dev' of github.com:didi/KnowStreaming into dev 2022-09-29 11:49:00 +08:00
zengqiao
7377ef3ec5 增加v3.0.0变更内容 2022-09-29 11:45:29 +08:00
lucasun
a28d064b7a Merge pull request #634 from GraceWalk/dev
前端 bug 修复 & 问题优化
2022-09-29 11:23:25 +08:00
GraceWalk
e2e57e8575 fix: 依赖版本更新 2022-09-29 11:15:47 +08:00
zengqiao
9d90bd2835 补充升级至v3.0.0信息 2022-09-29 11:04:49 +08:00
EricZeng
7445e68df4 Merge pull request #632 from didi/master
合并主分支
2022-09-29 10:53:54 +08:00
GraceWalk
ab42625ad2 fix: 数字展示格式化 2022-09-29 10:52:31 +08:00
GraceWalk
18789a0a53 fix: IconFont 组件改为从独立包引入 2022-09-29 10:51:52 +08:00
zengqiao
68a37bb56a Merge branch 'master' of github.com:didi/KnowStreaming 2022-09-29 10:49:46 +08:00
GraceWalk
3b33652c47 fix: Rebalance 卡片 icon 颜色调整 2022-09-29 10:48:52 +08:00
GraceWalk
1e0c4c3904 feat: Topic 详情消息 Value 列支持复制 2022-09-29 10:48:09 +08:00
zengqiao
04e223de16 修改协议文案 2022-09-29 10:48:00 +08:00
GraceWalk
c4a691aa8a fix: 多集群列表兼容集群无 ZK 情况 2022-09-29 10:44:28 +08:00
GraceWalk
ff9dde163a feat: 图表支持存储拖拽排序 & 补点逻辑优化 2022-09-29 10:42:44 +08:00
EricZeng
eb7efbd1a5 增加字段校验注解(#631)
增加字段校验注解(#631)
2022-09-29 09:59:02 +08:00
zengqiao
8c8c362c54 Merge branch 'dev' of github.com:didi/KnowStreaming into dev 2022-09-28 20:19:35 +08:00
zengqiao
66e119ad5d 增加字段校验注解 2022-09-28 20:16:06 +08:00
EricZeng
6dedc04a05 Merge pull request #630 from didi/dev
合并开发分支
2022-09-28 20:14:51 +08:00
EricZeng
0cf8bad0df Merge pull request #629 from didi/master
合并主分支
2022-09-28 20:06:26 +08:00
zengqiao
95c9582d8b 优化消费组详情指标为实时获取 2022-09-28 20:03:23 +08:00
EricZeng
7815126ff5 1、修复Group指标防重复不生效问题;2、修复自动创建ES索引模版失败问题; (#628)
* 修复自动创建ES索引模版失败问题

* 修复Group指标防重复不生效问题

Co-authored-by: zengqiao <zengqiao@didiglobal.com>
2022-09-28 19:55:30 +08:00
zengqiao
a5fa9de54b 修复Group指标防重复不生效问题 2022-09-28 19:52:11 +08:00
zengqiao
95f1a2c630 修复自动创建ES索引模版失败问题 2022-09-28 19:46:07 +08:00
zengqiao
1e256ae1fd 修复自动创建ES索引模版失败问题 2022-09-28 19:44:33 +08:00
zengqiao
9fc9c54fa1 bump version to 3.0.0 2022-09-28 11:20:16 +08:00
zengqiao
1b362b1e02 Merge branch 'master' of github.com:didi/KnowStreaming 2022-09-28 11:16:54 +08:00
EricZeng
04e3172cca [ISSUE-624]过滤掉不存在的Topic(#625)
[ISSUE-624]过滤掉不存在的Topic(#625)
2022-09-28 11:13:15 +08:00
EricZeng
1caab7f3f7 [ISSUE-624]过滤掉不存在的Topic(#624)
[ISSUE-624]过滤掉不存在的Topic(#624)
2022-09-28 10:41:39 +08:00
zengqiao
9d33c725ad [ISSUE-624]过滤掉不存在的Topic(#624)
同步Group元信息时,如果Topic已经不存在了,则过滤掉该Group+Topic信息
2022-09-28 10:39:33 +08:00
EricZeng
6ed1d38106 [ISSUE-598]Fix start_time not set when create reassign job in MySQL-8 (#623)
[ISSUE-598]Fix start_time not set when create reassign job in MySQL-8 (#623 )
2022-09-28 10:26:56 +08:00
zengqiao
0f07ddedaf [ISSUE-598]Fix start_time not set when create reassign job in MySQL-8 2022-09-28 10:24:32 +08:00
EricZeng
289945b471 Merge pull request #622 from didi/dev
后端补充Kafka集群运行模式字段信息
2022-09-28 10:08:17 +08:00
zengqiao
f331a6d144 后端补充Kafka集群运行模式字段信息 2022-09-27 18:43:22 +08:00
EricZeng
0c8c12a651 Merge pull request #621 from didi/dev
指标发送ES类按照指标类别拆分
2022-09-27 18:38:05 +08:00
zengqiao
028c3bb2fa 指标发送ES类按照指标类别拆分 2022-09-27 10:19:18 +08:00
EricZeng
d7a5a0d405 健康巡检任务按照类型进行拆分
健康巡检任务按照类型进行拆分
2022-09-27 10:17:12 +08:00
zengqiao
5ef5f6e531 健康巡检任务按照类型进行拆分 2022-09-26 20:10:49 +08:00
EricZeng
1d205734b3 Merge pull request #619 from didi/dev
集群信息中,补充ZK配置字段
2022-09-26 19:50:26 +08:00
Peng
5edd43884f Update README.md 2022-09-26 18:43:25 +08:00
zengqiao
c1992373bc 集群信息中,补充ZK配置字段 2022-09-26 11:10:38 +08:00
EricZeng
ed562f9c8a Merge pull request #618 from didi/dev
DB中Group信息的更新方式,由replace调整为insert或update
2022-09-26 10:02:24 +08:00
zengqiao
b4d44ef8c7 DB中Group信息的更新方式,由replace调整为insert或update 2022-09-23 17:02:25 +08:00
EricZeng
ad0c16a1b4 升级Helm版本及增加Docker相关文件
升级Helm版本及增加Docker相关文件
2022-09-23 16:17:00 +08:00
wangdongfang-aden
7eabe66853 Merge pull request #616 from wangdongfang-aden/dev
添加docker-compose部署和更新helm
2022-09-23 14:50:22 +08:00
wangdongfang-aden
3983d73695 Update Chart.yaml 2022-09-23 14:47:40 +08:00
wangdongfang-aden
161d4c4562 Update 单机部署手册.md 2022-09-23 14:46:27 +08:00
wangdongfang-aden
9a1e89564e Update 单机部署手册.md 2022-09-23 14:44:49 +08:00
wangdongfang-aden
0c18c5b4f6 Update 单机部署手册.md 2022-09-23 14:43:23 +08:00
wangdongfang-aden
3e12ba34f7 Update docker-compose.yml 2022-09-23 14:33:05 +08:00
wangdongfang-aden
e71e29391b Delete ks-start.sh 2022-09-23 14:26:24 +08:00
wangdongfang-aden
9b7b9a7af0 Delete es_template_create.sh 2022-09-23 14:26:16 +08:00
wangdongfang-aden
a23819c308 Create ks-start.sh 2022-09-23 14:19:35 +08:00
wangdongfang-aden
6cb1825d96 Create es_template_create.sh 2022-09-23 14:19:10 +08:00
wangdongfang-aden
77b8c758dc Create initsql 2022-09-23 14:18:17 +08:00
wangdongfang-aden
e5a582cfad Create my.cnf 2022-09-23 14:17:25 +08:00
wangdongfang-aden
ec83db267e Create init.sh 2022-09-23 14:17:02 +08:00
wangdongfang-aden
bfd026cae7 Create dockerfile 2022-09-23 14:16:28 +08:00
wangdongfang-aden
35f1dd8082 Create dockerfile 2022-09-23 14:14:47 +08:00
wangdongfang-aden
7ed0e7dd23 Create dockerfile 2022-09-23 14:14:02 +08:00
wangdongfang-aden
1a3cbf7a9d Create knowstreaming.conf 2022-09-23 14:07:04 +08:00
wangdongfang-aden
d9e4abc3de Create ks-start.sh 2022-09-23 14:05:59 +08:00
wangdongfang-aden
a4186085d3 Create es_template_create.sh 2022-09-23 14:05:05 +08:00
wangdongfang-aden
26b1846bb4 Create docker-compose.yml 2022-09-23 14:03:14 +08:00
wangdongfang-aden
1aa89527a6 helm update 3.0.0-beta.3 2022-09-23 11:36:46 +08:00
wangdongfang-aden
eac76d7ad0 helm update 3.0.0-beta.3 2022-09-23 11:36:01 +08:00
wangdongfang-aden
cea0cd56f6 Merge pull request #607 from haoqi123/dev
[单机部署手册.md]docker-compose部署方式添加注释描述
2022-09-23 10:27:04 +08:00
EricZeng
c4b897f282 bump version to 3.0.0-beta.4
bump version to 3.0.0-beta.4
2022-09-23 10:24:52 +08:00
zengqiao
47389dbabb bump version to 3.0.0-beta.4 2022-09-23 10:17:58 +08:00
haoqi
a2f8b1a851 1. [单机部署手册.md]docker-compose部署方式添加注释描述 2022-09-22 19:46:21 +08:00
EricZeng
feac0a058f Merge pull request #613 from didi/dev
补充v3.0.0-beta.2变更信息
2022-09-22 17:30:35 +08:00
zengqiao
27eeac9fd4 补充v3.0.0-beta.2变更信息 2022-09-22 17:28:51 +08:00
EricZeng
a14db4b194 Merge pull request #612 from didi/dev
合并开发分支
2022-09-22 17:28:09 +08:00
lucasun
54ee271a47 Merge pull request #611 from GraceWalk/dev
修复前端bug和体验问题
2022-09-22 15:51:46 +08:00
GraceWalk
a3a9be4f7f fix: 更正前端本地环境接口代理地址 2022-09-22 15:37:24 +08:00
GraceWalk
d4f0a832f3 fix: 样式更新 2022-09-22 15:31:52 +08:00
GraceWalk
7dc533372c fix: 更正文件引用路径 2022-09-22 15:31:34 +08:00
GraceWalk
1737d87713 fix: 修复配置无法删除的问题 2022-09-22 15:31:13 +08:00
GraceWalk
dbb98dea11 fix: 更新登录页图片 2022-09-22 15:21:04 +08:00
GraceWalk
802b382b36 fix: Topic Messages 详情提示优化 2022-09-22 15:20:31 +08:00
GraceWalk
fc82999d45 fix: 消费测试 Message 限制最大值 2022-09-22 15:19:56 +08:00
GraceWalk
08aa000c07 refactor: 接入/编辑集群优化 2022-09-22 15:19:03 +08:00
GraceWalk
39015b5100 feat: 多集群管理列表页增加手动刷新功能 2022-09-22 15:18:13 +08:00
GraceWalk
0d635ad419 refactor: webpack 配置结构调整 2022-09-22 15:13:25 +08:00
EricZeng
9133205915 Merge pull request #610 from didi/dev
合并开发分支
2022-09-22 14:51:23 +08:00
zengqiao
725ac10c3d 1、调整KafkaZKDao位置;2、offset信息获取时,过滤掉无leader分区;3、调整验证ZK是否合法时的session超时时间 2022-09-22 11:30:46 +08:00
zengqiao
2b76358c8f Overview页面,后端增加排序信息 2022-09-22 11:24:13 +08:00
zengqiao
833c360698 bump oshi-core version to 5.6.1 2022-09-22 11:17:59 +08:00
zengqiao
7da1e67b01 FAQ补充权限识别失败问题说明 2022-09-22 11:13:54 +08:00
GraceWalk
7eb86a47dd fix: 部分依赖更新 2022-09-21 16:22:45 +08:00
GraceWalk
d67e383c28 feat: 系统管理列表增加手动刷新功能 2022-09-21 16:21:57 +08:00
GraceWalk
8749d3e1f5 fix: config 子应用 axios 配置错误兼容 2022-09-21 16:21:07 +08:00
GraceWalk
30fba21c48 fix: 生产测试单词发送消息数限制为 0~1000 2022-09-21 16:15:19 +08:00
GraceWalk
d83d35aee9 fix: 样式 & 文案优化 2022-09-21 16:12:13 +08:00
GraceWalk
1d3caeea7d feat: Cluster 图表去掉放大功能 2022-09-21 16:11:14 +08:00
haoqi
26916f6632 1. [单机部署手册.md]docker-compose部署方式添加注释描述
2. 更改docker-compose中ui对外访问port为80
2022-09-21 12:55:43 +08:00
EricZeng
fbfa0d2d2a Merge pull request #600 from haoqi123/dev
docker-compose addition
2022-09-21 10:49:08 +08:00
haoqi
e626b99090 1. 删除km-dist/docker文件夹,以[单机部署手册.md]为准 2022-09-20 19:30:20 +08:00
haoqi123
203859b71b Merge branch 'didi:dev' into dev 2022-09-20 19:25:12 +08:00
haoqi
9a25c22f3a 1. 调整docker-compose.yml中各个服务的镜像
2. 经过@wangdongfang-aden大哥的调试将helm与docker镜像合二为一,于是删减掉各个镜像的Dockerfile与启动脚本,后续也不需要额外维护
2022-09-20 19:23:18 +08:00
zengqiao
0a03f41a7c 后端增加指标摆放顺序功能 2022-09-20 14:42:22 +08:00
zengqiao
56191939c8 Merge branch 'dev' of github.com:didi/KnowStreaming into dev 2022-09-20 14:23:09 +08:00
zengqiao
beb754aaaa 修复JMX连接被关闭,抛出IOException后,未进行连接重建的问题 2022-09-20 14:22:06 +08:00
EricZeng
f234f740ca Merge pull request #603 from didi/dev
合并开发分支
2022-09-20 10:51:39 +08:00
EricZeng
e14679694c Merge pull request #602 from f1558/dev
fix issue
2022-09-20 10:31:16 +08:00
zengqiao
e06712397e 修复因DB中Broker信息不存在导致TotalLogSize指标获取时抛空指针问题 2022-09-20 10:27:30 +08:00
Richard
b6c6df7ffc fix issue
* SQL specification comments to avoid direct operation failure
2022-09-20 09:42:42 +08:00
zengqiao
375c6f56c9 修改GroupOffsetResetEnum类名为OffsetTypeEnum 2022-09-19 13:55:59 +08:00
EricZeng
0bf85c97b5 Merge pull request #555 from superspeedone/dev
Dev
2022-09-19 11:18:28 +08:00
EricZeng
630e582321 Merge pull request #593 from Mengqi777/mengqi-dev
fix: adjust os judgment method with uname
2022-09-19 10:34:16 +08:00
EricZeng
a89fe23bdd Merge pull request #597 from WYAOBO/dev
文档更新
2022-09-19 10:15:38 +08:00
haoqi
a7a5fa9a31 1. 调整docker-compose.yml中networks配置
2. ks-manager添加健康检查
3. 更新单机部署手册
2022-09-18 19:10:22 +08:00
_haoqi
c73a7eee2f 1. 调整docker-compose服务,容器名称 2022-09-16 20:03:58 +08:00
_haoqi
121f8468d5 1. 调整文件格式LF
2. 调整docker-compose服务,容器名称
2022-09-16 17:33:19 +08:00
haoqi
7b0b6936e0 1. 调整docker-compose.yml中容器名称 2022-09-16 15:54:34 +08:00
Peng
597ea04a96 Update README.md 2022-09-16 15:20:04 +08:00
Peng
f7f90aeaaa Update README.md 2022-09-16 15:18:29 +08:00
_haoqi
227479f695 1. 修改dockerfile
2. 删除无用配置文件
2022-09-16 15:13:18 +08:00
WYAOBO
6477fb3fe0 Merge branch 'didi:dev' into dev 2022-09-16 14:50:13 +08:00
wangdongfang-aden
4223f4f3c4 Merge pull request #596 from wangdongfang-aden/dev
helm update 3.0.0-beta.2
2022-09-16 14:45:43 +08:00
wangdongfang-aden
7288874d72 helm update 3.0.0-beta.2 2022-09-16 14:44:14 +08:00
wangdongfang-aden
68f76f2daf helm update 3.0.0-beta.2 2022-09-16 14:42:34 +08:00
wyb
fe6ddebc49 文档更新 2022-09-16 14:41:45 +08:00
wangdongfang-aden
12b5acd073 helm update 3.0.0-beta.2 2022-09-16 14:41:40 +08:00
wangdongfang-aden
a6f1fe07b3 helm update 3.0.0-beta.2 2022-09-16 14:41:02 +08:00
wangdongfang-aden
85e3f2a946 helm update 3.0.0-beta.2 2022-09-16 14:40:34 +08:00
pokemeng
d4f416de14 fix: adjust os judgment method with uname 2022-09-16 11:34:03 +08:00
haoqi
0d9a6702c1 1. 更改es初始化脚本输出追加为重定向 2022-09-15 17:13:58 +08:00
haoqi
d11285cdbf Merge branch 'master' into dev
# Conflicts:
#	km-dist/init/sql/ddl-logi-security.sql
2022-09-15 17:01:39 +08:00
EricZeng
5f1f33d2b9 Merge pull request #591 from didi/master
合并主分支
2022-09-15 16:59:11 +08:00
zengqiao
474daf752d bump version to 3.0.0-beta.3 2022-09-15 16:54:52 +08:00
haoqi
27d1b92690 1. 添加init容器,只用于初始化es索引 2022-09-15 16:22:51 +08:00
haoqi
792f8d939d 1. 更改Dockerfile 2022-09-15 15:06:19 +08:00
haoqi
0c14c641d0 1. 添加docker-compose部署方式
2. 更改manage服务初始化方式
3. 更改es初始化方式
2022-09-15 14:26:45 +08:00
haoqi
61efdf492f 添加docker-compose部署方式 2022-09-13 23:20:41 +08:00
superspeedone
405e6e0c1d Topic消息查询支持Timestamp排序,接口支持按指定日期查询 2022-09-09 18:56:45 +08:00
superspeedone
0d227aef49 Topic消息查询支持Timestamp排序,接口支持按指定日期查询 2022-09-09 17:29:22 +08:00
superspeedone
0e49002f42 Topic消息查询支持Timestamp排序,接口支持按指定日期查询 2022-09-09 15:45:31 +08:00
superspeedone
8e50d145d5 Topic消息查询支持Timestamp排序,支持查询最新消息或最早消息 #534 2022-09-07 11:17:59 +08:00
superspeedone
0f35427645 Merge branch 'dev' of https://github.com/superspeedone/KnowStreaming into dev 2022-09-05 15:41:08 +08:00
yanweiwen
fa7ad64140 Topic消息查询支持Timestamp排序,支持查询最新消息或最早消息 #534 2022-09-05 14:46:40 +08:00
188 changed files with 5163 additions and 2211 deletions

View File

@@ -13,7 +13,7 @@ Before sending pull request to this project, please read and follow guidelines b
Add device mode, API version, related log, screenshots and other related information in your pull request if possible. Add device mode, API version, related log, screenshots and other related information in your pull request if possible.
NOTE: We assume all your contribution can be licensed under the [Apache License 2.0](LICENSE). NOTE: We assume all your contribution can be licensed under the [AGPL-3.0](LICENSE).
## Issues ## Issues

View File

@@ -51,16 +51,16 @@
- 无需侵入改造 `Apache Kafka` ,一键便能纳管 `0.10.x` ~ `3.x.x` 众多版本的Kafka包括 `ZK``Raft` 运行模式的版本,同时在兼容架构上具备良好的扩展性,帮助您提升集群管理水平; - 无需侵入改造 `Apache Kafka` ,一键便能纳管 `0.10.x` ~ `3.x.x` 众多版本的Kafka包括 `ZK``Raft` 运行模式的版本,同时在兼容架构上具备良好的扩展性,帮助您提升集群管理水平;
- 🌪️ &nbsp;**零成本、界面化** - 🌪️ &nbsp;**零成本、界面化**
- 提炼高频 CLI 能力,设计合理的产品路径,提供清新美观的 GUI 界面,支持 Cluster、Broker、Topic、Group、Message、ACL 等组件 GUI 管理普通用户5分钟即可上手 - 提炼高频 CLI 能力,设计合理的产品路径,提供清新美观的 GUI 界面,支持 Cluster、Broker、Zookeeper、Topic、ConsumerGroup、Message、ACL、Connect 等组件 GUI 管理普通用户5分钟即可上手
- 👏 &nbsp;**云原生、插件化** - 👏 &nbsp;**云原生、插件化**
- 基于云原生构建,具备水平扩展能力,只需要增加节点即可获取更强的采集及对外服务能力,提供众多可热插拔的企业级特性,覆盖可观测性生态整合、资源治理、多活容灾等核心场景; - 基于云原生构建,具备水平扩展能力,只需要增加节点即可获取更强的采集及对外服务能力,提供众多可热插拔的企业级特性,覆盖可观测性生态整合、资源治理、多活容灾等核心场景;
- 🚀 &nbsp;**专业能力** - 🚀 &nbsp;**专业能力**
- 集群管理:支持集群一键纳管,健康分析、核心组件观测 等功能; - 集群管理:支持一键纳管,健康分析、核心组件观测 等功能;
- 观测提升:多维度指标观测大盘、观测指标最佳实践 等功能; - 观测提升:多维度指标观测大盘、观测指标最佳实践 等功能;
- 异常巡检:集群多维度健康巡检、集群多维度健康分 等功能; - 异常巡检:集群多维度健康巡检、集群多维度健康分 等功能;
- 能力增强Topic扩缩副本、Topic副本迁移 等功能; - 能力增强:集群负载均衡、Topic扩缩副本、Topic副本迁移 等功能;
&nbsp; &nbsp;
@@ -133,6 +133,8 @@ PS: 提问请尽量把问题一次性描述清楚,并告知环境信息情况
**`2、微信群`** **`2、微信群`**
微信加群:添加`mike_zhangliang``PenceXie`的微信号备注KnowStreaming加群。 微信加群:添加`mike_zhangliang``PenceXie`的微信号备注KnowStreaming加群。
<br/>
<img width="116" alt="wx" src="https://user-images.githubusercontent.com/71620349/192257217-c4ebc16c-3ad9-485d-a914-5911d3a4f46b.png">
## Star History ## Star History

View File

@@ -1,5 +1,66 @@
## v3.0.0
**Bug修复**
- 修复 Group 指标防重复采集不生效问题
- 修复自动创建 ES 索引模版失败问题
- 修复 Group+Topic 列表中存在已删除Topic的问题
- 修复使用 MySQL-8 ,因兼容问题, start_time 信息为 NULL 时,会导致创建任务失败的问题
- 修复 Group 信息表更新时,出现死锁的问题
- 修复图表补点逻辑与图表时间范围不适配的问题
**体验优化**
- 按照资源类别,拆分健康巡检任务
- 优化 Group 详情页的指标为实时获取
- 图表拖拽排序支持用户级存储
- 多集群列表 ZK 信息展示兼容无 ZK 情况
- Topic 详情消息预览支持复制功能
- 部分内容大数字支持千位分割符展示
**新增**
- 集群信息中,新增 Zookeeper 客户端配置字段
- 集群信息中,新增 Kafka 集群运行模式字段
- 新增 docker-compose 的部署方式
## v3.0.0-beta.3
**文档**
- FAQ 补充权限识别失败问题的说明
- 同步更新文档,保持与官网一致
**Bug修复**
- Offset 信息获取时,过滤掉无 Leader 的分区
- 升级 oshi-core 版本至 5.6.1 版本,修复 Windows 系统获取系统指标失败问题
- 修复 JMX 连接被关闭后,未进行重建的问题
- 修复因 DB 中 Broker 信息不存在导致 TotalLogSize 指标获取时抛空指针问题
- 修复 dml-logi.sql 中SQL 注释错误的问题
- 修复 startup.sh 中,识别操作系统类型错误的问题
- 修复配置管理页面删除配置失败的问题
- 修复系统管理应用文件引用路径
- 修复 Topic Messages 详情提示信息点击跳转 404 的问题
- 修复扩副本时,当前副本数不显示问题
**体验优化**
- Topic-Messages 页面增加返回数据的排序以及按照Earliest/Latest的获取方式
- 优化 GroupOffsetResetEnum 类名为 OffsetTypeEnum使得类名含义更准确
- 移动 KafkaZKDAO 类,及 Kafka Znode 实体类的位置,使得 Kafka Zookeeper DAO 更加内聚及便于识别
- 后端补充 Overview 页面指标排序的功能
- 前端 Webpack 配置优化
- Cluster Overview 图表取消放大展示功能
- 列表页增加手动刷新功能
- 接入/编辑集群,优化 JMX-PORTVersion 信息的回显优化JMX信息的展示
- 提高登录页面图片展示清晰度
- 部分样式和文案优化
---
## v3.0.0-beta.2 ## v3.0.0-beta.2
**文档** **文档**

View File

@@ -9,7 +9,7 @@ error_exit ()
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME [ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
if [ -z "$JAVA_HOME" ]; then if [ -z "$JAVA_HOME" ]; then
if $darwin; then if [ "Darwin" = "$(uname -s)" ]; then
if [ -x '/usr/libexec/java_home' ] ; then if [ -x '/usr/libexec/java_home' ] ; then
export JAVA_HOME=`/usr/libexec/java_home` export JAVA_HOME=`/usr/libexec/java_home`

View File

@@ -59,6 +59,8 @@ sh deploy_KnowStreaming-offline.sh
### 2.1.3、容器部署 ### 2.1.3、容器部署
#### 2.1.3.1、Helm
**环境依赖** **环境依赖**
- Kubernetes >= 1.14 Helm >= 2.17.0 - Kubernetes >= 1.14 Helm >= 2.17.0
@@ -72,11 +74,11 @@ sh deploy_KnowStreaming-offline.sh
```bash ```bash
# 相关镜像在Docker Hub都可以下载 # 相关镜像在Docker Hub都可以下载
# 快速安装(NAMESPACE需要更改为已存在的安装启动需要几分钟初始化请稍等~) # 快速安装(NAMESPACE需要更改为已存在的安装启动需要几分钟初始化请稍等~)
helm install -n [NAMESPACE] [NAME] http://download.knowstreaming.com/charts/knowstreaming-manager-0.1.3.tgz helm install -n [NAMESPACE] [NAME] http://download.knowstreaming.com/charts/knowstreaming-manager-0.1.5.tgz
# 获取KnowStreaming前端ui的service. 默认nodeport方式. # 获取KnowStreaming前端ui的service. 默认nodeport方式.
# (http://nodeIP:nodeport默认用户名密码admin/admin2022_) # (http://nodeIP:nodeport默认用户名密码admin/admin2022_)
# `v3.0.0-beta.2`版本开始,默认账号密码为`admin` / `admin` # `v3.0.0-beta.2`版本开始helm chart包版本0.1.4开始),默认账号密码为`admin` / `admin`
# 添加仓库 # 添加仓库
helm repo add knowstreaming http://download.knowstreaming.com/charts helm repo add knowstreaming http://download.knowstreaming.com/charts
@@ -87,6 +89,156 @@ helm pull knowstreaming/knowstreaming-manager
&nbsp; &nbsp;
#### 2.1.3.2、Docker Compose
**环境依赖**
- [Docker](https://docs.docker.com/engine/install/)
- [Docker Compose](https://docs.docker.com/compose/install/)
**安装命令**
```bash
# `v3.0.0-beta.2`版本开始(docker镜像为0.2.0版本开始),默认账号密码为`admin` / `admin`
# https://hub.docker.com/u/knowstreaming 在此处寻找最新镜像版本
# mysql与es可以使用自己搭建的服务,调整对应配置即可
# 复制docker-compose.yml到指定位置后执行下方命令即可启动
docker-compose up -d
```
**验证安装**
```shell
docker-compose ps
# 验证启动 - 状态为 UP 则表示成功
Name Command State Ports
----------------------------------------------------------------------------------------------------
elasticsearch-single /usr/local/bin/docker-entr ... Up 9200/tcp, 9300/tcp
knowstreaming-init /bin/bash /es_template_cre ... Up
knowstreaming-manager /bin/sh /ks-start.sh Up 80/tcp
knowstreaming-mysql /entrypoint.sh mysqld Up (health: starting) 3306/tcp, 33060/tcp
knowstreaming-ui /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp
# 稍等一分钟左右 knowstreaming-init 会退出表示es初始化完成可以访问页面
Name Command State Ports
-------------------------------------------------------------------------------------------
knowstreaming-init /bin/bash /es_template_cre ... Exit 0
knowstreaming-mysql /entrypoint.sh mysqld Up (healthy) 3306/tcp, 33060/tcp
```
**访问**
```http request
http://127.0.0.1:80/
```
**docker-compose.yml**
```yml
version: "2"
services:
# *不要调整knowstreaming-manager服务名称ui中会用到
knowstreaming-manager:
image: knowstreaming/knowstreaming-manager:latest
container_name: knowstreaming-manager
privileged: true
restart: always
depends_on:
- elasticsearch-single
- knowstreaming-mysql
expose:
- 80
command:
- /bin/sh
- /ks-start.sh
environment:
TZ: Asia/Shanghai
# mysql服务地址
SERVER_MYSQL_ADDRESS: knowstreaming-mysql:3306
# mysql数据库名
SERVER_MYSQL_DB: know_streaming
# mysql用户名
SERVER_MYSQL_USER: root
# mysql用户密码
SERVER_MYSQL_PASSWORD: admin2022_
# es服务地址
SERVER_ES_ADDRESS: elasticsearch-single:9200
# 服务JVM参数
JAVA_OPTS: -Xmx1g -Xms1g
# 对于kafka中ADVERTISED_LISTENERS填写的hostname可以通过该方式完成
# extra_hosts:
# - "hostname:x.x.x.x"
# 服务日志路径
# volumes:
# - /ks/manage/log:/logs
knowstreaming-ui:
image: knowstreaming/knowstreaming-ui:latest
container_name: knowstreaming-ui
restart: always
ports:
- '80:80'
environment:
TZ: Asia/Shanghai
depends_on:
- knowstreaming-manager
# extra_hosts:
# - "hostname:x.x.x.x"
elasticsearch-single:
image: docker.io/library/elasticsearch:7.6.2
container_name: elasticsearch-single
restart: always
expose:
- 9200
- 9300
# ports:
# - '9200:9200'
# - '9300:9300'
environment:
TZ: Asia/Shanghai
# es的JVM参数
ES_JAVA_OPTS: -Xms512m -Xmx512m
# 单节点配置,多节点集群参考 https://www.elastic.co/guide/en/elasticsearch/reference/7.6/docker.html#docker-compose-file
discovery.type: single-node
# 数据持久化路径
# volumes:
# - /ks/es/data:/usr/share/elasticsearch/data
# es初始化服务与manager使用同一镜像
# 首次启动es需初始化模版和索引,后续会自动创建
knowstreaming-init:
image: knowstreaming/knowstreaming-manager:latest
container_name: knowstreaming-init
depends_on:
- elasticsearch-single
command:
- /bin/bash
- /es_template_create.sh
environment:
TZ: Asia/Shanghai
# es服务地址
SERVER_ES_ADDRESS: elasticsearch-single:9200
knowstreaming-mysql:
image: knowstreaming/knowstreaming-mysql:latest
container_name: knowstreaming-mysql
restart: always
environment:
TZ: Asia/Shanghai
# root 用户密码
MYSQL_ROOT_PASSWORD: admin2022_
# 初始化时创建的数据库名称
MYSQL_DATABASE: know_streaming
# 通配所有host,可以访问远程
MYSQL_ROOT_HOST: '%'
expose:
- 3306
# ports:
# - '3306:3306'
# 数据持久化路径
# volumes:
# - /ks/mysql/data:/data/mysql
```
&nbsp;
### 2.1.4、手动部署 ### 2.1.4、手动部署
**部署流程** **部署流程**

View File

@@ -1,12 +1,28 @@
## 6.2、版本升级手册 ## 6.2、版本升级手册
注意:如果想升级至具体版本,需要将你当前版本至你期望使用版本的变更统统执行一遍,然后才能正常使用。 注意:
- 如果想升级至具体版本,需要将你当前版本至你期望使用版本的变更统统执行一遍,然后才能正常使用。
- 如果中间某个版本没有升级信息,则表示该版本直接替换安装包即可从前一个版本升级至当前版本。
### 6.2.0、升级至 `master` 版本 ### 6.2.0、升级至 `master` 版本
暂无 暂无
### 6.2.1、升级至 `v3.0.0-beta.2`版本
### 6.2.1、升级至 `v3.0.0` 版本
**SQL 变更**
```sql
ALTER TABLE `ks_km_physical_cluster`
ADD COLUMN `zk_properties` TEXT NULL COMMENT 'ZK配置' AFTER `jmx_properties`;
```
---
### 6.2.2、升级至 `v3.0.0-beta.2`版本
**配置变更** **配置变更**
@@ -40,8 +56,7 @@ thread-pool:
``` ```
**SQL 变更**
**SQL变更**
```sql ```sql
-- 多集群管理权限2022-09-06新增 -- 多集群管理权限2022-09-06新增
@@ -78,14 +93,13 @@ ALTER TABLE `logi_security_oplog`
--- ---
### 6.2.2、升级至 `v3.0.0-beta.1`版本 ### 6.2.3、升级至 `v3.0.0-beta.1`版本
**SQL 变更**
**SQL变更**
1、在`ks_km_broker`表增加了一个监听信息字段。 1、在`ks_km_broker`表增加了一个监听信息字段。
2、为`logi_security_oplog`表operation_methods字段设置默认值''。 2、为`logi_security_oplog` operation_methods 字段设置默认值''。
因此需要执行下面的sql对数据库表进行更新。 因此需要执行下面的 sql 对数据库表进行更新。
```sql ```sql
ALTER TABLE `ks_km_broker` ALTER TABLE `ks_km_broker`
@@ -98,8 +112,7 @@ ALTER COLUMN `operation_methods` set default '';
--- ---
### 6.2.4、`2.x`版本 升级至 `v3.0.0-beta.0`版本
### 6.2.3、`2.x`版本 升级至 `v3.0.0-beta.0`版本
**升级步骤:** **升级步骤:**
@@ -123,14 +136,14 @@ ALTER COLUMN `operation_methods` set default '';
UPDATE ks_km_topic UPDATE ks_km_topic
INNER JOIN INNER JOIN
(SELECT (SELECT
topic.cluster_id AS cluster_id, topic.cluster_id AS cluster_id,
topic.topic_name AS topic_name, topic.topic_name AS topic_name,
topic.description AS description topic.description AS description
FROM topic WHERE description != '' FROM topic WHERE description != ''
) AS t ) AS t
ON ks_km_topic.cluster_phy_id = t.cluster_id ON ks_km_topic.cluster_phy_id = t.cluster_id
AND ks_km_topic.topic_name = t.topic_name AND ks_km_topic.topic_name = t.topic_name
AND ks_km_topic.id > 0 AND ks_km_topic.id > 0
SET ks_km_topic.description = t.description; SET ks_km_topic.description = t.description;
``` ```

View File

@@ -166,3 +166,19 @@ Node 版本: v12.22.12
需要到具体的应用中执行 `npm run start`,例如 `cd packages/layout-clusters-fe` 后,执行 `npm run start` 需要到具体的应用中执行 `npm run start`,例如 `cd packages/layout-clusters-fe` 后,执行 `npm run start`
应用启动后需要到基座应用中查看(需要启动基座应用,即 layout-clusters-fe 应用启动后需要到基座应用中查看(需要启动基座应用,即 layout-clusters-fe
## 8.12、权限识别失败问题
1、使用admin账号登陆KnowStreaming时点击系统管理-用户管理-角色管理-新增角色,查看页面是否正常。
<img src="http://img-ys011.didistatic.com/static/dc2img/do1_gwGfjN9N92UxzHU8dfzr" width = "400" >
2、查看'/logi-security/api/v1/permission/tree'接口返回值,出现如下图所示乱码现象。
![接口返回值](http://img-ys011.didistatic.com/static/dc2img/do1_jTxBkwNGU9vZuYQQbdNw)
3、查看logi_security_permission表看看是否出现了中文乱码现象。
根据以上几点,我们可以确定是由于数据库乱码造成的权限识别失败问题。
+ 原因:由于数据库编码和我们提供的脚本不一致,数据库里的数据发生了乱码,因此出现权限识别失败问题。
+ 解决方案清空数据库数据将数据库字符集调整为utf8最后重新执行[dml-logi.sql](https://github.com/didi/KnowStreaming/blob/master/km-dist/init/sql/dml-logi.sql)脚本导入数据即可。

View File

@@ -11,7 +11,7 @@
下面是用户第一次使用我们产品的典型体验路径: 下面是用户第一次使用我们产品的典型体验路径:
![text](http://img-ys011.didistatic.com/static/dc2img/do1_YehqxqmsVaqU5gf3XphI) ![text](http://img-ys011.didistatic.com/static/dc2img/do1_qgqPsAY46sZeBaPUCwXY)
## 5.3、常用功能 ## 5.3、常用功能

View File

@@ -19,7 +19,7 @@ import com.xiaojukeji.know.streaming.km.common.bean.vo.group.GroupTopicConsumedD
import com.xiaojukeji.know.streaming.km.common.bean.vo.group.GroupTopicOverviewVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.group.GroupTopicOverviewVO;
import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant; import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant;
import com.xiaojukeji.know.streaming.km.common.enums.AggTypeEnum; import com.xiaojukeji.know.streaming.km.common.enums.AggTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.GroupOffsetResetEnum; import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.group.GroupStateEnum; import com.xiaojukeji.know.streaming.km.common.enums.group.GroupStateEnum;
import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException; import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException;
import com.xiaojukeji.know.streaming.km.common.exception.NotExistException; import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
@@ -199,12 +199,12 @@ public class GroupManagerImpl implements GroupManager {
return Result.buildFromRSAndMsg(ResultStatus.NOT_EXIST, MsgConstant.getTopicNotExist(dto.getClusterId(), dto.getTopicName())); return Result.buildFromRSAndMsg(ResultStatus.NOT_EXIST, MsgConstant.getTopicNotExist(dto.getClusterId(), dto.getTopicName()));
} }
if (GroupOffsetResetEnum.PRECISE_OFFSET.getResetType() == dto.getResetType() if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()
&& ValidateUtils.isEmptyList(dto.getOffsetList())) { && ValidateUtils.isEmptyList(dto.getOffsetList())) {
return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误指定offset重置需传offset信息"); return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误指定offset重置需传offset信息");
} }
if (GroupOffsetResetEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType() if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()
&& ValidateUtils.isNull(dto.getTimestamp())) { && ValidateUtils.isNull(dto.getTimestamp())) {
return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误,指定时间重置需传时间信息"); return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误,指定时间重置需传时间信息");
} }
@@ -213,7 +213,7 @@ public class GroupManagerImpl implements GroupManager {
} }
private Result<Map<TopicPartition, Long>> getPartitionOffset(GroupOffsetResetDTO dto) { private Result<Map<TopicPartition, Long>> getPartitionOffset(GroupOffsetResetDTO dto) {
if (GroupOffsetResetEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()) { if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()) {
return Result.buildSuc(dto.getOffsetList().stream().collect(Collectors.toMap( return Result.buildSuc(dto.getOffsetList().stream().collect(Collectors.toMap(
elem -> new TopicPartition(dto.getTopicName(), elem.getPartitionId()), elem -> new TopicPartition(dto.getTopicName(), elem.getPartitionId()),
PartitionOffsetDTO::getOffset, PartitionOffsetDTO::getOffset,
@@ -222,9 +222,9 @@ public class GroupManagerImpl implements GroupManager {
} }
OffsetSpec offsetSpec = null; OffsetSpec offsetSpec = null;
if (GroupOffsetResetEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()) { if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()) {
offsetSpec = OffsetSpec.forTimestamp(dto.getTimestamp()); offsetSpec = OffsetSpec.forTimestamp(dto.getTimestamp());
} else if (GroupOffsetResetEnum.EARLIEST.getResetType() == dto.getResetType()) { } else if (OffsetTypeEnum.EARLIEST.getResetType() == dto.getResetType()) {
offsetSpec = OffsetSpec.earliest(); offsetSpec = OffsetSpec.earliest();
} else { } else {
offsetSpec = OffsetSpec.latest(); offsetSpec = OffsetSpec.latest();
@@ -272,15 +272,11 @@ public class GroupManagerImpl implements GroupManager {
// 获取Group指标信息 // 获取Group指标信息
Result<List<GroupMetrics>> groupMetricsResult = groupMetricService.listPartitionLatestMetricsFromES( Result<List<GroupMetrics>> groupMetricsResult = groupMetricService.collectGroupMetricsFromKafka(clusterPhyId, groupName, latestMetricNames == null ? Arrays.asList() : latestMetricNames);
clusterPhyId,
groupName,
topicName,
latestMetricNames == null? Arrays.asList(): latestMetricNames
);
// 转换Group指标 // 转换Group指标
List<GroupMetrics> esGroupMetricsList = groupMetricsResult.hasData()? groupMetricsResult.getData(): new ArrayList<>(); List<GroupMetrics> esGroupMetricsList = groupMetricsResult.hasData() ? groupMetricsResult.getData().stream().filter(elem -> topicName.equals(elem.getTopic())).collect(Collectors.toList()) : new ArrayList<>();
Map<Integer, GroupMetrics> esMetricsMap = new HashMap<>(); Map<Integer, GroupMetrics> esMetricsMap = new HashMap<>();
for (GroupMetrics groupMetrics: esGroupMetricsList) { for (GroupMetrics groupMetrics: esGroupMetricsList) {
esMetricsMap.put(groupMetrics.getPartitionId(), groupMetrics); esMetricsMap.put(groupMetrics.getPartitionId(), groupMetrics);

View File

@@ -1,5 +1,6 @@
package com.xiaojukeji.know.streaming.km.biz.topic; package com.xiaojukeji.know.streaming.km.biz.topic;
import com.xiaojukeji.know.streaming.km.common.bean.dto.pagination.PaginationSortDTO;
import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.TopicRecordDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.TopicRecordDTO;
import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result; import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result;
import com.xiaojukeji.know.streaming.km.common.bean.vo.topic.TopicBrokersPartitionsSummaryVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.topic.TopicBrokersPartitionsSummaryVO;

View File

@@ -22,25 +22,26 @@ import com.xiaojukeji.know.streaming.km.common.bean.vo.topic.partition.TopicPart
import com.xiaojukeji.know.streaming.km.common.constant.Constant; import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import com.xiaojukeji.know.streaming.km.common.constant.KafkaConstant; import com.xiaojukeji.know.streaming.km.common.constant.KafkaConstant;
import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant; import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant;
import com.xiaojukeji.know.streaming.km.common.converter.PartitionConverter;
import com.xiaojukeji.know.streaming.km.common.converter.TopicVOConverter; import com.xiaojukeji.know.streaming.km.common.converter.TopicVOConverter;
import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.SortTypeEnum;
import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException; import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException;
import com.xiaojukeji.know.streaming.km.common.exception.NotExistException; import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.PaginationUtil;
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils; import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService; import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService;
import com.xiaojukeji.know.streaming.km.core.service.cluster.ClusterPhyService; import com.xiaojukeji.know.streaming.km.core.service.cluster.ClusterPhyService;
import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionMetricService; import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionMetricService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicConfigService;
import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionService; import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicConfigService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicMetricService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicMetricService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.core.service.version.metrics.TopicMetricVersionItems; import com.xiaojukeji.know.streaming.km.core.service.version.metrics.TopicMetricVersionItems;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.admin.OffsetSpec; import org.apache.kafka.clients.admin.OffsetSpec;
import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.config.TopicConfig; import org.apache.kafka.common.config.TopicConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -160,8 +161,31 @@ public class TopicStateManagerImpl implements TopicStateManager {
} }
maxMessage = Math.min(maxMessage, dto.getMaxRecords()); maxMessage = Math.min(maxMessage, dto.getMaxRecords());
kafkaConsumer.assign(partitionList); kafkaConsumer.assign(partitionList);
Map<TopicPartition, OffsetAndTimestamp> partitionOffsetAndTimestampMap = new HashMap<>();
// 获取指定时间每个分区的offset按指定开始时间查询消息时
if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getFilterOffsetReset()) {
Map<TopicPartition, Long> timestampsToSearch = new HashMap<>();
partitionList.forEach(topicPartition -> {
timestampsToSearch.put(topicPartition, dto.getStartTimestampUnitMs());
});
partitionOffsetAndTimestampMap = kafkaConsumer.offsetsForTimes(timestampsToSearch);
}
for (TopicPartition partition : partitionList) { for (TopicPartition partition : partitionList) {
kafkaConsumer.seek(partition, Math.max(beginOffsetsMapResult.getData().get(partition), endOffsetsMapResult.getData().get(partition) - dto.getMaxRecords())); if (OffsetTypeEnum.EARLIEST.getResetType() == dto.getFilterOffsetReset()) {
// 重置到最旧
kafkaConsumer.seek(partition, beginOffsetsMapResult.getData().get(partition));
} else if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getFilterOffsetReset()) {
// 重置到指定时间
kafkaConsumer.seek(partition, partitionOffsetAndTimestampMap.get(partition).offset());
} else if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getFilterOffsetReset()) {
// 重置到指定位置
} else {
// 默认,重置到最新
kafkaConsumer.seek(partition, Math.max(beginOffsetsMapResult.getData().get(partition), endOffsetsMapResult.getData().get(partition) - dto.getMaxRecords()));
}
} }
// 这里需要减去 KafkaConstant.POLL_ONCE_TIMEOUT_UNIT_MS 是因为poll一次需要耗时如果这里不减去则可能会导致poll之后超过要求的时间 // 这里需要减去 KafkaConstant.POLL_ONCE_TIMEOUT_UNIT_MS 是因为poll一次需要耗时如果这里不减去则可能会导致poll之后超过要求的时间
@@ -185,6 +209,15 @@ public class TopicStateManagerImpl implements TopicStateManager {
} }
} }
// 排序
if (ObjectUtils.isNotEmpty(voList)) {
// 默认按时间倒序排序
if (StringUtils.isBlank(dto.getSortType())) {
dto.setSortType(SortTypeEnum.DESC.getSortType());
}
PaginationUtil.pageBySort(voList, dto.getSortField(), dto.getSortType());
}
return Result.buildSuc(voList.subList(0, Math.min(dto.getMaxRecords(), voList.size()))); return Result.buildSuc(voList.subList(0, Math.min(dto.getMaxRecords(), voList.size())));
} catch (Exception e) { } catch (Exception e) {
log.error("method=getTopicMessages||clusterPhyId={}||topicName={}||param={}||errMsg=exception", clusterPhyId, topicName, dto, e); log.error("method=getTopicMessages||clusterPhyId={}||topicName={}||param={}||errMsg=exception", clusterPhyId, topicName, dto, e);

View File

@@ -7,12 +7,14 @@ import com.didiglobal.logi.log.LogFactory;
import com.didiglobal.logi.security.common.dto.config.ConfigDTO; import com.didiglobal.logi.security.common.dto.config.ConfigDTO;
import com.didiglobal.logi.security.service.ConfigService; import com.didiglobal.logi.security.service.ConfigService;
import com.xiaojukeji.know.streaming.km.biz.version.VersionControlManager; import com.xiaojukeji.know.streaming.km.biz.version.VersionControlManager;
import com.xiaojukeji.know.streaming.km.common.bean.dto.metrices.MetricDetailDTO;
import com.xiaojukeji.know.streaming.km.common.bean.dto.metrices.UserMetricConfigDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.metrices.UserMetricConfigDTO;
import com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric.UserMetricConfig; import com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric.UserMetricConfig;
import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result; import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result;
import com.xiaojukeji.know.streaming.km.common.bean.entity.version.VersionControlItem; import com.xiaojukeji.know.streaming.km.common.bean.entity.version.VersionControlItem;
import com.xiaojukeji.know.streaming.km.common.bean.vo.config.metric.UserMetricConfigVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.config.metric.UserMetricConfigVO;
import com.xiaojukeji.know.streaming.km.common.bean.vo.version.VersionItemVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.version.VersionItemVO;
import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import com.xiaojukeji.know.streaming.km.common.enums.version.VersionEnum; import com.xiaojukeji.know.streaming.km.common.enums.version.VersionEnum;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.VersionUtil; import com.xiaojukeji.know.streaming.km.common.utils.VersionUtil;
@@ -47,29 +49,29 @@ public class VersionControlManagerImpl implements VersionControlManager {
@PostConstruct @PostConstruct
public void init(){ public void init(){
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_TOTAL_PRODUCE_REQUESTS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_FETCH_REQ, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_FETCH_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_PRODUCE_REQ, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_MESSAGE_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_UNDER_REPLICA_PARTITIONS, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_UNDER_REPLICA_PARTITIONS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_TOTAL_PRODUCE_REQUESTS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_OUT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_OUT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_REJECTED, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_REJECTED, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_MESSAGE_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_REQ_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_RES_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_ACTIVE_CONTROLLER_COUNT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_ACTIVE_CONTROLLER_COUNT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_LOG_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_CONNECTIONS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_MESSAGES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_OUT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_OUT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_GROUP_REBALANCES, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_CONNECTIONS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_JOB_RUNNING, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_MESSAGES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITIONS_NO_LEADER, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITIONS_NO_LEADER, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITION_URP, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITION_URP, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_LOG_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_REQ_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_RES_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_GROUP_REBALANCES, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_JOB_RUNNING, true));
defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_OFFSET_CONSUMED, true)); defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_OFFSET_CONSUMED, true));
defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_LAG, true)); defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_LAG, true));
@@ -77,18 +79,18 @@ public class VersionControlManagerImpl implements VersionControlManager {
defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_REQ_QUEUE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_CONNECTION_COUNT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_RES_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_MESSAGE_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_MESSAGE_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_NETWORK_RPO_AVG_IDLE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_NETWORK_RPO_AVG_IDLE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_REQ_AVG_IDLE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_REQ_AVG_IDLE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_CONNECTION_COUNT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_REQ_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_OUT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_RES_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_PARTITIONS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_LEADERS_SKEW, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_LEADERS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_UNDER_REPLICATE_PARTITION, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_UNDER_REPLICATE_PARTITION, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_PARTITIONS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_OUT, true));
} }
@Autowired @Autowired
@@ -159,6 +161,9 @@ public class VersionControlManagerImpl implements VersionControlManager {
UserMetricConfig umc = userMetricConfigMap.get(itemType + "@" + metric); UserMetricConfig umc = userMetricConfigMap.get(itemType + "@" + metric);
userMetricConfigVO.setSet(null != umc && umc.isSet()); userMetricConfigVO.setSet(null != umc && umc.isSet());
if (umc != null) {
userMetricConfigVO.setRank(umc.getRank());
}
userMetricConfigVO.setName(itemVO.getName()); userMetricConfigVO.setName(itemVO.getName());
userMetricConfigVO.setType(itemVO.getType()); userMetricConfigVO.setType(itemVO.getType());
userMetricConfigVO.setDesc(itemVO.getDesc()); userMetricConfigVO.setDesc(itemVO.getDesc());
@@ -178,13 +183,29 @@ public class VersionControlManagerImpl implements VersionControlManager {
@Override @Override
public Result<Void> updateUserMetricItem(Long clusterId, Integer type, UserMetricConfigDTO dto, String operator) { public Result<Void> updateUserMetricItem(Long clusterId, Integer type, UserMetricConfigDTO dto, String operator) {
Map<String, Boolean> metricsSetMap = dto.getMetricsSet(); Map<String, Boolean> metricsSetMap = dto.getMetricsSet();
if(null == metricsSetMap || metricsSetMap.isEmpty()){
//转换metricDetailDTOList
List<MetricDetailDTO> metricDetailDTOList = dto.getMetricDetailDTOList();
Map<String, MetricDetailDTO> metricDetailMap = new HashMap<>();
if (metricDetailDTOList != null && !metricDetailDTOList.isEmpty()) {
metricDetailMap = metricDetailDTOList.stream().collect(Collectors.toMap(MetricDetailDTO::getMetric, Function.identity()));
}
//转换metricsSetMap
if (metricsSetMap != null && !metricsSetMap.isEmpty()) {
for (Map.Entry<String, Boolean> metricAndShowEntry : metricsSetMap.entrySet()) {
if (metricDetailMap.containsKey(metricAndShowEntry.getKey())) continue;
metricDetailMap.put(metricAndShowEntry.getKey(), new MetricDetailDTO(metricAndShowEntry.getKey(), metricAndShowEntry.getValue(), null));
}
}
if (metricDetailMap.isEmpty()) {
return Result.buildSuc(); return Result.buildSuc();
} }
Set<UserMetricConfig> userMetricConfigs = getUserMetricConfig(operator); Set<UserMetricConfig> userMetricConfigs = getUserMetricConfig(operator);
for(Map.Entry<String, Boolean> metricAndShowEntry : metricsSetMap.entrySet()){ for (MetricDetailDTO metricDetailDTO : metricDetailMap.values()) {
UserMetricConfig userMetricConfig = new UserMetricConfig(type, metricAndShowEntry.getKey(), metricAndShowEntry.getValue()); UserMetricConfig userMetricConfig = new UserMetricConfig(type, metricDetailDTO.getMetric(), metricDetailDTO.getSet(), metricDetailDTO.getRank());
userMetricConfigs.remove(userMetricConfig); userMetricConfigs.remove(userMetricConfig);
userMetricConfigs.add(userMetricConfig); userMetricConfigs.add(userMetricConfig);
} }
@@ -228,7 +249,7 @@ public class VersionControlManagerImpl implements VersionControlManager {
return defaultMetrics; return defaultMetrics;
} }
return JSON.parseObject(value, new TypeReference<Set<UserMetricConfig>>(){}); return JSON.parseObject(value, new TypeReference<Set<UserMetricConfig>>() {});
} }
public static void main(String[] args){ public static void main(String[] args){

View File

@@ -1,121 +0,0 @@
package com.xiaojukeji.know.streaming.km.collector.metric;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.*;
import com.xiaojukeji.know.streaming.km.common.bean.po.BaseESPO;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.*;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.EnvUtil;
import com.xiaojukeji.know.streaming.km.common.utils.NamedThreadFactory;
import com.xiaojukeji.know.streaming.km.persistence.es.dao.BaseMetricESDAO;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.*;
@Component
public class MetricESSender implements ApplicationListener<BaseMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
private static final int THRESHOLD = 100;
private ThreadPoolExecutor esExecutor = new ThreadPoolExecutor(10, 20, 6000, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(1000),
new NamedThreadFactory("KM-Collect-MetricESSender-ES"),
(r, e) -> LOGGER.warn("class=MetricESSender||msg=KM-Collect-MetricESSender-ES Deque is blocked, taskCount:{}" + e.getTaskCount()));
@PostConstruct
public void init(){
LOGGER.info("class=MetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(BaseMetricEvent event) {
if(event instanceof BrokerMetricEvent) {
BrokerMetricEvent brokerMetricEvent = (BrokerMetricEvent)event;
send2es(BROKER_INDEX,
ConvertUtil.list2List(brokerMetricEvent.getBrokerMetrics(), BrokerMetricPO.class)
);
} else if(event instanceof ClusterMetricEvent) {
ClusterMetricEvent clusterMetricEvent = (ClusterMetricEvent)event;
send2es(CLUSTER_INDEX,
ConvertUtil.list2List(clusterMetricEvent.getClusterMetrics(), ClusterMetricPO.class)
);
} else if(event instanceof TopicMetricEvent) {
TopicMetricEvent topicMetricEvent = (TopicMetricEvent)event;
send2es(TOPIC_INDEX,
ConvertUtil.list2List(topicMetricEvent.getTopicMetrics(), TopicMetricPO.class)
);
} else if(event instanceof PartitionMetricEvent) {
PartitionMetricEvent partitionMetricEvent = (PartitionMetricEvent)event;
send2es(PARTITION_INDEX,
ConvertUtil.list2List(partitionMetricEvent.getPartitionMetrics(), PartitionMetricPO.class)
);
} else if(event instanceof GroupMetricEvent) {
GroupMetricEvent groupMetricEvent = (GroupMetricEvent)event;
send2es(GROUP_INDEX,
ConvertUtil.list2List(groupMetricEvent.getGroupMetrics(), GroupMetricPO.class)
);
} else if(event instanceof ReplicaMetricEvent) {
ReplicaMetricEvent replicaMetricEvent = (ReplicaMetricEvent)event;
send2es(REPLICATION_INDEX,
ConvertUtil.list2List(replicaMetricEvent.getReplicationMetrics(), ReplicationMetricPO.class)
);
}
}
/**
* 根据不同监控维度来发送
*/
private boolean send2es(String index, List<? extends BaseESPO> statsList){
if (CollectionUtils.isEmpty(statsList)) {
return true;
}
if (!EnvUtil.isOnline()) {
LOGGER.info("class=MetricESSender||method=send2es||ariusStats={}||size={}",
index, statsList.size());
}
BaseMetricESDAO baseMetricESDao = BaseMetricESDAO.getByStatsType(index);
if (Objects.isNull( baseMetricESDao )) {
LOGGER.error("class=MetricESSender||method=send2es||errMsg=fail to find {}", index);
return false;
}
int size = statsList.size();
int num = (size) % THRESHOLD == 0 ? (size / THRESHOLD) : (size / THRESHOLD + 1);
if (size < THRESHOLD) {
esExecutor.execute(
() -> baseMetricESDao.batchInsertStats(statsList)
);
return true;
}
for (int i = 1; i < num + 1; i++) {
int end = (i * THRESHOLD) > size ? size : (i * THRESHOLD);
int start = (i - 1) * THRESHOLD;
esExecutor.execute(
() -> baseMetricESDao.batchInsertStats(statsList.subList(start, end))
);
}
return true;
}
}

View File

@@ -0,0 +1,72 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.po.BaseESPO;
import com.xiaojukeji.know.streaming.km.common.utils.EnvUtil;
import com.xiaojukeji.know.streaming.km.common.utils.NamedThreadFactory;
import com.xiaojukeji.know.streaming.km.persistence.es.dao.BaseMetricESDAO;
import org.apache.commons.collections.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public abstract class AbstractMetricESSender {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
private static final int THRESHOLD = 100;
private static final ThreadPoolExecutor esExecutor = new ThreadPoolExecutor(
10,
20,
6000,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(1000),
new NamedThreadFactory("KM-Collect-MetricESSender-ES"),
(r, e) -> LOGGER.warn("class=MetricESSender||msg=KM-Collect-MetricESSender-ES Deque is blocked, taskCount:{}" + e.getTaskCount())
);
/**
* 根据不同监控维度来发送
*/
protected boolean send2es(String index, List<? extends BaseESPO> statsList){
if (CollectionUtils.isEmpty(statsList)) {
return true;
}
if (!EnvUtil.isOnline()) {
LOGGER.info("class=MetricESSender||method=send2es||ariusStats={}||size={}",
index, statsList.size());
}
BaseMetricESDAO baseMetricESDao = BaseMetricESDAO.getByStatsType(index);
if (Objects.isNull( baseMetricESDao )) {
LOGGER.error("class=MetricESSender||method=send2es||errMsg=fail to find {}", index);
return false;
}
int size = statsList.size();
int num = (size) % THRESHOLD == 0 ? (size / THRESHOLD) : (size / THRESHOLD + 1);
if (size < THRESHOLD) {
esExecutor.execute(
() -> baseMetricESDao.batchInsertStats(statsList)
);
return true;
}
for (int i = 1; i < num + 1; i++) {
int end = (i * THRESHOLD) > size ? size : (i * THRESHOLD);
int start = (i - 1) * THRESHOLD;
esExecutor.execute(
() -> baseMetricESDao.batchInsertStats(statsList.subList(start, end))
);
}
return true;
}
}

View File

@@ -0,0 +1,28 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.BrokerMetricEvent;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.BrokerMetricPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.BROKER_INDEX;
@Component
public class BrokerMetricESSender extends AbstractMetricESSender implements ApplicationListener<BrokerMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
@PostConstruct
public void init(){
LOGGER.info("class=BrokerMetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(BrokerMetricEvent event) {
send2es(BROKER_INDEX, ConvertUtil.list2List(event.getBrokerMetrics(), BrokerMetricPO.class));
}
}

View File

@@ -0,0 +1,29 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.ClusterMetricEvent;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.ClusterMetricPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.CLUSTER_INDEX;
@Component
public class ClusterMetricESSender extends AbstractMetricESSender implements ApplicationListener<ClusterMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
@PostConstruct
public void init(){
LOGGER.info("class=ClusterMetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(ClusterMetricEvent event) {
send2es(CLUSTER_INDEX, ConvertUtil.list2List(event.getClusterMetrics(), ClusterMetricPO.class));
}
}

View File

@@ -0,0 +1,29 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.GroupMetricEvent;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.GroupMetricPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.GROUP_INDEX;
@Component
public class GroupMetricESSender extends AbstractMetricESSender implements ApplicationListener<GroupMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
@PostConstruct
public void init(){
LOGGER.info("class=GroupMetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(GroupMetricEvent event) {
send2es(GROUP_INDEX, ConvertUtil.list2List(event.getGroupMetrics(), GroupMetricPO.class));
}
}

View File

@@ -0,0 +1,28 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.PartitionMetricEvent;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.PartitionMetricPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.PARTITION_INDEX;
@Component
public class PartitionMetricESSender extends AbstractMetricESSender implements ApplicationListener<PartitionMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
@PostConstruct
public void init(){
LOGGER.info("class=PartitionMetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(PartitionMetricEvent event) {
send2es(PARTITION_INDEX, ConvertUtil.list2List(event.getPartitionMetrics(), PartitionMetricPO.class));
}
}

View File

@@ -0,0 +1,28 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.ReplicaMetricEvent;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.ReplicationMetricPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.REPLICATION_INDEX;
@Component
public class ReplicaMetricESSender extends AbstractMetricESSender implements ApplicationListener<ReplicaMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
@PostConstruct
public void init(){
LOGGER.info("class=GroupMetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(ReplicaMetricEvent event) {
send2es(REPLICATION_INDEX, ConvertUtil.list2List(event.getReplicationMetrics(), ReplicationMetricPO.class));
}
}

View File

@@ -0,0 +1,29 @@
package com.xiaojukeji.know.streaming.km.collector.sink;
import com.didiglobal.logi.log.ILog;
import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.event.metric.*;
import com.xiaojukeji.know.streaming.km.common.bean.po.metrice.*;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import static com.xiaojukeji.know.streaming.km.common.constant.ESIndexConstant.TOPIC_INDEX;
@Component
public class TopicMetricESSender extends AbstractMetricESSender implements ApplicationListener<TopicMetricEvent> {
protected static final ILog LOGGER = LogFactory.getLog("METRIC_LOGGER");
@PostConstruct
public void init(){
LOGGER.info("class=TopicMetricESSender||method=init||msg=init finished");
}
@Override
public void onApplicationEvent(TopicMetricEvent event) {
send2es(TOPIC_INDEX, ConvertUtil.list2List(event.getTopicMetrics(), TopicMetricPO.class));
}
}

View File

@@ -3,6 +3,7 @@ package com.xiaojukeji.know.streaming.km.common.bean.dto.cluster;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xiaojukeji.know.streaming.km.common.bean.dto.BaseDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.BaseDTO;
import com.xiaojukeji.know.streaming.km.common.bean.entity.config.JmxConfig; import com.xiaojukeji.know.streaming.km.common.bean.entity.config.JmxConfig;
import com.xiaojukeji.know.streaming.km.common.bean.entity.config.ZKConfig;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -34,4 +35,8 @@ public class ClusterPhyBaseDTO extends BaseDTO {
@NotNull(message = "jmxProperties不允许为空") @NotNull(message = "jmxProperties不允许为空")
@ApiModelProperty(value="Jmx配置") @ApiModelProperty(value="Jmx配置")
protected JmxConfig jmxProperties; protected JmxConfig jmxProperties;
// TODO 前端页面增加时,需要加一个不为空的限制
@ApiModelProperty(value="ZK配置")
protected ZKConfig zkProperties;
} }

View File

@@ -3,6 +3,7 @@ package com.xiaojukeji.know.streaming.km.common.bean.dto.group;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xiaojukeji.know.streaming.km.common.bean.dto.partition.PartitionOffsetDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.partition.PartitionOffsetDTO;
import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.ClusterTopicDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.ClusterTopicDTO;
import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -23,7 +24,7 @@ public class GroupOffsetResetDTO extends ClusterTopicDTO {
private String groupName; private String groupName;
/** /**
* @see com.xiaojukeji.know.streaming.km.common.enums.GroupOffsetResetEnum * @see OffsetTypeEnum
*/ */
@NotNull(message = "resetType不允许为空") @NotNull(message = "resetType不允许为空")
@ApiModelProperty(value = "重置方式", example = "1") @ApiModelProperty(value = "重置方式", example = "1")

View File

@@ -0,0 +1,32 @@
package com.xiaojukeji.know.streaming.km.common.bean.dto.metrices;
import com.xiaojukeji.know.streaming.km.common.bean.dto.BaseDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
/**
* @author didi
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "指标详细属性信息")
public class MetricDetailDTO extends BaseDTO {
@ApiModelProperty("指标名称")
private String metric;
@ApiModelProperty("指标是否显示")
private Boolean set;
@NotNull(message = "MetricDetailDTO的rank字段应不为空")
@ApiModelProperty("指标优先级")
private Integer rank;
}

View File

@@ -7,6 +7,8 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import javax.validation.Valid;
import java.util.List;
import java.util.Map; import java.util.Map;
@@ -17,4 +19,8 @@ import java.util.Map;
public class UserMetricConfigDTO extends BaseDTO { public class UserMetricConfigDTO extends BaseDTO {
@ApiModelProperty("指标展示设置项key指标名value是否展现(true展现/false不展现)") @ApiModelProperty("指标展示设置项key指标名value是否展现(true展现/false不展现)")
private Map<String, Boolean> metricsSet; private Map<String, Boolean> metricsSet;
@Valid
@ApiModelProperty("指标自定义属性列表")
private List<MetricDetailDTO> metricDetailDTOList;
} }

View File

@@ -1,7 +1,8 @@
package com.xiaojukeji.know.streaming.km.common.bean.dto.topic; package com.xiaojukeji.know.streaming.km.common.bean.dto.topic;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xiaojukeji.know.streaming.km.common.bean.dto.BaseDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.pagination.PaginationSortDTO;
import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -15,7 +16,7 @@ import javax.validation.constraints.NotNull;
@Data @Data
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel(description = "Topic记录") @ApiModel(description = "Topic记录")
public class TopicRecordDTO extends BaseDTO { public class TopicRecordDTO extends PaginationSortDTO {
@NotNull(message = "truncate不允许为空") @NotNull(message = "truncate不允许为空")
@ApiModelProperty(value = "是否截断", example = "true") @ApiModelProperty(value = "是否截断", example = "true")
private Boolean truncate; private Boolean truncate;
@@ -34,4 +35,13 @@ public class TopicRecordDTO extends BaseDTO {
@ApiModelProperty(value = "预览超时时间", example = "10000") @ApiModelProperty(value = "预览超时时间", example = "10000")
private Long pullTimeoutUnitMs = 8000L; private Long pullTimeoutUnitMs = 8000L;
/**
* @see OffsetTypeEnum
*/
@ApiModelProperty(value = "offset", example = "")
private Integer filterOffsetReset = 0;
@ApiModelProperty(value = "开始日期时间戳", example = "")
private Long startTimestampUnitMs;
} }

View File

@@ -5,7 +5,6 @@ import com.alibaba.fastjson.TypeReference;
import com.xiaojukeji.know.streaming.km.common.bean.entity.common.IpPortData; import com.xiaojukeji.know.streaming.km.common.bean.entity.common.IpPortData;
import com.xiaojukeji.know.streaming.km.common.bean.po.broker.BrokerPO; import com.xiaojukeji.know.streaming.km.common.bean.po.broker.BrokerPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.BrokerMetadata;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -79,20 +78,6 @@ public class Broker implements Serializable {
return metadata; return metadata;
} }
public static Broker buildFrom(Long clusterPhyId, Integer brokerId, BrokerMetadata brokerMetadata) {
Broker metadata = new Broker();
metadata.setClusterPhyId(clusterPhyId);
metadata.setBrokerId(brokerId);
metadata.setHost(brokerMetadata.getHost());
metadata.setPort(brokerMetadata.getPort());
metadata.setJmxPort(brokerMetadata.getJmxPort());
metadata.setStartTimestamp(brokerMetadata.getTimestamp());
metadata.setRack(brokerMetadata.getRack());
metadata.setStatus(1);
metadata.setEndpointMap(brokerMetadata.getEndpointMap());
return metadata;
}
public static Broker buildFrom(BrokerPO brokerPO) { public static Broker buildFrom(BrokerPO brokerPO) {
Broker broker = ConvertUtil.obj2Obj(brokerPO, Broker.class); Broker broker = ConvertUtil.obj2Obj(brokerPO, Broker.class);
String endpointMapStr = brokerPO.getEndpointMap(); String endpointMapStr = brokerPO.getEndpointMap();

View File

@@ -53,9 +53,16 @@ public class ClusterPhy implements Comparable<ClusterPhy>, EntifyIdInterface {
/** /**
* jmx配置 * jmx配置
* @see com.xiaojukeji.know.streaming.km.common.bean.entity.config.JmxConfig
*/ */
private String jmxProperties; private String jmxProperties;
/**
* zk配置
* @see com.xiaojukeji.know.streaming.km.common.bean.entity.config.ZKConfig
*/
private String zkProperties;
/** /**
* 开启ACL * 开启ACL
* @see com.xiaojukeji.know.streaming.km.common.enums.cluster.ClusterAuthTypeEnum * @see com.xiaojukeji.know.streaming.km.common.enums.cluster.ClusterAuthTypeEnum

View File

@@ -0,0 +1,31 @@
package com.xiaojukeji.know.streaming.km.common.bean.entity.config;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Properties;
/**
* @author zengqiao
* @date 22/02/24
*/
@Data
@ApiModel(description = "ZK配置")
public class ZKConfig implements Serializable {
@ApiModelProperty(value="ZK的jmx配置")
private JmxConfig jmxConfig;
@ApiModelProperty(value="ZK是否开启secure", example = "false")
private Boolean openSecure = false;
@ApiModelProperty(value="ZK的Session超时时间", example = "15000")
private Long sessionTimeoutUnitMs = 15000L;
@ApiModelProperty(value="ZK的Request超时时间", example = "5000")
private Long requestTimeoutUnitMs = 5000L;
@ApiModelProperty(value="ZK的Request超时时间")
private Properties otherProps = new Properties();
}

View File

@@ -1,12 +1,12 @@
package com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric; package com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric;
import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
public class UserMetricConfig { public class UserMetricConfig {
private int type; private int type;
@@ -15,6 +15,22 @@ public class UserMetricConfig {
private boolean set; private boolean set;
private Integer rank;
public UserMetricConfig(int type, String metric, boolean set, Integer rank) {
this.type = type;
this.metric = metric;
this.set = set;
this.rank = rank;
}
public UserMetricConfig(int type, String metric, boolean set) {
this.type = type;
this.metric = metric;
this.set = set;
this.rank = null;
}
@Override @Override
public int hashCode(){ public int hashCode(){
return metric.hashCode() << 1 + type; return metric.hashCode() << 1 + type;

View File

@@ -41,6 +41,11 @@ public class ClusterPhyPO extends BasePO {
*/ */
private String jmxProperties; private String jmxProperties;
/**
* zk配置
*/
private String zkProperties;
/** /**
* 认证类型 * 认证类型
* @see com.xiaojukeji.know.streaming.km.common.enums.cluster.ClusterAuthTypeEnum * @see com.xiaojukeji.know.streaming.km.common.enums.cluster.ClusterAuthTypeEnum

View File

@@ -31,9 +31,15 @@ public class ClusterPhyBaseVO extends BaseTimeVO {
@ApiModelProperty(value="Jmx配置", example = "{}") @ApiModelProperty(value="Jmx配置", example = "{}")
protected String jmxProperties; protected String jmxProperties;
@ApiModelProperty(value="ZK配置", example = "{}")
protected String zkProperties;
@ApiModelProperty(value="描述", example = "测试") @ApiModelProperty(value="描述", example = "测试")
protected String description; protected String description;
@ApiModelProperty(value="集群的kafka版本", example = "2.5.1") @ApiModelProperty(value="集群的kafka版本", example = "2.5.1")
protected String kafkaVersion; protected String kafkaVersion;
@ApiModelProperty(value="集群的运行模式", example = "2raft模式其他是ZK模式")
private Integer runState;
} }

View File

@@ -14,4 +14,7 @@ import lombok.NoArgsConstructor;
public class UserMetricConfigVO extends VersionItemVO { public class UserMetricConfigVO extends VersionItemVO {
@ApiModelProperty(value = "该指标用户是否设置展现", example = "true") @ApiModelProperty(value = "该指标用户是否设置展现", example = "true")
private Boolean set; private Boolean set;
@ApiModelProperty(value = "该指标展示优先级", example = "1")
private Integer rank;
} }

View File

@@ -42,6 +42,7 @@ public class Constant {
*/ */
public static final Integer DEFAULT_CLUSTER_HEALTH_SCORE = 90; public static final Integer DEFAULT_CLUSTER_HEALTH_SCORE = 90;
public static final String DEFAULT_USER_NAME = "know-streaming-app"; public static final String DEFAULT_USER_NAME = "know-streaming-app";
public static final int INVALID_CODE = -1; public static final int INVALID_CODE = -1;
@@ -64,4 +65,5 @@ public class Constant {
public static final Float COLLECT_METRICS_ERROR_COST_TIME = -1.0F; public static final Float COLLECT_METRICS_ERROR_COST_TIME = -1.0F;
public static final Integer DEFAULT_RETRY_TIME = 3; public static final Integer DEFAULT_RETRY_TIME = 3;
} }

View File

@@ -52,6 +52,10 @@ public class MsgConstant {
/**************************************************** Partition ****************************************************/ /**************************************************** Partition ****************************************************/
public static String getPartitionNoLeader(Long clusterPhyId, String topicName) {
return String.format("集群ID:[%d] Topic名称:[%s] 所有分区NoLeader", clusterPhyId, topicName);
}
public static String getPartitionNotExist(Long clusterPhyId, String topicName) { public static String getPartitionNotExist(Long clusterPhyId, String topicName) {
return String.format("集群ID:[%d] Topic名称:[%s] 存在非法的分区ID", clusterPhyId, topicName); return String.format("集群ID:[%d] Topic名称:[%s] 存在非法的分区ID", clusterPhyId, topicName);
} }

View File

@@ -19,6 +19,11 @@ public class ClusterConverter {
ClusterPhyPO clusterPhyPO = ConvertUtil.obj2Obj(dto, ClusterPhyPO.class); ClusterPhyPO clusterPhyPO = ConvertUtil.obj2Obj(dto, ClusterPhyPO.class);
clusterPhyPO.setClientProperties(ConvertUtil.obj2Json(dto.getClientProperties())); clusterPhyPO.setClientProperties(ConvertUtil.obj2Json(dto.getClientProperties()));
clusterPhyPO.setJmxProperties(ConvertUtil.obj2Json(dto.getJmxProperties())); clusterPhyPO.setJmxProperties(ConvertUtil.obj2Json(dto.getJmxProperties()));
if (ValidateUtils.isNull(dto.getZkProperties())) {
clusterPhyPO.setZkProperties("");
} else {
clusterPhyPO.setZkProperties(ConvertUtil.obj2Json(dto.getZkProperties()));
}
clusterPhyPO.setRunState( clusterPhyPO.setRunState(
ValidateUtils.isBlank(dto.getZookeeper())? ValidateUtils.isBlank(dto.getZookeeper())?
ClusterRunStateEnum.RUN_RAFT.getRunState() : ClusterRunStateEnum.RUN_RAFT.getRunState() :
@@ -32,6 +37,11 @@ public class ClusterConverter {
ClusterPhyPO clusterPhyPO = ConvertUtil.obj2Obj(dto, ClusterPhyPO.class); ClusterPhyPO clusterPhyPO = ConvertUtil.obj2Obj(dto, ClusterPhyPO.class);
clusterPhyPO.setClientProperties(ConvertUtil.obj2Json(dto.getClientProperties())); clusterPhyPO.setClientProperties(ConvertUtil.obj2Json(dto.getClientProperties()));
clusterPhyPO.setJmxProperties(ConvertUtil.obj2Json(dto.getJmxProperties())); clusterPhyPO.setJmxProperties(ConvertUtil.obj2Json(dto.getJmxProperties()));
if (ValidateUtils.isNull(dto.getZkProperties())) {
clusterPhyPO.setZkProperties("");
} else {
clusterPhyPO.setZkProperties(ConvertUtil.obj2Json(dto.getZkProperties()));
}
clusterPhyPO.setRunState( clusterPhyPO.setRunState(
ValidateUtils.isBlank(dto.getZookeeper())? ValidateUtils.isBlank(dto.getZookeeper())?
ClusterRunStateEnum.RUN_RAFT.getRunState() : ClusterRunStateEnum.RUN_RAFT.getRunState() :

View File

@@ -3,19 +3,19 @@ package com.xiaojukeji.know.streaming.km.common.enums;
import lombok.Getter; import lombok.Getter;
/** /**
* 重置offset * offset类型
* @author zengqiao * @author zengqiao
* @date 19/4/8 * @date 19/4/8
*/ */
@Getter @Getter
public enum GroupOffsetResetEnum { public enum OffsetTypeEnum {
LATEST(0, "重置到最新"), LATEST(0, "最新"),
EARLIEST(1, "重置到最旧"), EARLIEST(1, "最旧"),
PRECISE_TIMESTAMP(2, "按时间进行重置"), PRECISE_TIMESTAMP(2, "指定时间"),
PRECISE_OFFSET(3, "重置到指定位置"), PRECISE_OFFSET(3, "指定位置"),
; ;
@@ -23,7 +23,7 @@ public enum GroupOffsetResetEnum {
private final String message; private final String message;
GroupOffsetResetEnum(int resetType, String message) { OffsetTypeEnum(int resetType, String message) {
this.resetType = resetType; this.resetType = resetType;
this.message = message; this.message = message;
} }

View File

@@ -90,6 +90,8 @@ public class JmxConnectorWrap {
} }
try { try {
jmxConnector.close(); jmxConnector.close();
jmxConnector = null;
} catch (IOException e) { } catch (IOException e) {
LOGGER.warn("close JmxConnector exception, physicalClusterId:{} brokerId:{} host:{} port:{}.", physicalClusterId, brokerId, host, port, e); LOGGER.warn("close JmxConnector exception, physicalClusterId:{} brokerId:{} host:{} port:{}.", physicalClusterId, brokerId, host, port, e);
} }
@@ -105,6 +107,11 @@ public class JmxConnectorWrap {
acquire(); acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.getAttribute(name, attribute); return mBeanServerConnection.getAttribute(name, attribute);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally { } finally {
atomicInteger.incrementAndGet(); atomicInteger.incrementAndGet();
} }
@@ -120,6 +127,11 @@ public class JmxConnectorWrap {
acquire(); acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.getAttributes(name, attributes); return mBeanServerConnection.getAttributes(name, attributes);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally { } finally {
atomicInteger.incrementAndGet(); atomicInteger.incrementAndGet();
} }
@@ -131,6 +143,11 @@ public class JmxConnectorWrap {
acquire(); acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.queryNames(name, query); return mBeanServerConnection.queryNames(name, query);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally { } finally {
atomicInteger.incrementAndGet(); atomicInteger.incrementAndGet();
} }
@@ -186,4 +203,26 @@ public class JmxConnectorWrap {
} }
} }
} }
private synchronized void reInitDueIOException() {
try {
if (jmxConnector == null) {
return;
}
// 检查是否正常
jmxConnector.getConnectionId();
// 如果正常则直接返回
return;
} catch (Exception e) {
// ignore
}
// 关闭旧的
this.close();
// 重新创建
this.checkJmxConnectionAndInitIfNeed();
}
} }

View File

@@ -5100,9 +5100,9 @@
} }
}, },
"is-callable": { "is-callable": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz", "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==", "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==",
"dev": true "dev": true
}, },
"is-ci": { "is-ci": {

View File

@@ -1,205 +0,0 @@
/* eslint-disable */
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const theme = require('./theme');
var cwd = process.cwd();
const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
require.resolve('@babel/plugin-proposal-optional-chaining'), //
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), // 解决 ?? 无法转义问题
require.resolve('@babel/plugin-proposal-numeric-separator'), // 转义 1_000_000
!isProd && require.resolve('react-refresh/babel'),
]
.filter(Boolean)
.concat([
[
'babel-plugin-import',
{
libraryName: 'antd',
style: true,
},
],
'@babel/plugin-transform-object-assign',
]),
};
module.exports = () => {
const manifestName = `manifest.json`;
const cssFileName = isProd ? '[name]-[chunkhash].css' : '[name].css';
const plugins = [
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new MiniCssExtractPlugin({
filename: cssFileName,
}),
new StatsPlugin(manifestName, {
chunkModules: false,
source: true,
chunks: false,
modules: false,
assets: true,
children: false,
exclude: [/node_modules/],
}),
new HappyPack({
id: 'babel',
loaders: [
'cache-loader',
{
loader: 'babel-loader',
options: babelOptions,
},
],
threadPool: happyThreadPool,
}),
!isProd &&
new ReactRefreshWebpackPlugin({
overlay: false,
}),
// new BundleAnalyzerPlugin({
// analyzerPort: 8889
// }),
].filter(Boolean);
if (isProd) {
plugins.push(new CleanWebpackPlugin());
}
return {
externals: isProd
? [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^antd$/,
/^lodash$/,
/^react-router$/,
/^react-router-dom$/,
]
: [],
resolve: {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
// '@pkgs': path.resolve(cwd, 'src/packages'),
'@pkgs': path.resolve(cwd, './node_modules/@didi/d1-packages'),
'@cpts': path.resolve(cwd, 'src/components'),
'@interface': path.resolve(cwd, 'src/interface'),
'@apis': path.resolve(cwd, 'src/api'),
react: path.resolve('./node_modules/react'),
actions: path.resolve(cwd, 'src/actions'),
lib: path.resolve(cwd, 'src/lib'),
constants: path.resolve(cwd, 'src/constants'),
components: path.resolve(cwd, 'src/components'),
container: path.resolve(cwd, 'src/container'),
api: path.resolve(cwd, 'src/api'),
assets: path.resolve(cwd, 'src/assets'),
mobxStore: path.resolve(cwd, 'src/mobxStore'),
},
},
plugins,
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules\/(?!react-intl|@didi\/dcloud-design)/,
use: [
{
loader: 'happypack/loader?id=babel',
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
optimization: Object.assign(
{
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3500000,
},
},
},
},
isProd
? {
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
}
: {}
),
devtool: isProd ? 'cheap-module-source-map' : 'source-map',
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
};
};

View File

@@ -0,0 +1,132 @@
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const theme = require('./theme');
const pkgJson = require('../package');
const devMode = process.env.NODE_ENV === 'development';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-property-in-object'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
require.resolve('@babel/plugin-proposal-optional-chaining'), //
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), // 解决 ?? 无法转义问题
require.resolve('@babel/plugin-proposal-numeric-separator'), // 转义 1_000_000
devMode && require.resolve('react-refresh/babel'),
].filter(Boolean),
};
module.exports = {
entry: {
[pkgJson.ident]: ['./src/index.tsx'],
},
resolve: {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
'@src': path.resolve(process.cwd(), 'src'),
},
},
plugins: [
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new StatsPlugin('manifest.json', {
chunkModules: false,
source: true,
chunks: false,
modules: false,
assets: true,
children: false,
exclude: [/node_modules/],
}),
new HappyPack({
id: 'babel',
loaders: [
'cache-loader',
{
loader: 'babel-loader',
options: babelOptions,
},
],
threadPool: happyThreadPool,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
inject: 'body',
}),
].filter(Boolean),
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules\/(?!react-intl|@didi\/dcloud-design)/,
use: [
{
loader: 'happypack/loader?id=babel',
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
stats: 'errors-warnings',
};

View File

@@ -0,0 +1,35 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const pkgJson = require('../package');
module.exports = {
mode: 'development',
plugins: [
new MiniCssExtractPlugin(),
new ReactRefreshWebpackPlugin({
overlay: false,
}),
],
devServer: {
host: '127.0.0.1',
port: pkgJson.port,
hot: true,
open: false,
publicPath: `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
inline: true,
disableHostCheck: true,
historyApiFallback: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
output: {
path: '/',
publicPath: `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
library: pkgJson.ident,
libraryTarget: 'amd',
filename: '[name].js',
chunkFilename: '[name].js',
},
devtool: 'cheap-module-eval-source-map',
};

View File

@@ -0,0 +1,59 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const pkgJson = require('../package');
module.exports = {
mode: 'production',
externals: [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^lodash$/,
/^react-router$/,
/^react-router-dom$/,
],
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name]-[chunkhash].css',
}),
],
output: {
path: path.resolve(process.cwd(), `../../../km-rest/src/main/resources/templates/${pkgJson.ident}`),
publicPath: `${process.env.PUBLIC_PATH}/${pkgJson.ident}/`,
library: pkgJson.ident,
libraryTarget: 'amd',
filename: '[name]-[chunkhash].js',
chunkFilename: '[name]-[chunkhash].js',
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3500000,
},
},
},
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'none',
};

View File

@@ -1344,6 +1344,16 @@
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
} }
}, },
"@knowdesign/icons": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/@knowdesign/icons/-/icons-1.0.0.tgz",
"integrity": "sha512-7c+h2TSbh2ihTkXIivuO+DddNC5wG7hVv9SS4ccmkvTKls2ZTLitPu+U0wpufDxPhkPMaKEQfsECsVJ+7jLMiw==",
"requires": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",
"react": "16.12.0"
}
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -6815,9 +6825,9 @@
"dev": true "dev": true
}, },
"is-callable": { "is-callable": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz", "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==", "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==",
"dev": true "dev": true
}, },
"is-color-stop": { "is-color-stop": {

View File

@@ -21,9 +21,11 @@
"build": "cross-env NODE_ENV=production webpack --max_old_space_size=8000" "build": "cross-env NODE_ENV=production webpack --max_old_space_size=8000"
}, },
"dependencies": { "dependencies": {
"@knowdesign/icons": "^1.0.0",
"babel-preset-react-app": "^10.0.0", "babel-preset-react-app": "^10.0.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"knowdesign": "1.3.7",
"less": "^3.9.0", "less": "^3.9.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"mobx": "4.15.7", "mobx": "4.15.7",
@@ -36,8 +38,7 @@
"react-intl": "^3.2.1", "react-intl": "^3.2.1",
"react-router-cache-route": "^1.11.1", "react-router-cache-route": "^1.11.1",
"single-spa": "^5.8.0", "single-spa": "^5.8.0",
"single-spa-react": "^2.14.0", "single-spa-react": "^2.14.0"
"knowdesign": "1.3.7"
}, },
"devDependencies": { "devDependencies": {
"@ant-design/icons": "^4.6.2", "@ant-design/icons": "^4.6.2",

View File

@@ -22,6 +22,20 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-bottom: 12px; margin-bottom: 12px;
.left,
.right {
display: flex;
align-items: center;
}
.left .refresh-icon {
font-size: 20px;
color: #74788d;
cursor: pointer;
}
.right .search-input {
width: 248px;
margin-right: 8px;
}
} }
} }
} }

View File

@@ -47,8 +47,8 @@ serviceInstance.interceptors.response.use(
return res; return res;
}, },
(err: any) => { (err: any) => {
const config = err.config; const config = err?.config;
if (!config || !config.retryTimes) return dealResponse(err, config.customNotification); if (!config || !config.retryTimes) return dealResponse(err);
const { __retryCount = 0, retryDelay = 300, retryTimes } = config; const { __retryCount = 0, retryDelay = 300, retryTimes } = config;
config.__retryCount = __retryCount; config.__retryCount = __retryCount;
if (__retryCount >= retryTimes) { if (__retryCount >= retryTimes) {

View File

@@ -1,6 +1,6 @@
import React, { useLayoutEffect } from 'react'; import React, { useLayoutEffect } from 'react';
import { Utils, AppContainer } from 'knowdesign'; import { Utils, AppContainer } from 'knowdesign';
import { goLogin } from 'constants/axiosConfig'; import { goLogin } from '@src/constants/axiosConfig';
// 权限对应表 // 权限对应表
export enum ConfigPermissionMap { export enum ConfigPermissionMap {

View File

@@ -15,6 +15,7 @@ import {
AppContainer, AppContainer,
Utils, Utils,
} from 'knowdesign'; } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
// 引入代码编辑器 // 引入代码编辑器
@@ -26,8 +27,8 @@ import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/edit/closebrackets'; import 'codemirror/addon/edit/closebrackets';
require('codemirror/mode/xml/xml'); require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript'); require('codemirror/mode/javascript/javascript');
import api from 'api'; import api from '@src/api';
import { defaultPagination } from 'constants/common'; import { defaultPagination } from '@src/constants/common';
import TypicalListCard from '../../components/TypicalListCard'; import TypicalListCard from '../../components/TypicalListCard';
import { ConfigPermissionMap } from '../CommonConfig'; import { ConfigPermissionMap } from '../CommonConfig';
import { ConfigOperate, ConfigProps } from './config'; import { ConfigOperate, ConfigProps } from './config';
@@ -384,7 +385,7 @@ export default () => {
const onDelete = (record: ConfigProps) => { const onDelete = (record: ConfigProps) => {
confirm({ confirm({
title: '确定删除配置吗?', title: '确定删除配置吗?',
content: `配置${record.valueName}${record.status === 1 ? '为启用状态,无法删除' : ''}`, content: `配置 [${record.valueName}] ${record.status === 1 ? '为启用状态,无法删除' : ''}`,
centered: true, centered: true,
okText: '删除', okText: '删除',
okType: 'primary', okType: 'primary',
@@ -398,9 +399,11 @@ export default () => {
}, },
maskClosable: true, maskClosable: true,
onOk() { onOk() {
return request(api.editConfig, { return request(api.delConfig, {
method: 'POST', method: 'DELETE',
data: record.id, params: {
id: record.id,
},
}).then((_) => { }).then((_) => {
message.success('删除成功'); message.success('删除成功');
getConfigList(); getConfigList();
@@ -431,22 +434,28 @@ export default () => {
<TypicalListCard title="配置管理"> <TypicalListCard title="配置管理">
<div className="config-manage-page"> <div className="config-manage-page">
<div className="operate-bar"> <div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getConfigList({ page: 1 })}> <div className="left">
<Form.Item name="valueGroup"> <div className="refresh-icon" onClick={() => getConfigList()}>
<Select style={{ width: 180 }} placeholder="请选择模块" options={configGroupList} /> <IconFont className="icon" type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="valueName"> <Divider type="vertical" style={{ height: 20, top: 0 }} />
<Input style={{ width: 180 }} placeholder="请输入配置键" /> <Form form={form} layout="inline" onFinish={() => getConfigList({ page: 1 })}>
</Form.Item> <Form.Item name="valueGroup">
<Form.Item name="memo"> <Select style={{ width: 180 }} placeholder="请选择模块" options={configGroupList} />
<Input style={{ width: 180 }} placeholder="请输入描述" /> </Form.Item>
</Form.Item> <Form.Item name="valueName">
<Form.Item> <Input style={{ width: 180 }} placeholder="请输入配置键" />
<Button type="primary" ghost htmlType="submit"> </Form.Item>
<Form.Item name="memo">
</Button> <Input style={{ width: 180 }} placeholder="请输入描述" />
</Form.Item> </Form.Item>
</Form> <Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
{global.hasPermission && global.hasPermission(ConfigPermissionMap.CONFIG_ADD) ? ( {global.hasPermission && global.hasPermission(ConfigPermissionMap.CONFIG_ADD) ? (
<Button <Button
type="primary" type="primary"

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, Form, Input, Select, ProTable, DatePicker, Utils, Tooltip } from 'knowdesign'; import { Button, Form, Input, Select, ProTable, DatePicker, Utils, Tooltip, Divider } from 'knowdesign';
import api from 'api'; import { IconFont } from '@knowdesign/icons';
import { defaultPagination } from 'constants/common'; import api from '@src/api';
import { defaultPagination } from '@src/constants/common';
import TypicalListCard from '../../components/TypicalListCard'; import TypicalListCard from '../../components/TypicalListCard';
import './index.less'; import './index.less';
import moment from 'moment'; import moment from 'moment';
@@ -119,25 +120,32 @@ export default () => {
<> <>
<TypicalListCard title="操作记录"> <TypicalListCard title="操作记录">
<div className="operate-bar"> <div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getData({ page: 1 })}> <div className="left">
<Form.Item name="targetType"> <div className="refresh-icon" onClick={() => getData()}>
<Select placeholder="请选择模块" options={configGroupList} style={{ width: 160 }} /> <IconFont className="icon" type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="target"> <Divider type="vertical" style={{ height: 20, top: 0 }} />
<Input placeholder="请输入操作对象" />
</Form.Item> <Form form={form} layout="inline" onFinish={() => getData({ page: 1 })}>
<Form.Item name="detail"> <Form.Item name="targetType">
<Input placeholder="请输入操作内容" /> <Select placeholder="请选择模块" options={configGroupList} style={{ width: 160 }} />
</Form.Item> </Form.Item>
<Form.Item name="time"> <Form.Item name="target">
<RangePicker showTime /> <Input placeholder="请输入操作对象" />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item name="detail">
<Button type="primary" ghost htmlType="submit"> <Input placeholder="请输入操作内容" />
</Form.Item>
</Button> <Form.Item name="time">
</Form.Item> <RangePicker showTime />
</Form> </Form.Item>
<Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
</div> </div>
<ProTable <ProTable

View File

@@ -21,9 +21,9 @@ import {
} from 'knowdesign'; } from 'knowdesign';
import moment from 'moment'; import moment from 'moment';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'; import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import { defaultPagination } from 'constants/common'; import { defaultPagination } from '@src/constants/common';
import { RoleProps, PermissionNode, AssignUser, RoleOperate, FormItemPermission } from './config'; import { RoleProps, PermissionNode, AssignUser, RoleOperate, FormItemPermission } from './config';
import api from 'api'; import api from '@src/api';
import CheckboxGroupContainer from './CheckboxGroupContainer'; import CheckboxGroupContainer from './CheckboxGroupContainer';
import { ConfigPermissionMap } from '../CommonConfig'; import { ConfigPermissionMap } from '../CommonConfig';
@@ -611,38 +611,45 @@ export default (props: { curTabKey: string }): JSX.Element => {
return ( return (
<> <>
<div className="operate-bar-right"> <div className="operate-bar">
<Input <div className="left">
className="search-input" <div className="refresh-icon" onClick={() => getRoleList()}>
suffix={ <IconFont className="icon" type="icon-shuaxin1" />
<IconFont </div>
type="icon-fangdajing" </div>
onClick={(_) => { <div className="right">
setSearchKeywords(searchKeywordsInput); <Input
}} className="search-input"
style={{ fontSize: '16px' }} suffix={
/> <IconFont
} type="icon-fangdajing"
placeholder="请输入角色名称" onClick={(_) => {
value={searchKeywordsInput} setSearchKeywords(searchKeywordsInput);
onPressEnter={(_) => { }}
setSearchKeywords(searchKeywordsInput); style={{ fontSize: '16px' }}
}} />
onChange={(e) => { }
setSearchKeywordsInput(e.target.value); placeholder="请输入角色名称"
}} value={searchKeywordsInput}
/> onPressEnter={(_) => {
{global.hasPermission && global.hasPermission(ConfigPermissionMap.ROLE_ADD) ? ( setSearchKeywords(searchKeywordsInput);
<Button }}
type="primary" onChange={(e) => {
icon={<PlusOutlined />} setSearchKeywordsInput(e.target.value);
onClick={() => detailRef.current.onOpen(true, RoleOperate.Add, getRoleList, undefined)} }}
> />
{global.hasPermission && global.hasPermission(ConfigPermissionMap.ROLE_ADD) ? (
</Button> <Button
) : ( type="primary"
<></> icon={<PlusOutlined />}
)} onClick={() => detailRef.current.onOpen(true, RoleOperate.Add, getRoleList, undefined)}
>
</Button>
) : (
<></>
)}
</div>
</div> </div>
<ProTable <ProTable

View File

@@ -1,12 +1,13 @@
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Form, ProTable, Select, Button, Input, Modal, message, Drawer, Space, Divider, AppContainer, Utils } from 'knowdesign'; import { Form, ProTable, Select, Button, Input, Modal, message, Drawer, Space, Divider, AppContainer, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
import { defaultPagination } from 'constants/common'; import { defaultPagination } from '@src/constants/common';
import { UserProps, UserOperate } from './config'; import { UserProps, UserOperate } from './config';
import CheckboxGroupContainer from './CheckboxGroupContainer'; import CheckboxGroupContainer from './CheckboxGroupContainer';
import TagsWithHide from '../../components/TagsWithHide/index'; import TagsWithHide from '../../components/TagsWithHide/index';
import api from 'api'; import api from '@src/api';
import { ConfigPermissionMap } from '../CommonConfig'; import { ConfigPermissionMap } from '../CommonConfig';
const { confirm } = Modal; const { confirm } = Modal;
@@ -341,22 +342,29 @@ export default (props: { curTabKey: string }) => {
return ( return (
<> <>
<div className="operate-bar"> <div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getUserList({ page: 1 })}> <div className="left">
<Form.Item name="userName"> <div className="refresh-icon" onClick={() => getUserList()}>
<Input placeholder="请输入用户账号" /> <IconFont className="icon" type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="realName"> <Divider type="vertical" style={{ height: 20, top: 0 }} />
<Input placeholder="请输入用户实名" />
</Form.Item> <Form form={form} layout="inline" onFinish={() => getUserList({ page: 1 })}>
<Form.Item name="roleId"> <Form.Item name="userName">
<Select style={{ width: 190 }} placeholder="选择平台已创建的角色名" options={simpleRoleList} /> <Input placeholder="请输入用户账号" />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item name="realName">
<Button type="primary" ghost htmlType="submit"> <Input placeholder="请输入用户实名" />
</Form.Item>
</Button> <Form.Item name="roleId">
</Form.Item> <Select style={{ width: 190 }} placeholder="选择平台已创建的角色名" options={simpleRoleList} />
</Form> </Form.Item>
<Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
{global.hasPermission && global.hasPermission(ConfigPermissionMap.USER_ADD) ? ( {global.hasPermission && global.hasPermission(ConfigPermissionMap.USER_ADD) ? (
<Button <Button
type="primary" type="primary"

View File

@@ -44,13 +44,3 @@
.role-tab-assign-user .desc-row { .role-tab-assign-user .desc-row {
margin-bottom: 24px; margin-bottom: 24px;
} }
.operate-bar-right {
display: flex;
justify-content: right;
margin-bottom: 12px;
.search-input {
width: 248px;
margin-right: 8px;
}
}

View File

@@ -1,56 +1,9 @@
/* eslint-disable */
const path = require('path'); const path = require('path');
require('dotenv').config({ path: path.resolve(process.cwd(), '../../.env') }); require('dotenv').config({ path: path.resolve(process.cwd(), '../../.env') });
const isProd = process.env.NODE_ENV === 'production';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const pkgJson = require('./package'); const devMode = process.env.NODE_ENV === 'development';
const getWebpackCommonConfig = require('./config/d1-webpack.base'); const commonConfig = require('./config/webpack.common');
const outPath = path.resolve(__dirname, `../../../km-rest/src/main/resources/templates/${pkgJson.ident}`); const devConfig = require('./config/webpack.dev');
const jsFileName = isProd ? '[name]-[chunkhash].js' : '[name].js'; const prodConfig = require('./config/webpack.prod');
module.exports = merge(getWebpackCommonConfig(), { module.exports = merge(commonConfig, devMode ? devConfig : prodConfig);
mode: isProd ? 'production' : 'development',
entry: {
[pkgJson.ident]: ['./src/index.tsx'],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
inject: 'body',
}),
],
output: {
path: outPath,
publicPath: isProd ? `${process.env.PUBLIC_PATH}/${pkgJson.ident}/` : `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
library: pkgJson.ident,
libraryTarget: 'amd',
filename: jsFileName,
chunkFilename: jsFileName,
},
devtool: isProd ? 'none' : 'cheap-module-eval-source-map',
devServer: {
host: '127.0.0.1',
port: pkgJson.port,
hot: true,
open: false,
publicPath: `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
inline: true,
disableHostCheck: true,
historyApiFallback: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {},
},
});

View File

@@ -1,183 +0,0 @@
/* eslint-disable */
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CoverHtmlWebpackPlugin = require('./CoverHtmlWebpackPlugin.js');
var webpackConfigResolveAlias = require('./webpackConfigResolveAlias');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const theme = require('./theme');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const isProd = process.env.NODE_ENV === 'production';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-property-in-object'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
!isProd && require.resolve('react-refresh/babel'),
]
.filter(Boolean)
.concat([
[
'babel-plugin-import',
{
libraryName: 'antd',
style: true,
},
],
'@babel/plugin-transform-object-assign',
]),
};
module.exports = () => {
const cssFileName = isProd ? '[name]-[chunkhash].css' : '[name].css';
const plugins = [
new CoverHtmlWebpackPlugin(),
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new MiniCssExtractPlugin({
filename: cssFileName,
}),
!isProd &&
new ReactRefreshWebpackPlugin({
overlay: false,
}),
].filter(Boolean);
const resolve = {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: webpackConfigResolveAlias,
};
if (isProd) {
plugins.push(new CleanWebpackPlugin());
}
if (!isProd) {
resolve.mainFields = ['module', 'browser', 'main'];
}
return {
externals: isProd
? [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^antd$/,
/^lodash$/,
/^echarts$/,
/^react-router$/,
/^react-router-dom$/,
]
: [],
resolve,
plugins,
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
],
},
{
test: /\.(ts|tsx)$/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
{
loader: 'ts-loader',
options: {
allowTsInNodeModules: true,
},
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
optimization: Object.assign(
isProd
? {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3000000,
},
},
},
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
}
: {}
),
devtool: isProd ? 'cheap-module-source-map' : '',
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
};
};

View File

@@ -0,0 +1,123 @@
const path = require('path');
const theme = require('./theme');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CoverHtmlWebpackPlugin = require('./CoverHtmlWebpackPlugin.js');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const devMode = process.env.NODE_ENV === 'development';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-property-in-object'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
devMode && require.resolve('react-refresh/babel'),
devMode && [
'babel-plugin-import',
{
libraryName: 'antd',
style: true,
},
],
].filter(Boolean),
};
module.exports = {
entry: {
layout: ['./src/index.tsx'],
},
resolve: {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
'@src': path.resolve('src'),
},
},
plugins: [
new CoverHtmlWebpackPlugin(),
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
BUSINESS_VERSION: process.env.BUSINESS_VERSION === 'true',
PUBLIC_PATH: JSON.stringify(process.env.PUBLIC_PATH),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
favicon: path.resolve('favicon.ico'),
inject: 'body',
}),
],
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
{
loader: 'ts-loader',
options: {
allowTsInNodeModules: true,
},
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
stats: 'errors-warnings',
};

View File

@@ -0,0 +1,45 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
mode: 'development',
plugins: [
new MiniCssExtractPlugin(),
new ReactRefreshWebpackPlugin({
overlay: false,
}),
],
output: {
path: '/',
publicPath: '/',
filename: '[name].js',
chunkFilename: '[name].js',
library: 'layout',
libraryTarget: 'amd',
},
devServer: {
host: 'localhost',
port: 8000,
hot: true,
open: true,
openPage: 'http://localhost:8000/',
inline: true,
historyApiFallback: true,
publicPath: `http://localhost:8000/`,
headers: {
'cache-control': 'no-cache',
pragma: 'no-cache',
'Access-Control-Allow-Origin': '*',
},
proxy: {
'/ks-km/api/v3': {
changeOrigin: true,
target: 'http://localhost:8080/',
},
'/logi-security/api/v1': {
changeOrigin: true,
target: 'http://localhost:8080/',
},
},
},
};

View File

@@ -0,0 +1,79 @@
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CountPlugin = require('./CountComponentWebpackPlugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const outputPath = path.resolve(process.cwd(), `../../../km-rest/src/main/resources/templates/layout`);
module.exports = {
mode: 'production',
plugins: [
new CleanWebpackPlugin(),
new CountPlugin({
pathname: 'knowdesign',
startCount: true,
isExportExcel: false,
}),
new MiniCssExtractPlugin({
filename: '[name]-[chunkhash].css',
}),
new CopyWebpackPlugin([
{
from: path.resolve(process.cwd(), 'static'),
to: path.resolve(outputPath, '../static'),
},
{
from: path.resolve(process.cwd(), 'favicon.ico'),
to: path.resolve(outputPath, '../favicon.ico'),
},
]),
],
externals: [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^antd$/,
/^lodash$/,
/^echarts$/,
/^react-router$/,
/^react-router-dom$/,
],
output: {
path: outputPath,
publicPath: process.env.PUBLIC_PATH + '/layout/',
filename: '[name]-[chunkhash].js',
chunkFilename: '[name]-[chunkhash].js',
library: 'layout',
libraryTarget: 'amd',
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3000000,
},
},
},
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'none',
};

View File

@@ -1,5 +0,0 @@
var path = require('path');
module.exports = {
react: path.resolve('./node_modules/react'),
};

View File

@@ -1387,6 +1387,16 @@
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
} }
}, },
"@knowdesign/icons": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/@knowdesign/icons/-/icons-1.0.1.tgz",
"integrity": "sha512-EI3s25BJt+Slv7/t6B3K3zv7I6TKkk2Wf1y68zuxK80MMkWf8lqqUtyAZbFDoPUfXAjw6vHktMBH44gbMHMRFA==",
"requires": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",
"react": "16.12.0"
}
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -6024,6 +6034,12 @@
} }
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -6562,6 +6578,12 @@
"kind-of": "^4.0.0" "kind-of": "^4.0.0"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -6638,18 +6660,6 @@
"resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", "resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="
}, },
"hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
}
},
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
@@ -6666,11 +6676,6 @@
"resolved": "https://registry.npmmirror.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz",
"integrity": "sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg==" "integrity": "sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg=="
}, },
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
},
"history": { "history": {
"version": "4.10.1", "version": "4.10.1",
"resolved": "https://registry.npmmirror.com/history/-/history-4.10.1.tgz", "resolved": "https://registry.npmmirror.com/history/-/history-4.10.1.tgz",
@@ -6883,6 +6888,12 @@
} }
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -7224,28 +7235,6 @@
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true "dev": true
}, },
"intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmmirror.com/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"requires": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
}
},
"invariant": { "invariant": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz", "resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz",
@@ -7287,6 +7276,12 @@
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -7354,16 +7349,10 @@
"has-tostringtag": "^1.0.0" "has-tostringtag": "^1.0.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-callable": { "is-callable": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz", "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==" "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q=="
}, },
"is-color-stop": { "is-color-stop": {
"version": "1.1.0", "version": "1.1.0",
@@ -7396,6 +7385,12 @@
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -7478,9 +7473,9 @@
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
}, },
"is-lite": { "is-lite": {
"version": "0.8.2", "version": "0.9.2",
"resolved": "https://registry.npmmirror.com/is-lite/-/is-lite-0.8.2.tgz", "resolved": "https://registry.npmmirror.com/is-lite/-/is-lite-0.9.2.tgz",
"integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==" "integrity": "sha512-qZuxbaEiKLOKhX4sbHLfhFN9iA3YciuZLb37/DfXCpWnz8p7qNL2lwkpxYMXfjlS8eEEjpULPZxAUI8N6FYvYQ=="
}, },
"is-negative-zero": { "is-negative-zero": {
"version": "2.0.2", "version": "2.0.2",
@@ -7531,12 +7526,6 @@
"path-is-inside": "^1.0.2" "path-is-inside": "^1.0.2"
} }
}, },
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
"dev": true
},
"is-plain-object": { "is-plain-object": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -7849,9 +7838,9 @@
}, },
"dependencies": { "dependencies": {
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==", "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": { "requires": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"react-is": "^16.12.0", "react-is": "^16.12.0",
@@ -8287,6 +8276,13 @@
"requires": { "requires": {
"fault": "^1.0.0", "fault": "^1.0.0",
"highlight.js": "~10.7.0" "highlight.js": "~10.7.0"
},
"dependencies": {
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
}
} }
}, },
"lru-cache": { "lru-cache": {
@@ -8799,6 +8795,12 @@
"is-descriptor": "^0.1.0" "is-descriptor": "^0.1.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -9092,19 +9094,6 @@
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
}, },
"parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"requires": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
}
},
"parse-json": { "parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
@@ -10259,9 +10248,9 @@
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
}, },
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==", "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": { "requires": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"react-is": "^16.12.0", "react-is": "^16.12.0",
@@ -10385,9 +10374,9 @@
}, },
"dependencies": { "dependencies": {
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==", "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": { "requires": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"react-is": "^16.12.0", "react-is": "^16.12.0",
@@ -10661,9 +10650,9 @@
}, },
"dependencies": { "dependencies": {
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==", "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": { "requires": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"react-is": "^16.12.0", "react-is": "^16.12.0",
@@ -10757,9 +10746,9 @@
}, },
"dependencies": { "dependencies": {
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==", "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": { "requires": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"react-is": "^16.12.0", "react-is": "^16.12.0",
@@ -10780,6 +10769,18 @@
"rc-util": "^5.7.0" "rc-util": "^5.7.0"
}, },
"dependencies": { "dependencies": {
"rc-tree": {
"version": "5.3.8",
"resolved": "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.3.8.tgz",
"integrity": "sha512-YuobEryPymqPmHFUOvsoOrYdm24psaj0CrGEUuDUQUeG/nNcTGw6FA2YmF4NsEaNBvNSJUSzwfZnFHrKa/xv0A==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"rc-motion": "^2.0.1",
"rc-util": "^5.16.1",
"rc-virtual-list": "^3.4.1"
}
},
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
@@ -11045,6 +11046,13 @@
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-proptype-conditional-require": "^1.0.4", "react-proptype-conditional-require": "^1.0.4",
"tree-changes": "^0.9.1" "tree-changes": "^0.9.1"
},
"dependencies": {
"is-lite": {
"version": "0.8.2",
"resolved": "https://registry.npmmirror.com/is-lite/-/is-lite-0.8.2.tgz",
"integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw=="
}
} }
}, },
"react-freeze": { "react-freeze": {
@@ -11122,6 +11130,30 @@
"intl-messageformat": "^7.8.4", "intl-messageformat": "^7.8.4",
"intl-messageformat-parser": "^3.6.4", "intl-messageformat-parser": "^3.6.4",
"shallow-equal": "^1.2.1" "shallow-equal": "^1.2.1"
},
"dependencies": {
"intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmmirror.com/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"requires": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
}
}
} }
}, },
"react-is": { "react-is": {
@@ -11130,19 +11162,19 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"react-joyride": { "react-joyride": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmmirror.com/react-joyride/-/react-joyride-2.5.2.tgz", "resolved": "https://registry.npmmirror.com/react-joyride/-/react-joyride-2.5.3.tgz",
"integrity": "sha512-wsSYX3PhVrdzdd0/fv5f6ySGvb7QyAzleQv/x9IH+x+SXO6b5MUJUkefS+189bgLPkuHMwtSRcPE/oMupfmCVQ==", "integrity": "sha512-DKKvb/JAAsHm0x/RWO3WI6NOtTMHDso5v8MTauxTSz2dFs7Tu1rWg1BDBWmEMj6pUCvem7hblFbCiDAcvhs8tQ==",
"requires": { "requires": {
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"exenv": "^1.2.2", "exenv": "^1.2.2",
"is-lite": "^0.8.2", "is-lite": "^0.9.2",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-floater": "^0.7.6", "react-floater": "^0.7.6",
"react-is": "^16.13.1", "react-is": "^16.13.1",
"scroll": "^3.0.1", "scroll": "^3.0.1",
"scrollparent": "^2.0.1", "scrollparent": "^2.0.1",
"tree-changes": "^0.9.1" "tree-changes": "^0.9.2"
} }
}, },
"react-lifecycles-compat": { "react-lifecycles-compat": {
@@ -11282,6 +11314,13 @@
"lowlight": "^1.14.0", "lowlight": "^1.14.0",
"prismjs": "^1.21.0", "prismjs": "^1.21.0",
"refractor": "^3.0.0" "refractor": "^3.0.0"
},
"dependencies": {
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
}
} }
}, },
"react-test-renderer": { "react-test-renderer": {
@@ -11449,6 +11488,31 @@
"prismjs": "~1.27.0" "prismjs": "~1.27.0"
}, },
"dependencies": { "dependencies": {
"hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
}
},
"parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"requires": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
}
},
"prismjs": { "prismjs": {
"version": "1.27.0", "version": "1.27.0",
"resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz", "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz",
@@ -12286,6 +12350,12 @@
"kind-of": "^3.2.0" "kind-of": "^3.2.0"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -12347,6 +12417,14 @@
"dev": true, "dev": true,
"requires": { "requires": {
"is-plain-obj": "^1.0.0" "is-plain-obj": "^1.0.0"
},
"dependencies": {
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
"dev": true
}
} }
}, },
"source-list-map": { "source-list-map": {
@@ -13049,6 +13127,12 @@
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -13098,12 +13182,19 @@
"integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g=="
}, },
"tree-changes": { "tree-changes": {
"version": "0.9.1", "version": "0.9.3",
"resolved": "https://registry.npmmirror.com/tree-changes/-/tree-changes-0.9.1.tgz", "resolved": "https://registry.npmmirror.com/tree-changes/-/tree-changes-0.9.3.tgz",
"integrity": "sha512-Un6R1T6eUStAVbN4G+2djuXEk271mDY78ptxZUUo+TVcwvHZeUgk+pwXZjOZLAJ9n0+p47KUijeuNJSmpuG6Dw==", "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==",
"requires": { "requires": {
"@gilbarbara/deep-equal": "^0.1.1", "@gilbarbara/deep-equal": "^0.1.1",
"is-lite": "^0.8.2" "is-lite": "^0.8.2"
},
"dependencies": {
"is-lite": {
"version": "0.8.2",
"resolved": "https://registry.npmmirror.com/is-lite/-/is-lite-0.8.2.tgz",
"integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw=="
}
} }
}, },
"ts-loader": { "ts-loader": {
@@ -13502,12 +13593,6 @@
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"dev": true "dev": true
}, },
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
},
"v8-compile-cache": { "v8-compile-cache": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@@ -13718,6 +13803,13 @@
"binary-extensions": "^1.0.0" "binary-extensions": "^1.0.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true,
"optional": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -13895,6 +13987,12 @@
} }
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -14338,6 +14436,12 @@
"binary-extensions": "^1.0.0" "binary-extensions": "^1.0.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -14439,6 +14543,14 @@
"requires": { "requires": {
"ansi-colors": "^3.0.0", "ansi-colors": "^3.0.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
} }
}, },
"webpack-merge": { "webpack-merge": {

View File

@@ -35,6 +35,7 @@
"dependencies": { "dependencies": {
"@ant-design/compatible": "^1.0.8", "@ant-design/compatible": "^1.0.8",
"@ant-design/icons": "^4.6.2", "@ant-design/icons": "^4.6.2",
"@knowdesign/icons": "^1.0.1",
"@types/react": "^17.0.39", "@types/react": "^17.0.39",
"@types/react-copy-to-clipboard": "^5.0.2", "@types/react-copy-to-clipboard": "^5.0.2",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
@@ -49,6 +50,7 @@
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"html-webpack-plugin": "^4.0.0", "html-webpack-plugin": "^4.0.0",
"knowdesign": "^1.3.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.24.0", "moment": "^2.24.0",
"react": "16.12.0", "react": "16.12.0",
@@ -56,12 +58,10 @@
"react-cron-antd": "^1.1.2", "react-cron-antd": "^1.1.2",
"react-dom": "16.12.0", "react-dom": "16.12.0",
"react-intl": "^3.2.1", "react-intl": "^3.2.1",
"react-joyride": "^2.5.0", "react-joyride": "^2.5.3",
"single-spa": "5.9.3", "single-spa": "5.9.3",
"single-spa-react": "2.14.0", "single-spa-react": "2.14.0",
"webpack-bundle-analyzer": "^4.5.0", "webpack-bundle-analyzer": "^4.5.0"
"knowdesign": "1.3.7",
"tree-changes": "0.9.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.5.5", "@babel/core": "^7.5.5",

View File

@@ -3,7 +3,8 @@ import '@babel/polyfill';
import React, { useState, useEffect, useLayoutEffect } from 'react'; import React, { useState, useEffect, useLayoutEffect } from 'react';
import { BrowserRouter, Switch, Route, useLocation, useHistory } from 'react-router-dom'; import { BrowserRouter, Switch, Route, useLocation, useHistory } from 'react-router-dom';
import { get as lodashGet } from 'lodash'; import { get as lodashGet } from 'lodash';
import { DProLayout, AppContainer, IconFont, Menu, Utils, Page403, Page404, Page500, Modal } from 'knowdesign'; import { DProLayout, AppContainer, Menu, Utils, Page403, Page404, Page500, Modal } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import dantdZhCN from 'knowdesign/es/locale/zh_CN'; import dantdZhCN from 'knowdesign/es/locale/zh_CN';
import dantdEnUS from 'knowdesign/es/locale/en_US'; import dantdEnUS from 'knowdesign/es/locale/en_US';
import { DotChartOutlined } from '@ant-design/icons'; import { DotChartOutlined } from '@ant-design/icons';

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import CardBar from './index'; import CardBar from './index';
import { IconFont, Tag, Utils, Tooltip, Popover, AppContainer } from 'knowdesign'; import { Tag, Utils, Tooltip, Popover, AppContainer } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import api from '@src/api'; import api from '@src/api';
import StateChart from './StateChart'; import StateChart from './StateChart';
import ClusterNorms from '@src/pages/LoadRebalance/ClusterNorms'; import ClusterNorms from '@src/pages/LoadRebalance/ClusterNorms';
@@ -138,15 +139,15 @@ const LoadRebalanceCardBar = (props: any) => {
// content={ // content={
// <div style={{ color: '#495057' }}> // <div style={{ color: '#495057' }}>
// <div> // <div>
// <IconFont className="cutomIcon" type="icon-chaoguo" /> // <IconFont className="cutomIcon cutomIcon-red" type="icon-chaoguo" />
// 超过均衡区间的有: {cpu?.bigNu || 0} // 超过均衡区间的有: {cpu?.bigNu || 0}
// </div> // </div>
// <div style={{ margin: '6px 0' }}> // <div style={{ margin: '6px 0' }}>
// <IconFont className="cutomIcon" type="icon-qujian" /> // <IconFont className="cutomIcon cutomIcon-green" type="icon-qujian" />
// 在均衡区间内的有: {cpu?.betweenNu || 0} // 在均衡区间内的有: {cpu?.betweenNu || 0}
// </div> // </div>
// <div> // <div>
// <IconFont className="cutomIcon" type="icon-diyu" /> // <IconFont className="cutomIcon cutomIcon-red" type="icon-diyu" />
// 低于均衡区间的有: {cpu?.smallNu || 0} // 低于均衡区间的有: {cpu?.smallNu || 0}
// </div> // </div>
// </div> // </div>
@@ -202,15 +203,15 @@ const LoadRebalanceCardBar = (props: any) => {
content={ content={
<div style={{ color: '#495057' }}> <div style={{ color: '#495057' }}>
<div> <div>
<IconFont className="cutomIcon" type="icon-chaoguo" /> <IconFont className="cutomIcon cutomIcon-red" type="icon-chaoguo" />
: {disk?.bigNu || 0} : {disk?.bigNu || 0}
</div> </div>
<div style={{ margin: '6px 0' }}> <div style={{ margin: '6px 0' }}>
<IconFont className="cutomIcon" type="icon-qujian" /> <IconFont className="cutomIcon cutomIcon-green" type="icon-qujian" />
: {disk?.betweenNu || 0} : {disk?.betweenNu || 0}
</div> </div>
<div> <div>
<IconFont className="cutomIcon" type="icon-diyu" /> <IconFont className="cutomIcon cutomIcon-red" type="icon-diyu" />
: {disk?.smallNu || 0} : {disk?.smallNu || 0}
</div> </div>
</div> </div>
@@ -267,15 +268,15 @@ const LoadRebalanceCardBar = (props: any) => {
content={ content={
<div style={{ color: '#495057' }}> <div style={{ color: '#495057' }}>
<div> <div>
<IconFont className="cutomIcon" type="icon-chaoguo" /> <IconFont className="cutomIcon cutomIcon-red" type="icon-chaoguo" />
: {bytesIn?.bigNu || 0} : {bytesIn?.bigNu || 0}
</div> </div>
<div style={{ margin: '6px 0' }}> <div style={{ margin: '6px 0' }}>
<IconFont className="cutomIcon" type="icon-qujian" /> <IconFont className="cutomIcon cutomIcon-green" type="icon-qujian" />
: {bytesIn?.betweenNu || 0} : {bytesIn?.betweenNu || 0}
</div> </div>
<div> <div>
<IconFont className="cutomIcon" type="icon-diyu" /> <IconFont className="cutomIcon cutomIcon-red" type="icon-diyu" />
: {bytesIn?.smallNu || 0} : {bytesIn?.smallNu || 0}
</div> </div>
</div> </div>
@@ -332,15 +333,15 @@ const LoadRebalanceCardBar = (props: any) => {
content={ content={
<div style={{ color: '#495057' }}> <div style={{ color: '#495057' }}>
<div> <div>
<IconFont className="cutomIcon" type="icon-chaoguo" /> <IconFont className="cutomIcon cutomIcon-red" type="icon-chaoguo" />
: {bytesOut?.bigNu || 0} : {bytesOut?.bigNu || 0}
</div> </div>
<div style={{ margin: '6px 0' }}> <div style={{ margin: '6px 0' }}>
<IconFont className="cutomIcon" type="icon-qujian" /> <IconFont className="cutomIcon cutomIcon-green" type="icon-qujian" />
: {bytesOut?.betweenNu || 0} : {bytesOut?.betweenNu || 0}
</div> </div>
<div> <div>
<IconFont className="cutomIcon" type="icon-diyu" /> <IconFont className="cutomIcon cutomIcon-red" type="icon-diyu" />
: {bytesOut?.smallNu || 0} : {bytesOut?.smallNu || 0}
</div> </div>
</div> </div>

View File

@@ -59,7 +59,7 @@ const EchartsExample = (props: any) => {
normal: { normal: {
color: (params: any) => { color: (params: any) => {
// 定义一个颜色数组colorList // 定义一个颜色数组colorList
const colorList = ['#00C0A2', '#CED4DA', '#FF7066']; const colorList = ['#FF7066', '#00C0A2', '#FF7066'];
return colorList[params.dataIndex]; return colorList[params.dataIndex];
}, },
}, },

View File

@@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import CardBar from '@src/components/CardBar'; import CardBar from '@src/components/CardBar';
import { healthDataProps } from '.'; import { healthDataProps } from '.';
import { IconFont, Utils } from 'knowdesign'; import { Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import api from '@src/api'; import api from '@src/api';
import { healthScoreCondition } from './const'; import { healthScoreCondition } from './const';
import { hashDataParse } from '@src/constants/common'; import { hashDataParse } from '@src/constants/common';

View File

@@ -203,11 +203,19 @@
background: rgba(33, 37, 41, 0.04); background: rgba(33, 37, 41, 0.04);
} }
.cutomIcon { .anticon.cutomIcon {
display: inline-block; display: inline-block;
margin-right: 2px; margin-right: 2px;
} }
.anticon.cutomIcon-red {
color: #ff7066;
}
.anticon.cutomIcon-green {
color: #00c0a2;
}
.rebalance-tooltip { .rebalance-tooltip {
.dcloud-tooltip-inner { .dcloud-tooltip-inner {
min-height: 20px; min-height: 20px;

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Drawer, IconFont, Select, Spin, Table } from 'knowdesign'; import { Drawer, Select, Spin, Table } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { Utils, Progress } from 'knowdesign'; import { Utils, Progress } from 'knowdesign';
import './index.less'; import './index.less';
import api from '@src/api'; import api from '@src/api';
@@ -110,8 +111,8 @@ const CardBar = (props: CardBarProps) => {
const promise = record const promise = record
? Utils.request(path) ? Utils.request(path)
: Utils.request(path, { : Utils.request(path, {
params: { dimensionCode: sceneObj.code }, params: { dimensionCode: sceneObj.code },
}); });
promise.then((data: any[]) => { promise.then((data: any[]) => {
setHealthCheckDetailList(data); setHealthCheckDetailList(data);
}); });

View File

@@ -0,0 +1,32 @@
.content-with-copy {
display: flex;
align-items: center;
.content {
flex: 1;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
}
.copy-icon {
width: 20px;
height: 20px;
padding-top: 2px;
border-radius: 50%;
margin-left: 4px;
font-size: 16px;
color: #adb5bc;
opacity: 0;
&:hover {
background: rgba(33, 37, 41, 0.04);
color: #74788d;
}
}
}
.dcloud-table-cell-row-hover {
.copy-icon {
opacity: 1;
}
}

View File

@@ -0,0 +1,35 @@
import { CheckCircleFilled } from '@ant-design/icons';
import { Tooltip } from 'knowdesign';
import React, { useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { IconFont } from '@knowdesign/icons';
import './index.less';
const ContentWithCopy = (props: { content: string }) => {
const { content } = props;
const [visible, setVisible] = useState(false);
return (
<CopyToClipboard text={content}>
<div className="content-with-copy">
<Tooltip title={content}>
<span className="content">{content}</span>
</Tooltip>
{content && (
<Tooltip
title={
<span>
<CheckCircleFilled style={{ color: '#00b365' }} />
</span>
}
visible={visible}
onVisibleChange={() => setVisible(false)}
>
<IconFont className="copy-icon" type="icon-fuzhi" onClick={() => setVisible(true)} />
</Tooltip>
)}
</div>
</CopyToClipboard>
);
};
export default ContentWithCopy;

View File

@@ -1,5 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Input, IconFont } from 'knowdesign'; import { Input } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import './style/index.less'; import './style/index.less';
interface IObjectProps { interface IObjectProps {

View File

@@ -2,9 +2,10 @@ import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, use
import { AppContainer, Drawer, Spin, Table, SingleChart, Utils, Tooltip } from 'knowdesign'; import { AppContainer, Drawer, Spin, Table, SingleChart, Utils, Tooltip } from 'knowdesign';
import moment from 'moment'; import moment from 'moment';
import api, { MetricType } from '@src/api'; import api, { MetricType } from '@src/api';
import { MetricDefaultChartDataType, MetricChartDataType, formatChartData } from '@src/constants/chartConfig';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { MetricDefaultChartDataType, MetricChartDataType, formatChartData, getDetailChartConfig } from './config'; import { getDetailChartConfig } from './config';
import { UNIT_MAP } from '@src/constants/chartConfig'; import { UNIT_MAP } from '@src/constants/chartConfig';
import RenderEmpty from '../RenderEmpty'; import RenderEmpty from '../RenderEmpty';
@@ -50,8 +51,6 @@ interface DataZoomEventProps {
const DATA_ZOOM_DEFAULT_SCALE = 0.25; const DATA_ZOOM_DEFAULT_SCALE = 0.25;
// 单次向服务器请求数据的范围(默认 6 小时,超过后采集频率间隔会变长),单位: ms // 单次向服务器请求数据的范围(默认 6 小时,超过后采集频率间隔会变长),单位: ms
const DEFAULT_REQUEST_TIME_RANGE = 6 * 60 * 60 * 1000; const DEFAULT_REQUEST_TIME_RANGE = 6 * 60 * 60 * 1000;
// 采样间隔,影响前端补点逻辑,单位: ms
const DEFAULT_POINT_INTERVAL = 60 * 1000;
// 向服务器每轮请求的数量 // 向服务器每轮请求的数量
const DEFAULT_REQUEST_COUNT = 6; const DEFAULT_REQUEST_COUNT = 6;
// 进入详情页默认展示的时间范围 // 进入详情页默认展示的时间范围
@@ -376,8 +375,6 @@ const ChartDetail = (props: ChartDetailProps) => {
global.getMetricDefine || {}, global.getMetricDefine || {},
metricType, metricType,
timeRange, timeRange,
DEFAULT_POINT_INTERVAL,
false,
chartInfo.current.transformUnit chartInfo.current.transformUnit
) as MetricChartDataType[]; ) as MetricChartDataType[];
// 增量填充图表数据 // 增量填充图表数据
@@ -540,14 +537,7 @@ const ChartDetail = (props: ChartDetailProps) => {
if (res?.length) { if (res?.length) {
// 格式化图表需要的数据 // 格式化图表需要的数据
const formattedMetricData = ( const formattedMetricData = (
formatChartData( formatChartData(res, global.getMetricDefine || {}, metricType, curTimeRange) as MetricChartDataType[]
res,
global.getMetricDefine || {},
metricType,
curTimeRange,
DEFAULT_POINT_INTERVAL,
false
) as MetricChartDataType[]
)[0]; )[0];
// 填充图表数据 // 填充图表数据
let initFullTimeRange = curTimeRange; let initFullTimeRange = curTimeRange;

View File

@@ -1,150 +1,4 @@
import { getUnit, getDataNumberUnit, getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig'; import { getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig';
import { MetricType } from '@src/api';
import { MetricsDefine } from '@src/pages/CommonConfig';
export interface MetricInfo {
name: string;
desc: string;
type: number;
set: boolean;
support: boolean;
}
// 接口返回图表原始数据类型
export interface MetricDefaultChartDataType {
metricName: string;
metricLines: {
name: string;
createTime: number;
updateTime: number;
metricPoints: {
aggType: string;
timeStamp: number;
value: number;
createTime: number;
updateTime: number;
}[];
}[];
}
// 格式化后图表数据类型
export interface MetricChartDataType {
metricName: string;
metricUnit: string;
metricLines: {
name: string;
data: (string | number)[][];
}[];
dragKey?: number;
}
// 补点
export const supplementaryPoints = (
lines: MetricChartDataType['metricLines'],
timeRange: readonly [number, number],
interval: number,
extraCallback?: (point: [number, 0]) => any[]
) => {
lines.forEach(({ data }) => {
// 获取未补点前线条的点的个数
let len = data.length;
// 记录当前处理到的点的下标值
let i = 0;
for (; i < len; i++) {
if (i === 0) {
let firstPointTimestamp = data[0][0] as number;
while (firstPointTimestamp - interval > timeRange[0]) {
const prevPointTimestamp = firstPointTimestamp - interval;
data.unshift(extraCallback ? extraCallback([prevPointTimestamp, 0]) : [prevPointTimestamp, 0]);
firstPointTimestamp = prevPointTimestamp;
len++;
i++;
}
}
if (i === len - 1) {
let lastPointTimestamp = data[i][0] as number;
while (lastPointTimestamp + interval < timeRange[1]) {
const nextPointTimestamp = lastPointTimestamp + interval;
data.push(extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
lastPointTimestamp = nextPointTimestamp;
}
break;
}
{
let timestamp = data[i][0] as number;
while (timestamp + interval < data[i + 1][0]) {
const nextPointTimestamp = timestamp + interval;
data.splice(i + 1, 0, extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
timestamp = nextPointTimestamp;
len++;
i++;
}
}
}
});
};
// 格式化图表数据
export const formatChartData = (
metricData: MetricDefaultChartDataType[],
getMetricDefine: (type: MetricType, metric: string) => MetricsDefine[keyof MetricsDefine],
metricType: MetricType,
timeRange: readonly [number, number],
supplementaryInterval: number,
needDrag = false,
transformUnit: [string, number] = undefined
): MetricChartDataType[] => {
return metricData.map(({ metricName, metricLines }) => {
const curMetricInfo = (getMetricDefine && getMetricDefine(metricType, metricName)) || null;
const isByteUnit = curMetricInfo?.unit?.toLowerCase().includes('byte');
let maxValue = -1;
const PointsMapMethod = ({ timeStamp, value }: { timeStamp: number; value: string | number }) => {
let parsedValue: string | number = Number(value);
if (Number.isNaN(parsedValue)) {
parsedValue = value;
} else {
// 为避免出现过小的数字影响图表展示效果,图表值统一只保留到小数点后三位
parsedValue = parseFloat(parsedValue.toFixed(3));
if (maxValue < parsedValue) maxValue = parsedValue;
}
return [timeStamp, parsedValue];
};
const chartData = Object.assign(
{
metricName,
metricUnit: curMetricInfo?.unit || '',
metricLines: metricLines
.sort((a, b) => Number(a.name < b.name) - 0.5)
.map(({ name, metricPoints }) => ({
name,
data: metricPoints.map(PointsMapMethod),
})),
},
needDrag ? { dragKey: 999 } : {}
);
chartData.metricLines.forEach(({ data }) => data.sort((a, b) => (a[0] as number) - (b[0] as number)));
supplementaryPoints(chartData.metricLines, timeRange, supplementaryInterval);
// 将所有图表点的值按单位进行转换
if (maxValue > 0) {
const [unitName, unitSize]: [string, number] = transformUnit || isByteUnit ? getUnit(maxValue) : getDataNumberUnit(maxValue);
chartData.metricUnit = isByteUnit
? chartData.metricUnit.toLowerCase().replace('byte', unitName)
: `${unitName}${chartData.metricUnit}`;
chartData.metricLines.forEach(({ data }) => data.forEach((point: any) => (point[1] /= unitSize)));
}
return chartData;
});
};
const seriesCallback = (lines: { name: string; data: [number, string | number][] }[]) => { const seriesCallback = (lines: { name: string; data: [number, string | number][] }[]) => {
const len = CHART_COLOR_LIST.length; const len = CHART_COLOR_LIST.length;

View File

@@ -1,14 +1,21 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { arrayMoveImmutable } from 'array-move'; import { arrayMoveImmutable } from 'array-move';
import { Utils, Empty, IconFont, Spin, AppContainer, SingleChart, Tooltip } from 'knowdesign'; import { Utils, Empty, Spin, AppContainer, SingleChart, Tooltip } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import api, { MetricType } from '@src/api'; import api, { MetricType } from '@src/api';
import {
MetricInfo,
MetricDefaultChartDataType,
MetricChartDataType,
formatChartData,
resolveMetricsRank,
} from '@src/constants/chartConfig';
import SingleChartHeader, { KsHeaderOptions } from '../SingleChartHeader'; import SingleChartHeader, { KsHeaderOptions } from '../SingleChartHeader';
import DragGroup from '../DragGroup'; import DragGroup from '../DragGroup';
import ChartDetail from './ChartDetail'; import ChartDetail from './ChartDetail';
import { MetricInfo, MetricDefaultChartDataType, MetricChartDataType, formatChartData, getChartConfig } from './config'; import { getChartConfig } from './config';
import './index.less'; import './index.less';
import { MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL } from '@src/constants/common';
interface IcustomScope { interface IcustomScope {
label: string; label: string;
@@ -39,8 +46,8 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>(); const [curHeaderOptions, setCurHeaderOptions] = useState<ChartFilterOptions>();
const [metricChartData, setMetricChartData] = useState<MetricChartDataType[]>([]); // 指标图表数据列表 const [metricChartData, setMetricChartData] = useState<MetricChartDataType[]>([]); // 指标图表数据列表
const [gridNum, setGridNum] = useState<number>(12); // 图表列布局 const [gridNum, setGridNum] = useState<number>(12); // 图表列布局
const metricRankList = useRef<string[]>([]);
const chartDetailRef = useRef(null); const chartDetailRef = useRef(null);
const chartDragOrder = useRef([]);
const curFetchingTimestamp = useRef(0); const curFetchingTimestamp = useRef(0);
// 获取节点范围列表 // 获取节点范围列表
@@ -60,23 +67,33 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
setScopeList(list); setScopeList(list);
}; };
// 更新 rank
const updateRank = (metricList: MetricInfo[]) => {
const { list, listInfo, shouldUpdate } = resolveMetricsRank(metricList);
metricRankList.current = list;
if (shouldUpdate) {
setMetricList(listInfo);
}
};
// 获取指标列表 // 获取指标列表
const getMetricList = () => { const getMetricList = () => {
Utils.request(api.getDashboardMetricList(clusterId, dashboardType)).then((res: MetricInfo[] | null) => { Utils.request(api.getDashboardMetricList(clusterId, dashboardType)).then((res: MetricInfo[] | null) => {
if (!res) return; if (!res) return;
const showMetrics = res.filter((metric) => metric.support); const supportMetrics = res.filter((metric) => metric.support);
const selectedMetrics = showMetrics.filter((metric) => metric.set).map((metric) => metric.name); const selectedMetrics = supportMetrics.filter((metric) => metric.set).map((metric) => metric.name);
setMetricsList(showMetrics); updateRank([...supportMetrics]);
setMetricsList(supportMetrics);
setSelectedMetricNames(selectedMetrics); setSelectedMetricNames(selectedMetrics);
}); });
}; };
// 更新指标 // 更新指标
const setMetricList = (metricsSet: { [name: string]: boolean }) => { const setMetricList = (metricDetailDTOList: { metric: string; rank: number; set: boolean }[]) => {
return Utils.request(api.getDashboardMetricList(clusterId, dashboardType), { return Utils.request(api.getDashboardMetricList(clusterId, dashboardType), {
method: 'POST', method: 'POST',
data: { data: {
metricsSet, metricDetailDTOList,
}, },
}); });
}; };
@@ -84,10 +101,11 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
// 根据筛选项获取图表信息 // 根据筛选项获取图表信息
const getMetricChartData = () => { const getMetricChartData = () => {
!curHeaderOptions.isAutoReload && setLoading(true); !curHeaderOptions.isAutoReload && setLoading(true);
const [startTime, endTime] = curHeaderOptions.rangeTime;
const [startTime, endTime] = curHeaderOptions.rangeTime;
const curTimestamp = Date.now(); const curTimestamp = Date.now();
curFetchingTimestamp.current = curTimestamp; curFetchingTimestamp.current = curTimestamp;
Utils.post(api.getDashboardMetricChartData(clusterId, dashboardType), { Utils.post(api.getDashboardMetricChartData(clusterId, dashboardType), {
startTime, startTime,
endTime, endTime,
@@ -108,36 +126,20 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
setMetricChartData([]); setMetricChartData([]);
} else { } else {
// 格式化图表需要的数据 // 格式化图表需要的数据
const supplementaryInterval = (endTime - startTime > MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL ? 10 : 1) * 60 * 1000;
const formattedMetricData = formatChartData( const formattedMetricData = formatChartData(
res, res,
global.getMetricDefine || {}, global.getMetricDefine || {},
dashboardType, dashboardType,
curHeaderOptions.rangeTime, curHeaderOptions.rangeTime
supplementaryInterval,
true
) as MetricChartDataType[]; ) as MetricChartDataType[];
// 处理图表的拖拽顺 // 指标排
if (chartDragOrder.current && chartDragOrder.current.length) { formattedMetricData.sort((a, b) => metricRankList.current.indexOf(a.metricName) - metricRankList.current.indexOf(b.metricName));
// 根据当前拖拽顺序排列图表数据
formattedMetricData.forEach((metric) => {
const i = chartDragOrder.current.indexOf(metric.metricName);
metric.dragKey = i === -1 ? 999 : i;
});
formattedMetricData.sort((a, b) => a.dragKey - b.dragKey);
}
// 更新当前拖拽顺序(处理新增或减少图表的情况)
chartDragOrder.current = formattedMetricData.map((data) => data.metricName);
setMetricChartData(formattedMetricData); setMetricChartData(formattedMetricData);
} }
setLoading(false); setLoading(false);
}, },
() => { () => curFetchingTimestamp.current === curTimestamp && setLoading(false)
if (curFetchingTimestamp.current === curTimestamp) {
setLoading(false);
}
}
); );
}; };
@@ -163,11 +165,19 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
// 指标选中项更新回调 // 指标选中项更新回调
const indicatorChangeCallback = (newMetricNames: (string | number)[]) => { const indicatorChangeCallback = (newMetricNames: (string | number)[]) => {
const updateMetrics: { [name: string]: boolean } = {}; const updateMetrics: { metric: string; set: boolean; rank: number }[] = [];
// 需要选中的指标 // 需要选中的指标
newMetricNames.forEach((name) => !selectedMetricNames.includes(name) && (updateMetrics[name] = true)); newMetricNames.forEach(
(name) =>
!selectedMetricNames.includes(name) &&
updateMetrics.push({ metric: name as string, set: true, rank: metricsList.find(({ name: metric }) => metric === name)?.rank })
);
// 取消选中的指标 // 取消选中的指标
selectedMetricNames.forEach((name) => !newMetricNames.includes(name) && (updateMetrics[name] = false)); selectedMetricNames.forEach(
(name) =>
!newMetricNames.includes(name) &&
updateMetrics.push({ metric: name as string, set: false, rank: metricsList.find(({ name: metric }) => metric === name)?.rank })
);
const requestPromise = Object.keys(updateMetrics).length ? setMetricList(updateMetrics) : Promise.resolve(); const requestPromise = Object.keys(updateMetrics).length ? setMetricList(updateMetrics) : Promise.resolve();
requestPromise.then( requestPromise.then(
@@ -186,7 +196,11 @@ const DashboardDragChart = (props: PropsType): JSX.Element => {
// 拖拽结束回调,更新图表顺序,并触发图表的 onDrag 事件( 设置为 false ),允许同步展示图表的 tooltip // 拖拽结束回调,更新图表顺序,并触发图表的 onDrag 事件( 设置为 false ),允许同步展示图表的 tooltip
const dragEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => { const dragEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
busInstance.emit('onDrag', false); busInstance.emit('onDrag', false);
chartDragOrder.current = arrayMoveImmutable(chartDragOrder.current, oldIndex, newIndex); const originFrom = metricRankList.current.indexOf(metricChartData[oldIndex].metricName);
const originTarget = metricRankList.current.indexOf(metricChartData[newIndex].metricName);
const newList = arrayMoveImmutable(metricRankList.current, originFrom, originTarget);
metricRankList.current = newList;
setMetricList(newList.map((metric, rank) => ({ metric, rank, set: metricsList.find(({ name }) => metric === name)?.set || false })));
setMetricChartData(arrayMoveImmutable(metricChartData, oldIndex, newIndex)); setMetricChartData(arrayMoveImmutable(metricChartData, oldIndex, newIndex));
}; };

View File

@@ -1,4 +1,5 @@
import { Col, IconFont, Row } from 'knowdesign'; import { Col, Row } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import React from 'react'; import React from 'react';
import { SortableContainer, SortableContainerProps, SortableHandle, SortableElement, SortableElementProps } from 'react-sortable-hoc'; import { SortableContainer, SortableContainerProps, SortableHandle, SortableElement, SortableElementProps } from 'react-sortable-hoc';
import './index.less'; import './index.less';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Drawer, Button, Space, Divider, AppContainer, ProTable, IconFont } from 'knowdesign'; import { Drawer, Button, Space, Divider, AppContainer, ProTable } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { IindicatorSelectModule } from './index'; import { IindicatorSelectModule } from './index';
import './style/indicator-drawer.less'; import './style/indicator-drawer.less';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Radio, Input, Popover, Space, Checkbox, Row, Col, Button, IconFont } from 'knowdesign'; import { Radio, Input, Popover, Space, Checkbox, Row, Col, Button } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { InodeScopeModule } from './index'; import { InodeScopeModule } from './index';
import './style/node-scope.less'; import './style/node-scope.less';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Tooltip, Select, IconFont, Utils, Divider, Button } from 'knowdesign'; import { Tooltip, Select, Utils, Divider, Button } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import moment from 'moment'; import moment from 'moment';
import { DRangeTime } from 'knowdesign'; import { DRangeTime } from 'knowdesign';
import IndicatorDrawer from './IndicatorDrawer'; import IndicatorDrawer from './IndicatorDrawer';

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined } from '@ant-design/icons';
import { Button, Divider, Drawer, IconFont, Select, Space, Table, Utils } from 'knowdesign'; import { Button, Divider, Drawer, Select, Space, Table, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import Api, { MetricType } from '@src/api/index'; import Api, { MetricType } from '@src/api/index';
const { Option } = Select; const { Option } = Select;

View File

@@ -18,9 +18,9 @@ import {
Space, Space,
Divider, Divider,
Transfer, Transfer,
IconFont,
Tooltip, Tooltip,
} from 'knowdesign'; } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import './index.less'; import './index.less';
import Api, { MetricType } from '@src/api/index'; import Api, { MetricType } from '@src/api/index';
import moment from 'moment'; import moment from 'moment';

View File

@@ -1,5 +1,44 @@
import moment from 'moment'; import moment from 'moment';
import { MetricType } from '@src/api';
import { MetricsDefine } from '@src/pages/CommonConfig';
export interface MetricInfo {
name: string;
desc: string;
type: number;
set: boolean;
rank: number | null;
support: boolean;
}
// 接口返回图表原始数据类型
export interface MetricDefaultChartDataType {
metricName: string;
metricLines: {
name: string;
createTime: number;
updateTime: number;
metricPoints: {
aggType: string;
timeStamp: number;
value: number;
createTime: number;
updateTime: number;
}[];
}[];
}
// 格式化后图表数据类型
export interface MetricChartDataType {
metricName: string;
metricUnit: string;
metricLines: {
name: string;
data: (string | number)[][];
}[];
}
// 图表颜色库
export const CHART_COLOR_LIST = [ export const CHART_COLOR_LIST = [
'#556ee6', '#556ee6',
'#94BEF2', '#94BEF2',
@@ -16,27 +55,176 @@ export const CHART_COLOR_LIST = [
'#C9E795', '#C9E795',
]; ];
// 图表存储单位换算
export const UNIT_MAP = { export const UNIT_MAP = {
TB: Math.pow(1024, 4), TB: Math.pow(1024, 4),
GB: Math.pow(1024, 3), GB: Math.pow(1024, 3),
MB: Math.pow(1024, 2), MB: Math.pow(1024, 2),
KB: 1024, KB: 1024,
}; };
export const getUnit = (value: number) => Object.entries(UNIT_MAP).find(([, size]) => value / size >= 1) || ['Byte', 1];
// 图表数字单位换算
export const DATA_NUMBER_MAP = { export const DATA_NUMBER_MAP = {
十亿: Math.pow(1000, 3), 十亿: Math.pow(1000, 3),
百万: Math.pow(1000, 2), 百万: Math.pow(1000, 2),
: 1000, : 1000,
}; };
export const getUnit = (value: number) => Object.entries(UNIT_MAP).find(([, size]) => value / size >= 1) || ['Byte', 1];
export const getDataNumberUnit = (value: number) => Object.entries(DATA_NUMBER_MAP).find(([, size]) => value / size >= 1) || ['', 1]; export const getDataNumberUnit = (value: number) => Object.entries(DATA_NUMBER_MAP).find(([, size]) => value / size >= 1) || ['', 1];
// 图表补点间隔计算
export const SUPPLEMENTARY_INTERVAL_MAP = {
'0': 60 * 1000,
// 6 小时10 分钟间隔
'21600000': 10 * 60 * 1000,
// 24 小时1 小时间隔
'86400000': 60 * 60 * 1000,
};
export const getSupplementaryInterval = (range: number) => {
return Object.entries(SUPPLEMENTARY_INTERVAL_MAP)
.reverse()
.find(([curRange]) => range > Number(curRange))[1];
};
// 处理图表排序
export const resolveMetricsRank = (metricList: MetricInfo[]) => {
const isRanked = metricList.some(({ rank }) => rank !== null);
let shouldUpdate = false;
let list: string[] = [];
if (isRanked) {
const rankedMetrics = metricList.filter(({ rank }) => rank !== null).sort((a, b) => a.rank - b.rank);
const unRankedMetrics = metricList.filter(({ rank }) => rank === null);
// 如果有新增/删除指标的情况,需要触发更新
if (unRankedMetrics.length || rankedMetrics.some(({ rank }, i) => rank !== i)) {
shouldUpdate = true;
}
list = [...rankedMetrics.map(({ name }) => name), ...unRankedMetrics.map(({ name }) => name).sort()];
} else {
shouldUpdate = true;
// 按字母先后顺序初始化指标排序
list = metricList.map(({ name }) => name).sort();
}
return {
list,
listInfo: list.map((metric, rank) => ({ metric, rank, set: metricList.find(({ name }) => metric === name)?.set || false })),
shouldUpdate,
};
};
// 补点
export const supplementaryPoints = (
lines: MetricChartDataType['metricLines'],
timeRange: readonly [number, number],
extraCallback?: (point: [number, 0]) => any[]
): void => {
const interval = getSupplementaryInterval(timeRange[1] - timeRange[0]);
lines.forEach(({ data }) => {
// 获取未补点前线条的点的个数
let len = data.length;
// 记录当前处理到的点的下标值
let i = 0;
for (; i < len; i++) {
if (i === 0) {
let firstPointTimestamp = data[0][0] as number;
while (firstPointTimestamp - interval > timeRange[0]) {
const prevPointTimestamp = firstPointTimestamp - interval;
data.unshift(extraCallback ? extraCallback([prevPointTimestamp, 0]) : [prevPointTimestamp, 0]);
firstPointTimestamp = prevPointTimestamp;
len++;
i++;
}
}
if (i === len - 1) {
let lastPointTimestamp = data[i][0] as number;
while (lastPointTimestamp + interval < timeRange[1]) {
const nextPointTimestamp = lastPointTimestamp + interval;
data.push(extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
lastPointTimestamp = nextPointTimestamp;
}
break;
}
{
let timestamp = data[i][0] as number;
while (timestamp + interval < data[i + 1][0]) {
const nextPointTimestamp = timestamp + interval;
data.splice(i + 1, 0, extraCallback ? extraCallback([nextPointTimestamp, 0]) : [nextPointTimestamp, 0]);
timestamp = nextPointTimestamp;
len++;
i++;
}
}
}
});
};
// 格式化图表数据
export const formatChartData = (
// 图表源数据
metricData: MetricDefaultChartDataType[],
// 获取指标单位
getMetricDefine: (type: MetricType, metric: string) => MetricsDefine[keyof MetricsDefine],
// 指标类型
metricType: MetricType,
// 图表时间范围,用于补点
timeRange: readonly [number, number],
transformUnit: [string, number] = undefined
): MetricChartDataType[] => {
return metricData.map(({ metricName, metricLines }) => {
const curMetricInfo = (getMetricDefine && getMetricDefine(metricType, metricName)) || null;
const isByteUnit = curMetricInfo?.unit?.toLowerCase().includes('byte');
let maxValue = -1;
const PointsMapMethod = ({ timeStamp, value }: { timeStamp: number; value: string | number }) => {
let parsedValue: string | number = Number(value);
if (Number.isNaN(parsedValue)) {
parsedValue = value;
} else {
// 为避免出现过小的数字影响图表展示效果,图表值统一保留小数点后三位
parsedValue = parseFloat(parsedValue.toFixed(3));
if (maxValue < parsedValue) maxValue = parsedValue;
}
return [timeStamp, parsedValue];
};
// 初始化返回结构
const chartData = {
metricName,
metricUnit: curMetricInfo?.unit || '',
metricLines: metricLines
.sort((a, b) => Number(a.name < b.name) - 0.5)
.map(({ name, metricPoints }) => ({
name,
data: metricPoints.map(PointsMapMethod),
})),
};
// 按时间先后进行对图表点排序
chartData.metricLines.forEach(({ data }) => data.sort((a, b) => (a[0] as number) - (b[0] as number)));
// 图表值单位转换
if (maxValue > 0) {
const [unitName, unitSize]: [string, number] = transformUnit || isByteUnit ? getUnit(maxValue) : getDataNumberUnit(maxValue);
chartData.metricUnit = isByteUnit
? chartData.metricUnit.toLowerCase().replace('byte', unitName)
: `${unitName}${chartData.metricUnit}`;
chartData.metricLines.forEach(({ data }) => data.forEach((point: any) => (point[1] /= unitSize)));
}
// 补点
supplementaryPoints(chartData.metricLines, timeRange);
return chartData;
});
};
// 图表 tooltip 基础展示样式 // 图表 tooltip 基础展示样式
const tooltipFormatter = (date: any, arr: any, tooltip: any) => { const tooltipFormatter = (date: any, arr: any, tooltip: any) => {
// 从大到小排序
// arr = arr.sort((a: any, b: any) => b.value - a.value);
const str = arr const str = arr
.map( .map(
(item: any) => `<div style="margin: 3px 0;"> (item: any) => `<div style="margin: 3px 0;">
@@ -121,9 +309,6 @@ export const getBasicChartConfig = (props: any = {}) => {
itemWidth: 8, itemWidth: 8,
itemGap: 8, itemGap: 8,
textStyle: { textStyle: {
// width: 85,
// overflow: 'truncate',
// ellipsis: '...',
fontSize: 11, fontSize: 11,
color: '#74788D', color: '#74788D',
}, },

View File

@@ -37,9 +37,6 @@ export const SMALL_DRAWER_WIDTH = 480;
export const MIDDLE_DRAWER_WIDTH = 728; export const MIDDLE_DRAWER_WIDTH = 728;
export const LARGE_DRAWER_WIDTH = 1080; export const LARGE_DRAWER_WIDTH = 1080;
// 小间隔1 分钟)图表点的最大请求时间范围,单位: ms
export const MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL = 6 * 60 * 60 * 1000;
export const primaryColor = '#556EE6'; export const primaryColor = '#556EE6';
export const numberToFixed = (value: number, num = 2) => { export const numberToFixed = (value: number, num = 2) => {
@@ -261,3 +258,6 @@ export const timeFormater = function formatDuring(mss: number) {
.map((o: any) => `${o.v}${o.unit}`) .map((o: any) => `${o.v}${o.unit}`)
.join(); .join();
}; };
// 列表页Header布局前缀
export const tableHeaderPrefix = 'table-header-layout';

View File

@@ -280,3 +280,38 @@ li {
line-height: 20px; line-height: 20px;
} }
} }
// Table Header 布局样式
.table-header-layout {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
&-left,
&-right {
display: flex;
align-items: center;
}
&-left {
&-refresh{
font-size: 20px;
color: #74788d;
cursor: pointer;
}
}
&-right{
&>*{
margin-left: 8px;
}
.search-input {
width: 248px;
}
}
&-divider{
height: 20px;
top: 0
}
}

View File

@@ -21,7 +21,6 @@ export default {
[`menu.${systemKey}.cluster`]: 'Cluster', [`menu.${systemKey}.cluster`]: 'Cluster',
[`menu.${systemKey}.cluster.overview`]: 'Overview', [`menu.${systemKey}.cluster.overview`]: 'Overview',
[`menu.${systemKey}.cluster.balance`]: 'Load Rebalance',
[`menu.${systemKey}.broker`]: 'Broker', [`menu.${systemKey}.broker`]: 'Broker',
[`menu.${systemKey}.broker.dashbord`]: 'Overview', [`menu.${systemKey}.broker.dashbord`]: 'Overview',
@@ -45,7 +44,7 @@ export default {
[`menu.${systemKey}.consumer-group.group-list`]: 'GroupList', [`menu.${systemKey}.consumer-group.group-list`]: 'GroupList',
[`menu.${systemKey}.operation`]: 'Operation', [`menu.${systemKey}.operation`]: 'Operation',
[`menu.${systemKey}.operation.balance`]: 'Load Rebalance', [`menu.${systemKey}.operation.balance`]: 'Rebalance',
[`menu.${systemKey}.operation.jobs`]: 'Job', [`menu.${systemKey}.operation.jobs`]: 'Job',
[`menu.${systemKey}.acls`]: 'ACLs', [`menu.${systemKey}.acls`]: 'ACLs',

View File

@@ -1,12 +1,14 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom'; import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Utils, AppContainer } from 'knowdesign'; import { ProTable, Utils, AppContainer, SearchInput } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import API from '../../api'; import API from '../../api';
import { getControllerChangeLogListColumns, defaultPagination } from './config'; import { getControllerChangeLogListColumns, defaultPagination } from './config';
import BrokerDetail from '../BrokerDetail'; import BrokerDetail from '../BrokerDetail';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck'; import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
import './index.less'; import './index.less';
import { tableHeaderPrefix } from '@src/constants/common';
const { request } = Utils; const { request } = Utils;
const ControllerChangeLogList: React.FC = (props: any) => { const ControllerChangeLogList: React.FC = (props: any) => {
@@ -89,26 +91,35 @@ const ControllerChangeLogList: React.FC = (props: any) => {
<BrokerHealthCheck /> <BrokerHealthCheck />
</div> </div>
<div className="clustom-table-content"> <div className="clustom-table-content">
<div className={tableHeaderPrefix}>
<div className={`${tableHeaderPrefix}-left`}>
<div
className={`${tableHeaderPrefix}-left-refresh`}
onClick={() => genData({ pageNo: pagination.current, pageSize: pagination.pageSize })}
>
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</div>
</div>
<div className={`${tableHeaderPrefix}-right`}>
<SearchInput
onSearch={setSearchKeywords}
attrs={{
placeholder: '请输入Broker Host',
style: { width: '248px', borderRiadus: '8px' },
maxLength: 128,
}}
/>
</div>
</div>
<ProTable <ProTable
showQueryForm={false} showQueryForm={false}
tableProps={{ tableProps={{
showHeader: true, showHeader: false,
rowKey: 'path', rowKey: 'path',
loading: loading, loading: loading,
columns: getControllerChangeLogListColumns(), columns: getControllerChangeLogListColumns(),
dataSource: data, dataSource: data,
paginationProps: { ...pagination }, paginationProps: { ...pagination },
tableHeaderSearchInput: {
// 搜索配置
submit: getSearchKeywords,
searchInputType: 'search',
searchAttr: {
placeholder: '请输入Broker Host',
style: {
width: '248px',
},
},
},
attrs: { attrs: {
onChange: onTableChange, onChange: onTableChange,
bordered: false, bordered: false,

View File

@@ -1,5 +1,6 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { Drawer, Form, Input, Space, Button, Checkbox, Utils, Row, Col, IconFont, Divider, message } from 'knowdesign'; import { Drawer, Form, Input, Space, Button, Checkbox, Utils, Row, Col, Divider, message } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import Api from '@src/api'; import Api from '@src/api';
export const ConfigurationEdit = (props: any) => { export const ConfigurationEdit = (props: any) => {

View File

@@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Utils, IconFont, Tooltip } from 'knowdesign'; import { Utils, Tooltip } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
export const getConfigurationColmns = (arg: any) => { export const getConfigurationColmns = (arg: any) => {
const columns: any = [ const columns: any = [
{ {

View File

@@ -1,9 +1,10 @@
import React, { useState, useEffect, memo } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom'; import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Drawer, Utils, AppContainer } from 'knowdesign'; import { ProTable, Drawer, Utils, AppContainer, SearchInput } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import API from '../../api'; import API from '../../api';
import { getBrokerListColumns, defaultPagination } from './config'; import { getBrokerListColumns, defaultPagination } from './config';
import { dealTableRequestParams } from '../../constants/common'; import { tableHeaderPrefix } from '@src/constants/common';
import BrokerDetail from '../BrokerDetail'; import BrokerDetail from '../BrokerDetail';
import CardBar from '@src/components/CardBar'; import CardBar from '@src/components/CardBar';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck'; import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
@@ -33,7 +34,6 @@ const BrokerList: React.FC = (props: any) => {
if (urlParams?.clusterId === undefined) return; if (urlParams?.clusterId === undefined) return;
// filters = filters || filteredInfo; // filters = filters || filteredInfo;
setLoading(true); setLoading(true);
// const params = dealTableRequestParams({ searchKeywords, pageNo, pageSize });
const params = { const params = {
searchKeywords: searchKeywords.slice(0, 128), searchKeywords: searchKeywords.slice(0, 128),
pageNo, pageNo,
@@ -99,29 +99,36 @@ const BrokerList: React.FC = (props: any) => {
<BrokerHealthCheck /> <BrokerHealthCheck />
</div> </div>
<div className="clustom-table-content"> <div className="clustom-table-content">
<div className={tableHeaderPrefix}>
<div className={`${tableHeaderPrefix}-left`}>
<div
className={`${tableHeaderPrefix}-left-refresh`}
onClick={() => genData({ pageNo: pagination.current, pageSize: pagination.pageSize })}
>
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</div>
</div>
<div className={`${tableHeaderPrefix}-right`}>
<SearchInput
onSearch={setSearchKeywords}
attrs={{
placeholder: '请输入Broker Host',
style: { width: '248px', borderRiadus: '8px' },
maxLength: 128,
}}
/>
</div>
</div>
<ProTable <ProTable
key="brokerTable" key="brokerTable"
showQueryForm={false} showQueryForm={false}
tableProps={{ tableProps={{
showHeader: true, showHeader: false,
rowKey: 'broker_list', rowKey: 'broker_list',
loading: loading, loading: loading,
columns: getBrokerListColumns(), columns: getBrokerListColumns(),
dataSource: data, dataSource: data,
paginationProps: { ...pagination }, paginationProps: { ...pagination },
tableHeaderSearchInput: {
// 搜索配置
submit: getSearchKeywords,
searchInputType: 'search',
searchAttr: {
placeholder: '请输入Broker Host',
maxLength: 128,
style: {
width: '248px',
borderRiadus: '8px',
},
},
},
attrs: { attrs: {
onChange: onTableChange, onChange: onTableChange,
scroll: { x: 'max-content', y: 'calc(100vh - 400px)' }, scroll: { x: 'max-content', y: 'calc(100vh - 400px)' },

View File

@@ -1,15 +1,15 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom'; import { useParams, useHistory } from 'react-router-dom';
import CopyToClipboard from 'react-copy-to-clipboard'; import { AppContainer, Divider, Drawer, ProTable, Select, SingleChart, Space, Tooltip, Utils } from 'knowdesign';
import { AppContainer, Divider, Drawer, IconFont, ProTable, Select, SingleChart, Space, Tooltip, Utils } from 'knowdesign'; import { IconFont } from '@knowdesign/icons';
import { DRangeTime } from 'knowdesign'; import { DRangeTime } from 'knowdesign';
import { CHART_COLOR_LIST, getBasicChartConfig } from '@src/constants/chartConfig'; import { CHART_COLOR_LIST, getBasicChartConfig } from '@src/constants/chartConfig';
import Api from '@src/api/index'; import Api from '@src/api/index';
import { hashDataParse } from '@src/constants/common'; import { hashDataParse } from '@src/constants/common';
import { ClustersPermissionMap } from '../CommonConfig'; import { ClustersPermissionMap } from '../CommonConfig';
import ResetOffsetDrawer from './ResetOffsetDrawer'; import ResetOffsetDrawer from './ResetOffsetDrawer';
import { CheckCircleFilled } from '@ant-design/icons';
import SwitchTab from '@src/components/SwitchTab'; import SwitchTab from '@src/components/SwitchTab';
import ContentWithCopy from '@src/components/CopyContent';
const { Option } = Select; const { Option } = Select;
@@ -44,33 +44,6 @@ const metricWithType = [
{ metricName: 'Lag', metricType: 102 }, { metricName: 'Lag', metricType: 102 },
]; ];
const ContentWithCopy = (props: { content: string }) => {
const { content } = props;
const [visible, setVisible] = useState(false);
return (
<CopyToClipboard text={content}>
<div className="content-with-copy">
<Tooltip title={content}>
<span className="content">{content}</span>
</Tooltip>
{content && (
<Tooltip
title={
<span>
<CheckCircleFilled style={{ color: '#00b365' }} />
</span>
}
visible={visible}
onVisibleChange={() => setVisible(false)}
>
<IconFont className="copy-icon" type="icon-fuzhi" onClick={() => setVisible(true)} />
</Tooltip>
)}
</div>
</CopyToClipboard>
);
};
export default (props: any) => { export default (props: any) => {
const { scene } = props; const { scene } = props;
const params = useParams<{ const params = useParams<{
@@ -104,6 +77,9 @@ export default (props: any) => {
title: 'Topic Partition', title: 'Topic Partition',
dataIndex: 'partitionId', dataIndex: 'partitionId',
key: 'partitionId', key: 'partitionId',
lineClampOne: true,
needTooltip: true,
width: 180,
render: (v: string, record: any) => { render: (v: string, record: any) => {
return `${record.topicName}-${v}`; return `${record.topicName}-${v}`;
}, },

View File

@@ -1,12 +1,8 @@
.operating-state { .operating-state {
.operation-bar { .consumers-search{
.left { display: contents;
.dcloud-form-item { .search-input-short{
margin-bottom: 0; margin-right: 8px;
}
.dcloud-form-item:first-of-type {
margin-right: 12px;
}
} }
} }
.pro-table-wrap { .pro-table-wrap {
@@ -53,6 +49,10 @@
align-items: center; align-items: center;
.d-range-time-input { .d-range-time-input {
height: 27px !important; height: 27px !important;
padding: 0 11px;
input{
line-height: 100%;
}
} }
.divider { .divider {
width: 1px; width: 1px;

View File

@@ -1,6 +1,7 @@
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { AppContainer, Form, Input, ProTable, Select, Utils } from 'knowdesign'; import { AppContainer, Divider, Form, Input, ProTable, Select, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import './index.less'; import './index.less';
import Api from '@src/api/index'; import Api from '@src/api/index';
import { getOperatingStateListParams } from './interface'; import { getOperatingStateListParams } from './interface';
@@ -8,7 +9,7 @@ import { useParams } from 'react-router-dom';
import ConsumerGroupDetail from './ConsumerGroupDetail'; import ConsumerGroupDetail from './ConsumerGroupDetail';
import ConsumerGroupHealthCheck from '@src/components/CardBar/ConsumerGroupHealthCheck'; import ConsumerGroupHealthCheck from '@src/components/CardBar/ConsumerGroupHealthCheck';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
import { hashDataParse } from '@src/constants/common'; import { hashDataParse, tableHeaderPrefix } from '@src/constants/common';
const { Option } = Select; const { Option } = Select;
@@ -181,17 +182,13 @@ const AutoPage = (props: any) => {
<div className={`operating-state ${scene !== 'topicDetail' && 'clustom-table-content'}`}> <div className={`operating-state ${scene !== 'topicDetail' && 'clustom-table-content'}`}>
{/* <CardBar cardColumns={data}></CardBar> */} {/* <CardBar cardColumns={data}></CardBar> */}
{scene !== 'topicDetail' && ( {scene !== 'topicDetail' && (
<div className="operation-bar"> <div className={tableHeaderPrefix}>
<div className="left"> <div className={`${tableHeaderPrefix}-left`}>
{/* <Radio.Group <div className={`${tableHeaderPrefix}-left-refresh`} onClick={() => searchFn()}>
options={showModes} <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
optionType="button" </div>
onChange={(e) => { <Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
setShowMode(e.target.value); <div className="consumers-search">
}}
value={showMode}
/> */}
<Form.Item label="">
<Input <Input
className="search-input-short" className="search-input-short"
placeholder="请输入Consumer Group" placeholder="请输入Consumer Group"
@@ -201,8 +198,6 @@ const AutoPage = (props: any) => {
}} }}
onPressEnter={searchFn} onPressEnter={searchFn}
/> />
</Form.Item>
<Form.Item label="">
<Input <Input
className="search-input-short" className="search-input-short"
placeholder="请输入Topic name" placeholder="请输入Topic name"
@@ -212,12 +207,12 @@ const AutoPage = (props: any) => {
}} }}
onPressEnter={searchFn} onPressEnter={searchFn}
/> />
</Form.Item> </div>
{/* <Button type="primary" className="add-btn" onClick={searchFn}> {/* <Button type="primary" className="add-btn" onClick={searchFn}>
查询 查询
</Button> */} </Button> */}
</div> </div>
<div className="right"></div> {/* <div className="right"></div> */}
</div> </div>
)} )}
{/* <Table columns={columns} dataSource={consumerGroupList} scroll={{ x: 1500 }} /> {/* <Table columns={columns} dataSource={consumerGroupList} scroll={{ x: 1500 }} />

View File

@@ -13,8 +13,8 @@ interface PropsType {
} }
const typeObj: any = { const typeObj: any = {
1: '周期均衡', 1: '立即均衡',
2: '立即均衡', 2: '周期均衡',
}; };
const { request, post } = Utils; const { request, post } = Utils;

View File

@@ -1,6 +1,7 @@
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Alert, Badge, Dropdown, IconFont, ProTable, Space, Table, Utils } from 'knowdesign'; import { Alert, Badge, Dropdown, ProTable, Space, Table, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import Api from '@src/api'; import Api from '@src/api';
import { getTaskDetailsColumns, getMoveBalanceColumns } from './config'; import { getTaskDetailsColumns, getMoveBalanceColumns } from './config';

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import moment from 'moment'; import moment from 'moment';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Button, Drawer, Utils, Descriptions, Tabs, Input, IconFont, message, Spin, InputNumber } from 'knowdesign'; import { Button, Drawer, Utils, Descriptions, Tabs, Input, message, Spin, InputNumber } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import TaskDetails from './TeskDetails'; import TaskDetails from './TeskDetails';
import NodeTraffic from './NodeTraffic'; import NodeTraffic from './NodeTraffic';
import RebalancePlan from './RebalancePlan'; import RebalancePlan from './RebalancePlan';

View File

@@ -89,12 +89,9 @@ export const getJobsListColumns = (arg?: any) => {
title: '任务执行对象', title: '任务执行对象',
dataIndex: 'target', dataIndex: 'target',
key: 'target', key: 'target',
width: 232,
render(t: any, r: any) { render(t: any, r: any) {
return ( return <TagsWithHide placement="bottom" list={t.split(',')} expandTagContent={(num: any) => `共有${num}`} />;
<div style={{ width: '232px' }}>
<TagsWithHide placement="bottom" list={t.split(',')} expandTagContent={(num: any) => `共有${num}`} />
</div>
);
}, },
}, },
{ {

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, memo } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom'; import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Drawer, Utils, AppContainer, Form, Select, Input, Button, message, Modal } from 'knowdesign'; import { ProTable, Drawer, Utils, AppContainer, Form, Select, Input, Button, message, Modal, Divider } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import API from '../../api'; import API from '../../api';
import { getJobsListColumns, defaultPagination, runningStatus, jobType } from './config'; import { getJobsListColumns, defaultPagination, runningStatus, jobType } from './config';
import JobsCheck from '@src/components/CardBar/JobsCheck'; import JobsCheck from '@src/components/CardBar/JobsCheck';
@@ -10,6 +11,7 @@ import './index.less';
import ReplicaChange from '@src/components/TopicJob/ReplicaChange'; import ReplicaChange from '@src/components/TopicJob/ReplicaChange';
import ReplicaMove from '@src/components/TopicJob/ReplicaMove'; import ReplicaMove from '@src/components/TopicJob/ReplicaMove';
import BalanceDrawer from '../LoadRebalance/BalanceDrawer'; import BalanceDrawer from '../LoadRebalance/BalanceDrawer';
import { tableHeaderPrefix } from '@src/constants/common';
const { request } = Utils; const { request } = Utils;
const JobsList: React.FC = (props: any) => { const JobsList: React.FC = (props: any) => {
@@ -171,35 +173,44 @@ const JobsList: React.FC = (props: any) => {
</div> </div>
{/* <Form form={form} layout="inline" onFinish={onFinish}> */} {/* <Form form={form} layout="inline" onFinish={onFinish}> */}
<div className="clustom-table-content"> <div className="clustom-table-content">
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '12px' }}> <div className={tableHeaderPrefix}>
<Form form={form} layout="inline" onFinish={onFinish}> <div className={`${tableHeaderPrefix}-left`}>
<Form.Item name="type"> <div
<Select options={jobType} style={{ width: '190px' }} className={'detail-table-select'} placeholder="选择任务类型" /> className={`${tableHeaderPrefix}-left-refresh`}
</Form.Item> onClick={() => genData({ pageNo: pagination.current, pageSize: pagination.pageSize })}
<Form.Item name="jobTarget"> >
<Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" /> <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="status"> <Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<Select <Form form={form} layout="inline" onFinish={onFinish}>
mode="multiple" <Form.Item name="type">
maxTagCount={'responsive'} <Select options={jobType} style={{ width: '190px' }} className={'detail-table-select'} placeholder="选择任务类型" />
options={runningStatus} </Form.Item>
style={{ width: '190px' }} <Form.Item name="jobTarget">
className={'detail-table-select'} <Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" />
placeholder="选择运行状态" </Form.Item>
showArrow <Form.Item name="status">
allowClear <Select
/> mode="multiple"
</Form.Item> maxTagCount={'responsive'}
</Form> options={runningStatus}
<div> style={{ width: '190px' }}
<Form style={{ justifyContent: 'flex-end' }} form={form} layout="inline" onFinish={onFinish}> className={'detail-table-select'}
<Form.Item style={{ marginRight: 0 }}> placeholder="选择运行状态"
<Button type="primary" ghost htmlType="submit"> showArrow
allowClear
</Button> />
</Form.Item> </Form.Item>
</Form> </Form>
<div>
<Form style={{ justifyContent: 'flex-end' }} form={form} layout="inline" onFinish={onFinish}>
<Form.Item style={{ marginRight: 0 }}>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
</div> </div>
</div> </div>
{/* </Form> */} {/* </Form> */}

View File

@@ -12,10 +12,9 @@ import {
Transfer, Transfer,
Select, Select,
message, message,
IconFont,
Tooltip, Tooltip,
} from 'knowdesign'; } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import CronInput from './CronInput'; import CronInput from './CronInput';
import BalanceEditTable from './BalanceEditTable'; import BalanceEditTable from './BalanceEditTable';
import PlanDrawer from './PlanDrawer'; import PlanDrawer from './PlanDrawer';

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, Popover, IconFont, Row, Col, Select } from 'knowdesign'; import { Button, Popover, Row, Col, Select } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { CloseOutlined } from '@ant-design/icons'; import { CloseOutlined } from '@ant-design/icons';
const balancePrefix = 'custom-popover-balance'; const balancePrefix = 'custom-popover-balance';

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Utils, Drawer, Button, Form, Space, Divider, AppContainer, Input, Transfer, message, IconFont, InputNumber } from 'knowdesign'; import { Utils, Drawer, Button, Form, Space, Divider, AppContainer, Input, Transfer, message, InputNumber } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { CloseOutlined } from '@ant-design/icons'; import { CloseOutlined } from '@ant-design/icons';
import api from '../../api'; import api from '../../api';
import './style/BalanceDrawer.less'; import './style/BalanceDrawer.less';

View File

@@ -1,5 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { message, Drawer, Button, Space, Divider, AppContainer, IconFont } from 'knowdesign'; import { message, Drawer, Button, Space, Divider, AppContainer } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import RebalancePlan from '../Jobs/RebalancePlan'; import RebalancePlan from '../Jobs/RebalancePlan';
interface PropsType extends React.HTMLAttributes<HTMLDivElement> { interface PropsType extends React.HTMLAttributes<HTMLDivElement> {

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Select, Form, Utils, AppContainer, Input, Button, ProTable, Badge, Tag, SearchInput } from 'knowdesign'; import { Select, Form, Utils, AppContainer, Input, Button, ProTable, Badge, Tag, SearchInput, Divider } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import BalanceDrawer from './BalanceDrawer'; import BalanceDrawer from './BalanceDrawer';
import HistoryDrawer from './HistoryDrawer'; import HistoryDrawer from './HistoryDrawer';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
@@ -9,6 +10,7 @@ import './index.less';
import LoadRebalanceCardBar from '@src/components/CardBar/LoadRebalanceCardBar'; import LoadRebalanceCardBar from '@src/components/CardBar/LoadRebalanceCardBar';
import { BalanceFilter } from './BalanceFilter'; import { BalanceFilter } from './BalanceFilter';
import { ClustersPermissionMap } from '../CommonConfig'; import { ClustersPermissionMap } from '../CommonConfig';
import { tableHeaderPrefix } from '@src/constants/common';
const Balance_Status_OPTIONS = [ const Balance_Status_OPTIONS = [
{ {
@@ -132,7 +134,7 @@ const LoadBalance: React.FC = (props: any) => {
key: 'disk_spec', key: 'disk_spec',
width: '150px', width: '150px',
render: (text: any, row: any) => { render: (text: any, row: any) => {
return text !== null ? `${text}GB` : '-'; return text !== null ? `${text.toLocaleString()}GB` : '-';
}, },
}, },
{ {
@@ -144,7 +146,10 @@ const LoadBalance: React.FC = (props: any) => {
return text !== null ? ( return text !== null ? (
<span> <span>
<Badge status={row?.disk_status === 0 ? 'success' : 'error'} /> <Badge status={row?.disk_status === 0 ? 'success' : 'error'} />
{`${getSizeAndUnit(text, 'B').valueWithUnit} (${((row.disk_avg * 100) / Utils.transGBToB(row.disk_spec)).toFixed(2)}%)`} {`${getSizeAndUnit(text, 'B').valueWithUnit.toLocaleString()} (${(
(row.disk_avg * 100) /
Utils.transGBToB(row.disk_spec)
).toFixed(2)}%)`}
</span> </span>
) : ( ) : (
'-' '-'
@@ -157,7 +162,7 @@ const LoadBalance: React.FC = (props: any) => {
key: 'bytesIn_spec', key: 'bytesIn_spec',
width: '150px', width: '150px',
render: (text: any, row: any) => { render: (text: any, row: any) => {
return text !== null ? `${text}MB/s` : '-'; return text !== null ? `${text.toLocaleString()}MB/s` : '-';
}, },
}, },
{ {
@@ -169,7 +174,10 @@ const LoadBalance: React.FC = (props: any) => {
return text !== null ? ( return text !== null ? (
<span> <span>
<Badge status={row?.bytesIn_status === 0 ? 'success' : 'error'} /> <Badge status={row?.bytesIn_status === 0 ? 'success' : 'error'} />
{`${getSizeAndUnit(text, 'B/s').valueWithUnit} (${((row.bytesIn_avg * 100) / (row.bytesIn_spec * 1024 * 1024)).toFixed(2)}%)`} {`${getSizeAndUnit(text, 'B/s').valueWithUnit.toLocaleString()} (${(
(row.bytesIn_avg * 100) /
(row.bytesIn_spec * 1024 * 1024)
).toFixed(2)}%)`}
</span> </span>
) : ( ) : (
'-' '-'
@@ -182,7 +190,7 @@ const LoadBalance: React.FC = (props: any) => {
key: 'bytesOut_spec', key: 'bytesOut_spec',
width: '150px', width: '150px',
render: (text: any, row: any) => { render: (text: any, row: any) => {
return text !== null ? `${text}MB/s` : '-'; return text !== null ? `${text.toLocaleString()}MB/s` : '-';
}, },
}, },
{ {
@@ -195,7 +203,10 @@ const LoadBalance: React.FC = (props: any) => {
return text !== null ? ( return text !== null ? (
<span> <span>
<Badge status={row?.bytesOut_status === 0 ? 'success' : 'error'} /> <Badge status={row?.bytesOut_status === 0 ? 'success' : 'error'} />
{`${getSizeAndUnit(text, 'B/s').valueWithUnit} (${((row.bytesOut_avg * 100) / (row.bytesOut_spec * 1024 * 1024)).toFixed(2)}%)`} {`${getSizeAndUnit(text, 'B/s').valueWithUnit.toLocaleString()} (${(
(row.bytesOut_avg * 100) /
(row.bytesOut_spec * 1024 * 1024)
).toFixed(2)}%)`}
</span> </span>
) : ( ) : (
'-' '-'
@@ -330,7 +341,7 @@ const LoadBalance: React.FC = (props: any) => {
breadcrumbs={[ breadcrumbs={[
{ label: '多集群管理', aHref: '/' }, { label: '多集群管理', aHref: '/' },
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` }, { label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
{ label: 'Load Rebalance', aHref: `` }, { label: 'Rebalance', aHref: `` },
]} ]}
/> />
</div> </div>
@@ -339,7 +350,17 @@ const LoadBalance: React.FC = (props: any) => {
</div> </div>
<div className="load-rebalance-container"> <div className="load-rebalance-container">
<div className="balance-main clustom-table-content"> <div className="balance-main clustom-table-content">
<div className="header-con"> <div className={tableHeaderPrefix}>
<div className={`${tableHeaderPrefix}-left`}>
<div
className={`${tableHeaderPrefix}-left-refresh`}
onClick={() => getList({ searchKeywords: searchValue, stateParam: balanceList })}
>
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</div>
<Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<BalanceFilter title="负载均衡列表筛选" data={[]} getNorms={getNorms} filterList={filterList} />
</div>
{/* <Form form={form} layout="inline" onFinish={resetList}> {/* <Form form={form} layout="inline" onFinish={resetList}>
<Form.Item name="status"> <Form.Item name="status">
<Select className="grid-select" placeholder="请选择状态" style={{ width: '180px' }} options={Balance_Status_OPTIONS} /> <Select className="grid-select" placeholder="请选择状态" style={{ width: '180px' }} options={Balance_Status_OPTIONS} />
@@ -354,8 +375,7 @@ const LoadBalance: React.FC = (props: any) => {
</Button> </Button>
</Form.Item> </Form.Item>
</Form> */} </Form> */}
<BalanceFilter title="负载均衡列表筛选" data={[]} getNorms={getNorms} filterList={filterList} /> <div className={`${tableHeaderPrefix}-right`}>
<div className="float-r">
<SearchInput <SearchInput
onSearch={hostSearch} onSearch={hostSearch}
attrs={{ attrs={{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 374 KiB

Some files were not shown because too many files have changed in this diff Show More