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.
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

View File

@@ -51,16 +51,16 @@
- 无需侵入改造 `Apache Kafka` ,一键便能纳管 `0.10.x` ~ `3.x.x` 众多版本的Kafka包括 `ZK``Raft` 运行模式的版本,同时在兼容架构上具备良好的扩展性,帮助您提升集群管理水平;
- 🌪️ &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;**专业能力**
- 集群管理:支持集群一键纳管,健康分析、核心组件观测 等功能;
- 集群管理:支持一键纳管,健康分析、核心组件观测 等功能;
- 观测提升:多维度指标观测大盘、观测指标最佳实践 等功能;
- 异常巡检:集群多维度健康巡检、集群多维度健康分 等功能;
- 能力增强Topic扩缩副本、Topic副本迁移 等功能;
- 能力增强:集群负载均衡、Topic扩缩副本、Topic副本迁移 等功能;
&nbsp;
@@ -133,6 +133,8 @@ PS: 提问请尽量把问题一次性描述清楚,并告知环境信息情况
**`2、微信群`**
微信加群:添加`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

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
**文档**

View File

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

View File

@@ -59,6 +59,8 @@ sh deploy_KnowStreaming-offline.sh
### 2.1.3、容器部署
#### 2.1.3.1、Helm
**环境依赖**
- Kubernetes >= 1.14 Helm >= 2.17.0
@@ -72,11 +74,11 @@ sh deploy_KnowStreaming-offline.sh
```bash
# 相关镜像在Docker Hub都可以下载
# 快速安装(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方式.
# (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
@@ -87,6 +89,156 @@ helm pull knowstreaming/knowstreaming-manager
&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、手动部署
**部署流程**

View File

@@ -1,12 +1,28 @@
## 6.2、版本升级手册
注意:如果想升级至具体版本,需要将你当前版本至你期望使用版本的变更统统执行一遍,然后才能正常使用。
注意:
- 如果想升级至具体版本,需要将你当前版本至你期望使用版本的变更统统执行一遍,然后才能正常使用。
- 如果中间某个版本没有升级信息,则表示该版本直接替换安装包即可从前一个版本升级至当前版本。
### 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
-- 多集群管理权限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`表增加了一个监听信息字段。
2、为`logi_security_oplog`表operation_methods字段设置默认值''。
因此需要执行下面的sql对数据库表进行更新。
2、为`logi_security_oplog` operation_methods 字段设置默认值''。
因此需要执行下面的 sql 对数据库表进行更新。
```sql
ALTER TABLE `ks_km_broker`
@@ -98,8 +112,7 @@ ALTER COLUMN `operation_methods` set default '';
---
### 6.2.3、`2.x`版本 升级至 `v3.0.0-beta.0`版本
### 6.2.4、`2.x`版本 升级至 `v3.0.0-beta.0`版本
**升级步骤:**
@@ -123,14 +136,14 @@ ALTER COLUMN `operation_methods` set default '';
UPDATE ks_km_topic
INNER JOIN
(SELECT
topic.cluster_id AS cluster_id,
topic.topic_name AS topic_name,
topic.description AS description
topic.cluster_id AS cluster_id,
topic.topic_name AS topic_name,
topic.description AS description
FROM topic WHERE description != ''
) AS t
ON ks_km_topic.cluster_phy_id = t.cluster_id
AND ks_km_topic.topic_name = t.topic_name
AND ks_km_topic.id > 0
SET ks_km_topic.description = t.description;
ON ks_km_topic.cluster_phy_id = t.cluster_id
AND ks_km_topic.topic_name = t.topic_name
AND ks_km_topic.id > 0
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`
应用启动后需要到基座应用中查看(需要启动基座应用,即 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、常用功能

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.constant.MsgConstant;
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.exception.AdminOperateException;
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()));
}
if (GroupOffsetResetEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()
if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()
&& ValidateUtils.isEmptyList(dto.getOffsetList())) {
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())) {
return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误,指定时间重置需传时间信息");
}
@@ -213,7 +213,7 @@ public class GroupManagerImpl implements GroupManager {
}
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(
elem -> new TopicPartition(dto.getTopicName(), elem.getPartitionId()),
PartitionOffsetDTO::getOffset,
@@ -222,9 +222,9 @@ public class GroupManagerImpl implements GroupManager {
}
OffsetSpec offsetSpec = null;
if (GroupOffsetResetEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()) {
if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()) {
offsetSpec = OffsetSpec.forTimestamp(dto.getTimestamp());
} else if (GroupOffsetResetEnum.EARLIEST.getResetType() == dto.getResetType()) {
} else if (OffsetTypeEnum.EARLIEST.getResetType() == dto.getResetType()) {
offsetSpec = OffsetSpec.earliest();
} else {
offsetSpec = OffsetSpec.latest();
@@ -272,15 +272,11 @@ public class GroupManagerImpl implements GroupManager {
// 获取Group指标信息
Result<List<GroupMetrics>> groupMetricsResult = groupMetricService.listPartitionLatestMetricsFromES(
clusterPhyId,
groupName,
topicName,
latestMetricNames == null? Arrays.asList(): latestMetricNames
);
Result<List<GroupMetrics>> groupMetricsResult = groupMetricService.collectGroupMetricsFromKafka(clusterPhyId, groupName, latestMetricNames == null ? Arrays.asList() : latestMetricNames);
// 转换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<>();
for (GroupMetrics groupMetrics: esGroupMetricsList) {
esMetricsMap.put(groupMetrics.getPartitionId(), groupMetrics);

View File

@@ -1,5 +1,6 @@
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.entity.result.Result;
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.KafkaConstant;
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.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.NotExistException;
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.core.service.broker.BrokerService;
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.topic.TopicConfigService;
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.TopicService;
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.consumer.ConsumerConfig;
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.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.config.TopicConfig;
import org.springframework.beans.factory.annotation.Autowired;
@@ -160,8 +161,31 @@ public class TopicStateManagerImpl implements TopicStateManager {
}
maxMessage = Math.min(maxMessage, dto.getMaxRecords());
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) {
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之后超过要求的时间
@@ -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())));
} catch (Exception 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.service.ConfigService;
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.entity.config.metric.UserMetricConfig;
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.vo.config.metric.UserMetricConfigVO;
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.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.VersionUtil;
@@ -47,29 +49,29 @@ public class VersionControlManagerImpl implements VersionControlManager {
@PostConstruct
public void init(){
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_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_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_OUT, 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_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_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_OUT, 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_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_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_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_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_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_TOTAL_RES_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_CONNECTION_COUNT, 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_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_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_OUT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_PARTITIONS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_REQ_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_RES_QUEUE, 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_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
@@ -159,6 +161,9 @@ public class VersionControlManagerImpl implements VersionControlManager {
UserMetricConfig umc = userMetricConfigMap.get(itemType + "@" + metric);
userMetricConfigVO.setSet(null != umc && umc.isSet());
if (umc != null) {
userMetricConfigVO.setRank(umc.getRank());
}
userMetricConfigVO.setName(itemVO.getName());
userMetricConfigVO.setType(itemVO.getType());
userMetricConfigVO.setDesc(itemVO.getDesc());
@@ -178,13 +183,29 @@ public class VersionControlManagerImpl implements VersionControlManager {
@Override
public Result<Void> updateUserMetricItem(Long clusterId, Integer type, UserMetricConfigDTO dto, String operator) {
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();
}
Set<UserMetricConfig> userMetricConfigs = getUserMetricConfig(operator);
for(Map.Entry<String, Boolean> metricAndShowEntry : metricsSetMap.entrySet()){
UserMetricConfig userMetricConfig = new UserMetricConfig(type, metricAndShowEntry.getKey(), metricAndShowEntry.getValue());
for (MetricDetailDTO metricDetailDTO : metricDetailMap.values()) {
UserMetricConfig userMetricConfig = new UserMetricConfig(type, metricDetailDTO.getMetric(), metricDetailDTO.getSet(), metricDetailDTO.getRank());
userMetricConfigs.remove(userMetricConfig);
userMetricConfigs.add(userMetricConfig);
}
@@ -228,7 +249,7 @@ public class VersionControlManagerImpl implements VersionControlManager {
return defaultMetrics;
}
return JSON.parseObject(value, new TypeReference<Set<UserMetricConfig>>(){});
return JSON.parseObject(value, new TypeReference<Set<UserMetricConfig>>() {});
}
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.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.ZKConfig;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -34,4 +35,8 @@ public class ClusterPhyBaseDTO extends BaseDTO {
@NotNull(message = "jmxProperties不允许为空")
@ApiModelProperty(value="Jmx配置")
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.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.enums.OffsetTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -23,7 +24,7 @@ public class GroupOffsetResetDTO extends ClusterTopicDTO {
private String groupName;
/**
* @see com.xiaojukeji.know.streaming.km.common.enums.GroupOffsetResetEnum
* @see OffsetTypeEnum
*/
@NotNull(message = "resetType不允许为空")
@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.NoArgsConstructor;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@@ -17,4 +19,8 @@ import java.util.Map;
public class UserMetricConfigDTO extends BaseDTO {
@ApiModelProperty("指标展示设置项key指标名value是否展现(true展现/false不展现)")
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;
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.ApiModelProperty;
import lombok.Data;
@@ -15,7 +16,7 @@ import javax.validation.constraints.NotNull;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel(description = "Topic记录")
public class TopicRecordDTO extends BaseDTO {
public class TopicRecordDTO extends PaginationSortDTO {
@NotNull(message = "truncate不允许为空")
@ApiModelProperty(value = "是否截断", example = "true")
private Boolean truncate;
@@ -34,4 +35,13 @@ public class TopicRecordDTO extends BaseDTO {
@ApiModelProperty(value = "预览超时时间", example = "10000")
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.po.broker.BrokerPO;
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.Data;
import lombok.NoArgsConstructor;
@@ -79,20 +78,6 @@ public class Broker implements Serializable {
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) {
Broker broker = ConvertUtil.obj2Obj(brokerPO, Broker.class);
String endpointMapStr = brokerPO.getEndpointMap();

View File

@@ -53,9 +53,16 @@ public class ClusterPhy implements Comparable<ClusterPhy>, EntifyIdInterface {
/**
* jmx配置
* @see com.xiaojukeji.know.streaming.km.common.bean.entity.config.JmxConfig
*/
private String jmxProperties;
/**
* zk配置
* @see com.xiaojukeji.know.streaming.km.common.bean.entity.config.ZKConfig
*/
private String zkProperties;
/**
* 开启ACL
* @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;
import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserMetricConfig {
private int type;
@@ -15,6 +15,22 @@ public class UserMetricConfig {
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
public int hashCode(){
return metric.hashCode() << 1 + type;

View File

@@ -41,6 +41,11 @@ public class ClusterPhyPO extends BasePO {
*/
private String jmxProperties;
/**
* zk配置
*/
private String zkProperties;
/**
* 认证类型
* @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 = "{}")
protected String jmxProperties;
@ApiModelProperty(value="ZK配置", example = "{}")
protected String zkProperties;
@ApiModelProperty(value="描述", example = "测试")
protected String description;
@ApiModelProperty(value="集群的kafka版本", example = "2.5.1")
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 {
@ApiModelProperty(value = "该指标用户是否设置展现", example = "true")
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 String DEFAULT_USER_NAME = "know-streaming-app";
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 Integer DEFAULT_RETRY_TIME = 3;
}

View File

@@ -52,6 +52,10 @@ public class MsgConstant {
/**************************************************** 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) {
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.setClientProperties(ConvertUtil.obj2Json(dto.getClientProperties()));
clusterPhyPO.setJmxProperties(ConvertUtil.obj2Json(dto.getJmxProperties()));
if (ValidateUtils.isNull(dto.getZkProperties())) {
clusterPhyPO.setZkProperties("");
} else {
clusterPhyPO.setZkProperties(ConvertUtil.obj2Json(dto.getZkProperties()));
}
clusterPhyPO.setRunState(
ValidateUtils.isBlank(dto.getZookeeper())?
ClusterRunStateEnum.RUN_RAFT.getRunState() :
@@ -32,6 +37,11 @@ public class ClusterConverter {
ClusterPhyPO clusterPhyPO = ConvertUtil.obj2Obj(dto, ClusterPhyPO.class);
clusterPhyPO.setClientProperties(ConvertUtil.obj2Json(dto.getClientProperties()));
clusterPhyPO.setJmxProperties(ConvertUtil.obj2Json(dto.getJmxProperties()));
if (ValidateUtils.isNull(dto.getZkProperties())) {
clusterPhyPO.setZkProperties("");
} else {
clusterPhyPO.setZkProperties(ConvertUtil.obj2Json(dto.getZkProperties()));
}
clusterPhyPO.setRunState(
ValidateUtils.isBlank(dto.getZookeeper())?
ClusterRunStateEnum.RUN_RAFT.getRunState() :

View File

@@ -3,19 +3,19 @@ package com.xiaojukeji.know.streaming.km.common.enums;
import lombok.Getter;
/**
* 重置offset
* offset类型
* @author zengqiao
* @date 19/4/8
*/
@Getter
public enum GroupOffsetResetEnum {
LATEST(0, "重置到最新"),
public enum OffsetTypeEnum {
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;
GroupOffsetResetEnum(int resetType, String message) {
OffsetTypeEnum(int resetType, String message) {
this.resetType = resetType;
this.message = message;
}

View File

@@ -90,6 +90,8 @@ public class JmxConnectorWrap {
}
try {
jmxConnector.close();
jmxConnector = null;
} catch (IOException e) {
LOGGER.warn("close JmxConnector exception, physicalClusterId:{} brokerId:{} host:{} port:{}.", physicalClusterId, brokerId, host, port, e);
}
@@ -105,6 +107,11 @@ public class JmxConnectorWrap {
acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.getAttribute(name, attribute);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally {
atomicInteger.incrementAndGet();
}
@@ -120,6 +127,11 @@ public class JmxConnectorWrap {
acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.getAttributes(name, attributes);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally {
atomicInteger.incrementAndGet();
}
@@ -131,6 +143,11 @@ public class JmxConnectorWrap {
acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.queryNames(name, query);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally {
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": {
"version": "1.2.5",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==",
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==",
"dev": true
},
"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"
}
},
"@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": {
"version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -6815,9 +6825,9 @@
"dev": true
},
"is-callable": {
"version": "1.2.5",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==",
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==",
"dev": true
},
"is-color-stop": {

View File

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

View File

@@ -22,6 +22,20 @@
display: flex;
justify-content: space-between;
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;
},
(err: any) => {
const config = err.config;
if (!config || !config.retryTimes) return dealResponse(err, config.customNotification);
const config = err?.config;
if (!config || !config.retryTimes) return dealResponse(err);
const { __retryCount = 0, retryDelay = 300, retryTimes } = config;
config.__retryCount = __retryCount;
if (__retryCount >= retryTimes) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,13 +44,3 @@
.role-tab-assign-user .desc-row {
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');
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 pkgJson = require('./package');
const getWebpackCommonConfig = require('./config/d1-webpack.base');
const outPath = path.resolve(__dirname, `../../../km-rest/src/main/resources/templates/${pkgJson.ident}`);
const jsFileName = isProd ? '[name]-[chunkhash].js' : '[name].js';
const devMode = process.env.NODE_ENV === 'development';
const commonConfig = require('./config/webpack.common');
const devConfig = require('./config/webpack.dev');
const prodConfig = require('./config/webpack.prod');
module.exports = merge(getWebpackCommonConfig(), {
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: {},
},
});
module.exports = merge(commonConfig, devMode ? devConfig : prodConfig);

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"
}
},
"@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": {
"version": "2.1.5",
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -6562,6 +6578,12 @@
"kind-of": "^4.0.0"
},
"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": {
"version": "3.0.0",
"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",
"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": {
"version": "1.2.0",
"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",
"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": {
"version": "4.10.1",
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -7224,28 +7235,6 @@
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"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": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz",
@@ -7287,6 +7276,12 @@
"kind-of": "^3.0.2"
},
"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": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -7354,16 +7349,10 @@
"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": {
"version": "1.2.5",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw=="
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q=="
},
"is-color-stop": {
"version": "1.1.0",
@@ -7396,6 +7385,12 @@
"kind-of": "^3.0.2"
},
"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": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -7478,9 +7473,9 @@
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
},
"is-lite": {
"version": "0.8.2",
"resolved": "https://registry.npmmirror.com/is-lite/-/is-lite-0.8.2.tgz",
"integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw=="
"version": "0.9.2",
"resolved": "https://registry.npmmirror.com/is-lite/-/is-lite-0.9.2.tgz",
"integrity": "sha512-qZuxbaEiKLOKhX4sbHLfhFN9iA3YciuZLb37/DfXCpWnz8p7qNL2lwkpxYMXfjlS8eEEjpULPZxAUI8N6FYvYQ=="
},
"is-negative-zero": {
"version": "2.0.2",
@@ -7531,12 +7526,6 @@
"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": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -7849,9 +7838,9 @@
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
@@ -8287,6 +8276,13 @@
"requires": {
"fault": "^1.0.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": {
@@ -8799,6 +8795,12 @@
"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": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -9092,19 +9094,6 @@
"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": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
@@ -10259,9 +10248,9 @@
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
@@ -10385,9 +10374,9 @@
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
@@ -10661,9 +10650,9 @@
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
@@ -10757,9 +10746,9 @@
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"version": "5.24.4",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.4.tgz",
"integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
@@ -10780,6 +10769,18 @@
"rc-util": "^5.7.0"
},
"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": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
@@ -11045,6 +11046,13 @@
"prop-types": "^15.8.1",
"react-proptype-conditional-require": "^1.0.4",
"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": {
@@ -11122,6 +11130,30 @@
"intl-messageformat": "^7.8.4",
"intl-messageformat-parser": "^3.6.4",
"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": {
@@ -11130,19 +11162,19 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-joyride": {
"version": "2.5.2",
"resolved": "https://registry.npmmirror.com/react-joyride/-/react-joyride-2.5.2.tgz",
"integrity": "sha512-wsSYX3PhVrdzdd0/fv5f6ySGvb7QyAzleQv/x9IH+x+SXO6b5MUJUkefS+189bgLPkuHMwtSRcPE/oMupfmCVQ==",
"version": "2.5.3",
"resolved": "https://registry.npmmirror.com/react-joyride/-/react-joyride-2.5.3.tgz",
"integrity": "sha512-DKKvb/JAAsHm0x/RWO3WI6NOtTMHDso5v8MTauxTSz2dFs7Tu1rWg1BDBWmEMj6pUCvem7hblFbCiDAcvhs8tQ==",
"requires": {
"deepmerge": "^4.2.2",
"exenv": "^1.2.2",
"is-lite": "^0.8.2",
"is-lite": "^0.9.2",
"prop-types": "^15.8.1",
"react-floater": "^0.7.6",
"react-is": "^16.13.1",
"scroll": "^3.0.1",
"scrollparent": "^2.0.1",
"tree-changes": "^0.9.1"
"tree-changes": "^0.9.2"
}
},
"react-lifecycles-compat": {
@@ -11282,6 +11314,13 @@
"lowlight": "^1.14.0",
"prismjs": "^1.21.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": {
@@ -11449,6 +11488,31 @@
"prismjs": "~1.27.0"
},
"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": {
"version": "1.27.0",
"resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz",
@@ -12286,6 +12350,12 @@
"kind-of": "^3.2.0"
},
"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": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -12347,6 +12417,14 @@
"dev": true,
"requires": {
"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": {
@@ -13049,6 +13127,12 @@
"kind-of": "^3.0.2"
},
"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": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -13098,12 +13182,19 @@
"integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g=="
},
"tree-changes": {
"version": "0.9.1",
"resolved": "https://registry.npmmirror.com/tree-changes/-/tree-changes-0.9.1.tgz",
"integrity": "sha512-Un6R1T6eUStAVbN4G+2djuXEk271mDY78ptxZUUo+TVcwvHZeUgk+pwXZjOZLAJ9n0+p47KUijeuNJSmpuG6Dw==",
"version": "0.9.3",
"resolved": "https://registry.npmmirror.com/tree-changes/-/tree-changes-0.9.3.tgz",
"integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==",
"requires": {
"@gilbarbara/deep-equal": "^0.1.1",
"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": {
@@ -13502,12 +13593,6 @@
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"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": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@@ -13718,6 +13803,13 @@
"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": {
"version": "3.0.0",
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -14338,6 +14436,12 @@
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -14439,6 +14543,14 @@
"requires": {
"ansi-colors": "^3.0.0",
"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": {

View File

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

View File

@@ -3,7 +3,8 @@ import '@babel/polyfill';
import React, { useState, useEffect, useLayoutEffect } from 'react';
import { BrowserRouter, Switch, Route, useLocation, useHistory } from 'react-router-dom';
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 dantdEnUS from 'knowdesign/es/locale/en_US';
import { DotChartOutlined } from '@ant-design/icons';

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
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 './index.less';
import api from '@src/api';
@@ -110,8 +111,8 @@ const CardBar = (props: CardBarProps) => {
const promise = record
? Utils.request(path)
: Utils.request(path, {
params: { dimensionCode: sceneObj.code },
});
params: { dimensionCode: sceneObj.code },
});
promise.then((data: any[]) => {
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 { Input, IconFont } from 'knowdesign';
import { Input } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import './style/index.less';
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 moment from 'moment';
import api, { MetricType } from '@src/api';
import { MetricDefaultChartDataType, MetricChartDataType, formatChartData } from '@src/constants/chartConfig';
import { useParams } from 'react-router-dom';
import { debounce } from 'lodash';
import { MetricDefaultChartDataType, MetricChartDataType, formatChartData, getDetailChartConfig } from './config';
import { getDetailChartConfig } from './config';
import { UNIT_MAP } from '@src/constants/chartConfig';
import RenderEmpty from '../RenderEmpty';
@@ -50,8 +51,6 @@ interface DataZoomEventProps {
const DATA_ZOOM_DEFAULT_SCALE = 0.25;
// 单次向服务器请求数据的范围(默认 6 小时,超过后采集频率间隔会变长),单位: ms
const DEFAULT_REQUEST_TIME_RANGE = 6 * 60 * 60 * 1000;
// 采样间隔,影响前端补点逻辑,单位: ms
const DEFAULT_POINT_INTERVAL = 60 * 1000;
// 向服务器每轮请求的数量
const DEFAULT_REQUEST_COUNT = 6;
// 进入详情页默认展示的时间范围
@@ -376,8 +375,6 @@ const ChartDetail = (props: ChartDetailProps) => {
global.getMetricDefine || {},
metricType,
timeRange,
DEFAULT_POINT_INTERVAL,
false,
chartInfo.current.transformUnit
) as MetricChartDataType[];
// 增量填充图表数据
@@ -540,14 +537,7 @@ const ChartDetail = (props: ChartDetailProps) => {
if (res?.length) {
// 格式化图表需要的数据
const formattedMetricData = (
formatChartData(
res,
global.getMetricDefine || {},
metricType,
curTimeRange,
DEFAULT_POINT_INTERVAL,
false
) as MetricChartDataType[]
formatChartData(res, global.getMetricDefine || {}, metricType, curTimeRange) as MetricChartDataType[]
)[0];
// 填充图表数据
let initFullTimeRange = curTimeRange;

View File

@@ -1,150 +1,4 @@
import { getUnit, getDataNumberUnit, 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;
});
};
import { getBasicChartConfig, CHART_COLOR_LIST } from '@src/constants/chartConfig';
const seriesCallback = (lines: { name: string; data: [number, string | number][] }[]) => {
const len = CHART_COLOR_LIST.length;

View File

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

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 { SortableContainer, SortableContainerProps, SortableHandle, SortableElement, SortableElementProps } from 'react-sortable-hoc';
import './index.less';

View File

@@ -1,5 +1,6 @@
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 './style/indicator-drawer.less';
import { useLocation } from 'react-router-dom';

View File

@@ -1,5 +1,6 @@
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 './style/node-scope.less';

View File

@@ -1,5 +1,6 @@
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 { DRangeTime } from 'knowdesign';
import IndicatorDrawer from './IndicatorDrawer';

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
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';
const { Option } = Select;

View File

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

View File

@@ -1,5 +1,44 @@
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 = [
'#556ee6',
'#94BEF2',
@@ -16,27 +55,176 @@ export const CHART_COLOR_LIST = [
'#C9E795',
];
// 图表存储单位换算
export const UNIT_MAP = {
TB: Math.pow(1024, 4),
GB: Math.pow(1024, 3),
MB: Math.pow(1024, 2),
KB: 1024,
};
export const getUnit = (value: number) => Object.entries(UNIT_MAP).find(([, size]) => value / size >= 1) || ['Byte', 1];
// 图表数字单位换算
export const DATA_NUMBER_MAP = {
十亿: Math.pow(1000, 3),
百万: Math.pow(1000, 2),
: 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 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 基础展示样式
const tooltipFormatter = (date: any, arr: any, tooltip: any) => {
// 从大到小排序
// arr = arr.sort((a: any, b: any) => b.value - a.value);
const str = arr
.map(
(item: any) => `<div style="margin: 3px 0;">
@@ -121,9 +309,6 @@ export const getBasicChartConfig = (props: any = {}) => {
itemWidth: 8,
itemGap: 8,
textStyle: {
// width: 85,
// overflow: 'truncate',
// ellipsis: '...',
fontSize: 11,
color: '#74788D',
},

View File

@@ -37,9 +37,6 @@ export const SMALL_DRAWER_WIDTH = 480;
export const MIDDLE_DRAWER_WIDTH = 728;
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 numberToFixed = (value: number, num = 2) => {
@@ -261,3 +258,6 @@ export const timeFormater = function formatDuring(mss: number) {
.map((o: any) => `${o.v}${o.unit}`)
.join();
};
// 列表页Header布局前缀
export const tableHeaderPrefix = 'table-header-layout';

View File

@@ -280,3 +280,38 @@ li {
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.overview`]: 'Overview',
[`menu.${systemKey}.cluster.balance`]: 'Load Rebalance',
[`menu.${systemKey}.broker`]: 'Broker',
[`menu.${systemKey}.broker.dashbord`]: 'Overview',
@@ -45,7 +44,7 @@ export default {
[`menu.${systemKey}.consumer-group.group-list`]: 'GroupList',
[`menu.${systemKey}.operation`]: 'Operation',
[`menu.${systemKey}.operation.balance`]: 'Load Rebalance',
[`menu.${systemKey}.operation.balance`]: 'Rebalance',
[`menu.${systemKey}.operation.jobs`]: 'Job',
[`menu.${systemKey}.acls`]: 'ACLs',

View File

@@ -1,12 +1,14 @@
import React, { useState, useEffect } from 'react';
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 { getControllerChangeLogListColumns, defaultPagination } from './config';
import BrokerDetail from '../BrokerDetail';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
import './index.less';
import { tableHeaderPrefix } from '@src/constants/common';
const { request } = Utils;
const ControllerChangeLogList: React.FC = (props: any) => {
@@ -89,26 +91,35 @@ const ControllerChangeLogList: React.FC = (props: any) => {
<BrokerHealthCheck />
</div>
<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
showQueryForm={false}
tableProps={{
showHeader: true,
showHeader: false,
rowKey: 'path',
loading: loading,
columns: getControllerChangeLogListColumns(),
dataSource: data,
paginationProps: { ...pagination },
tableHeaderSearchInput: {
// 搜索配置
submit: getSearchKeywords,
searchInputType: 'search',
searchAttr: {
placeholder: '请输入Broker Host',
style: {
width: '248px',
},
},
},
attrs: {
onChange: onTableChange,
bordered: false,

View File

@@ -1,5 +1,6 @@
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 Api from '@src/api';
export const ConfigurationEdit = (props: any) => {

View File

@@ -1,5 +1,6 @@
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) => {
const columns: any = [
{

View File

@@ -1,9 +1,10 @@
import React, { useState, useEffect, memo } from 'react';
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 { getBrokerListColumns, defaultPagination } from './config';
import { dealTableRequestParams } from '../../constants/common';
import { tableHeaderPrefix } from '@src/constants/common';
import BrokerDetail from '../BrokerDetail';
import CardBar from '@src/components/CardBar';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
@@ -33,7 +34,6 @@ const BrokerList: React.FC = (props: any) => {
if (urlParams?.clusterId === undefined) return;
// filters = filters || filteredInfo;
setLoading(true);
// const params = dealTableRequestParams({ searchKeywords, pageNo, pageSize });
const params = {
searchKeywords: searchKeywords.slice(0, 128),
pageNo,
@@ -99,29 +99,36 @@ const BrokerList: React.FC = (props: any) => {
<BrokerHealthCheck />
</div>
<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
key="brokerTable"
showQueryForm={false}
tableProps={{
showHeader: true,
showHeader: false,
rowKey: 'broker_list',
loading: loading,
columns: getBrokerListColumns(),
dataSource: data,
paginationProps: { ...pagination },
tableHeaderSearchInput: {
// 搜索配置
submit: getSearchKeywords,
searchInputType: 'search',
searchAttr: {
placeholder: '请输入Broker Host',
maxLength: 128,
style: {
width: '248px',
borderRiadus: '8px',
},
},
},
attrs: {
onChange: onTableChange,
scroll: { x: 'max-content', y: 'calc(100vh - 400px)' },

View File

@@ -1,15 +1,15 @@
import React, { useState, useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom';
import CopyToClipboard from 'react-copy-to-clipboard';
import { AppContainer, Divider, Drawer, IconFont, ProTable, Select, SingleChart, Space, Tooltip, Utils } from 'knowdesign';
import { AppContainer, Divider, Drawer, ProTable, Select, SingleChart, Space, Tooltip, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { DRangeTime } from 'knowdesign';
import { CHART_COLOR_LIST, getBasicChartConfig } from '@src/constants/chartConfig';
import Api from '@src/api/index';
import { hashDataParse } from '@src/constants/common';
import { ClustersPermissionMap } from '../CommonConfig';
import ResetOffsetDrawer from './ResetOffsetDrawer';
import { CheckCircleFilled } from '@ant-design/icons';
import SwitchTab from '@src/components/SwitchTab';
import ContentWithCopy from '@src/components/CopyContent';
const { Option } = Select;
@@ -44,33 +44,6 @@ const metricWithType = [
{ 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) => {
const { scene } = props;
const params = useParams<{
@@ -104,6 +77,9 @@ export default (props: any) => {
title: 'Topic Partition',
dataIndex: 'partitionId',
key: 'partitionId',
lineClampOne: true,
needTooltip: true,
width: 180,
render: (v: string, record: any) => {
return `${record.topicName}-${v}`;
},

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
/* eslint-disable react/display-name */
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 Api from '@src/api';
import { getTaskDetailsColumns, getMoveBalanceColumns } from './config';

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import moment from 'moment';
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 NodeTraffic from './NodeTraffic';
import RebalancePlan from './RebalancePlan';

View File

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

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, memo } from 'react';
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 { getJobsListColumns, defaultPagination, runningStatus, jobType } from './config';
import JobsCheck from '@src/components/CardBar/JobsCheck';
@@ -10,6 +11,7 @@ import './index.less';
import ReplicaChange from '@src/components/TopicJob/ReplicaChange';
import ReplicaMove from '@src/components/TopicJob/ReplicaMove';
import BalanceDrawer from '../LoadRebalance/BalanceDrawer';
import { tableHeaderPrefix } from '@src/constants/common';
const { request } = Utils;
const JobsList: React.FC = (props: any) => {
@@ -171,35 +173,44 @@ const JobsList: React.FC = (props: any) => {
</div>
{/* <Form form={form} layout="inline" onFinish={onFinish}> */}
<div className="clustom-table-content">
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '12px' }}>
<Form form={form} layout="inline" onFinish={onFinish}>
<Form.Item name="type">
<Select options={jobType} style={{ width: '190px' }} className={'detail-table-select'} placeholder="选择任务类型" />
</Form.Item>
<Form.Item name="jobTarget">
<Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" />
</Form.Item>
<Form.Item name="status">
<Select
mode="multiple"
maxTagCount={'responsive'}
options={runningStatus}
style={{ width: '190px' }}
className={'detail-table-select'}
placeholder="选择运行状态"
showArrow
allowClear
/>
</Form.Item>
</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>
<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>
<Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<Form form={form} layout="inline" onFinish={onFinish}>
<Form.Item name="type">
<Select options={jobType} style={{ width: '190px' }} className={'detail-table-select'} placeholder="选择任务类型" />
</Form.Item>
<Form.Item name="jobTarget">
<Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" />
</Form.Item>
<Form.Item name="status">
<Select
mode="multiple"
maxTagCount={'responsive'}
options={runningStatus}
style={{ width: '190px' }}
className={'detail-table-select'}
placeholder="选择运行状态"
showArrow
allowClear
/>
</Form.Item>
</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>
{/* </Form> */}

View File

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

View File

@@ -1,5 +1,6 @@
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';
const balancePrefix = 'custom-popover-balance';

View File

@@ -1,5 +1,6 @@
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 api from '../../api';
import './style/BalanceDrawer.less';

View File

@@ -1,5 +1,6 @@
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';
interface PropsType extends React.HTMLAttributes<HTMLDivElement> {

View File

@@ -1,5 +1,6 @@
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 HistoryDrawer from './HistoryDrawer';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
@@ -9,6 +10,7 @@ import './index.less';
import LoadRebalanceCardBar from '@src/components/CardBar/LoadRebalanceCardBar';
import { BalanceFilter } from './BalanceFilter';
import { ClustersPermissionMap } from '../CommonConfig';
import { tableHeaderPrefix } from '@src/constants/common';
const Balance_Status_OPTIONS = [
{
@@ -132,7 +134,7 @@ const LoadBalance: React.FC = (props: any) => {
key: 'disk_spec',
width: '150px',
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 ? (
<span>
<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>
) : (
'-'
@@ -157,7 +162,7 @@ const LoadBalance: React.FC = (props: any) => {
key: 'bytesIn_spec',
width: '150px',
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 ? (
<span>
<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>
) : (
'-'
@@ -182,7 +190,7 @@ const LoadBalance: React.FC = (props: any) => {
key: 'bytesOut_spec',
width: '150px',
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 ? (
<span>
<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>
) : (
'-'
@@ -330,7 +341,7 @@ const LoadBalance: React.FC = (props: any) => {
breadcrumbs={[
{ label: '多集群管理', aHref: '/' },
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
{ label: 'Load Rebalance', aHref: `` },
{ label: 'Rebalance', aHref: `` },
]}
/>
</div>
@@ -339,7 +350,17 @@ const LoadBalance: React.FC = (props: any) => {
</div>
<div className="load-rebalance-container">
<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.Item name="status">
<Select className="grid-select" placeholder="请选择状态" style={{ width: '180px' }} options={Balance_Status_OPTIONS} />
@@ -354,8 +375,7 @@ const LoadBalance: React.FC = (props: any) => {
</Button>
</Form.Item>
</Form> */}
<BalanceFilter title="负载均衡列表筛选" data={[]} getNorms={getNorms} filterList={filterList} />
<div className="float-r">
<div className={`${tableHeaderPrefix}-right`}>
<SearchInput
onSearch={hostSearch}
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