已合并
智能运维模块630需求内容,含实例监控插件、SQL诊断插件、告警通知插件 #277
Louisyzh创建于 2023年6月24日
智能运维模块630需求内容,含实例监控插件、SQL诊断插件、告警通知插件 #277
已合并
Louisyzh创建于 2023年6月24日
master合入到master
754 个文件变更+59336-22775
Aplugins/alert-center/README.md+543-0
@@ -0,0 +1,543 @@
1+# 告警通知插件(alert-center) 用户手册
2+ 
3+ 
4+ 
5+## 1告警通知插件(alert-center)用户手册
6+ 
7+​ 告警通知插件主要目的是为 openGauss 用户提供告警通知能力。它作为一体化平台的可插拔插件开发,本特性依赖于openGauss一体化平台的插件特性。
8+ 
9+## 2 前言
10+ 
11+ 
12+ 
13+### 2.1 概述
14+ 
15+​ 本章介绍手册相关信息。
16+ 
17+ 
18+ 
19+### 2.2 读者对象
20+ 
21+​ 本手册主要适用于以下人员:
22+ 
23+- 数据库开发人员
24+- 数据库管理员
25+ 
26+ 
27+ 
28+### 2.3 修订记录
29+ 
30+| 日期 | 版本 | 变更说明 | 修改人 | 审核人 |
31+| ---------- | ------ | ------------ | -------- | ----------- |
32+| 2023/06/12 | v1.0.0 | 新增编译初版 | wuyuebin | zengseliang |
33+ 
34+ 
35+ 
36+### 2.4 文档约定
37+ 
38+​ 本节描述了本手册的(内容、符号、GUI 和文本)约定。
39+ 
40+**图形化界面格式约定**
41+ 
42+​ 本手册中可能出现下列图形化界面格式约定,它们所代表的含义如下。
43+ 
44+| 格式 | 说明 |
45+| ---- | ------------------------------------------------------------ |
46+| 粗体 | 按钮、菜单、参数、页签、窗口及对话框标题均使用【】括起来。例如,单击【确定】。 |
47+ 
48+ 
49+ 
50+### 2.5 第三方许可证
51+ 
52+​ 本节包含适用于该插件的第三方许可证。
53+ 
54+**表2-1 第三方软件列表**
55+ 
56+| 第三方软件 |
57+| -------------- |
58+| 木兰宽松许可证 |
59+ 
60+ 
61+ 
62+### 2.6 参考文档
63+ 
64+
65+ 
66+
67+ 
68+## 3告警通知插件(alert-center)简介
69+ 
70+ 
71+ 
72+### 3.1 概述
73+ 
74+​ 告警通知插件主要目的是为 openGauss 用户提供告警通知能力。它作为一体化平台的可插拔插件开发,本特性依赖于openGauss一体化平台的插件特性。
75+ 
76+​ 实例监控功能主要分监控告警和通知配置两大功能。其中监控告警分告警记录、告警配置、告警模板、告警规则;通知配置分通知模板和通知方式,而通知方式又分为通知方式和通知渠道。
77+ 
78+ 
79+ 
80+### 3.2 支持的功能
81+ 
82+​ 告警通知插件(alert-center)提供的功能如下:
83+ 
84+**告警记录**
85+ 
86+- 展示prometheus发送过来的告警信息
87+- 统计告警信息
88+- 显示关联视图:根据告警规则获取指标的告警数据,通过图表展示
89+ 
90+**告警配置**
91+ 
92+- 展示告警配置列表
93+- 支持为单个或者批量实例配置模板
94+- 也可以直接为实例配置规则
95+ 
96+**告警模板**
97+ 
98+- 展示告警模板列表
99+- 支持增删改查
100+- 可以对告警模板中的规则进行修改
101+ 
102+**告警规则**
103+ 
104+- 展示告警规则列表,可查询规则详细
105+- 已内置一些规则:CPU使用率、内存使用率、磁盘使用量、IO、数据库LOCK个数和锁时间的告警
106+ 
107+**通知模板**
108+ 
109+- 展示通知模板列表,支持增删改查
110+ 
111+**通知方式**
112+ 
113+- 展示通知方式列表,支持增删改查
114+- 目前只支持邮箱的通知方式
115+ 
116+**通知渠道**
117+ 
118+- 展示发送人的邮箱、企业微信和钉钉相关信息,支持修改
119+- 提供通知渠道是否有效的测试功能,目前只支持邮箱测试
120+- 通知渠道目前只支持通过邮箱去发送告警信息
121+ 
122+ 
123+ 
124+### 3.3 约束和限制
125+ 
126+**安装插件前提**
127+ 
128+安装该插件之前,必须先安装实例监控插件,并在插件中安装prometheus
129+ 
130+**第一次使用**
131+ 
132+第一次安装该插件时,需要去设置告警接口的IP和端口号,IP为平台IP,端口后为后端的端口号。
133+ 
134+**项目运行**
135+ 
136+本项目依赖一体化主平台,若需要使用本项目所有功能,只能通过编译成 jar 包的形式作为插件运行在主平台上。
137+ 
138+ 
139+ 
140+### 3.4 告警通知插件(alert-center)项目结构
141+ 
142+下载告警通知插件(alert-center)项目代码
143+ 
144+```
145+git clone https://gitee.com/opengauss/openGauss-workbench
146+```
147+ 
148+告警通知插件(alert-center)项目结构如下图所示:
149+ 
150+![1](doc/1.png)
151+ 
152+告警通知插件项目结构说明如下:
153+ 
154+| 文件夹/文件 | 说明 |
155+| ----------- | ---------------------- |
156+| src | 保存后端代码及资源文件 |
157+| web-ui | 保存前端代码 |
158+| .gitignore | 提交git忽略的目录 |
159+| pom.xml | 后端依赖项 |
160+| LICENSE | 许可证 |
161+ 
162+### 3.5 系统要求
163+ 
164+​ 本节介绍使用告警通知插件(alert center)的最低系统要求。
165+ 
166+**系统要求**
167+ 
168+| 操作系统 | 版本 |
169+| -------- | -------------- |
170+| windows | windows7及以上 |
171+ 
172+**软件要求**
173+ 
174+| 软件 | 规格 |
175+| ---- | ----------- |
176+| Java | jdk 8及以上 |
177+ 
178+**数据库版本要求**
179+ 
180+| 数据库 | 版本 |
181+| --------- | ---- |
182+| openGauss | 所有 |
183+ 
184+ 
185+ 
186+## 4 部署告警通知插件(alert-center)
187+ 
188+​ 本章详细介绍如何部署告警通知插件(alert-center)。
189+ 
190+​ 前端技术栈:Vue3.0 + TS + Element plus
191+ 
192+​ 后端技术栈:Java + Spring boot
193+ 
194+### 4.1 编译项目并部署至一体化平台
195+ 
196+**前置条件:**① 安装 node.js,建议使用 v16 以上版本
197+ 
198+​ ② 安装Java jdk (建议使用v11及以上版本) 和 maven 3.X
199+ 
200+**步骤 1:** 检查自动构建前端项目的配置是否为false
201+ 
202+```
203+在 plugins > observability-instance > pom.xml 下将以下值设为false:
204+<web.build.skip>false</web.build.skip>
205+<web.clean.skip>false</web.clean.skip>
206+```
207+ 
208+**步骤 2:** 启动打包命令
209+ 
210+```
211+mvn clean package -P prod
212+```
213+ 
214+**步骤 3:**在target目录下找到生成的alert-center-5.0.0-repackage.jar,安装到一体化平台
215+ 
216+![](doc/2.png)
217+ 
218+### 4.2本地调试项目
219+ 
220+下面介绍如何在本地启动项目进行开发调试。
221+ 
222+#### 4.2.1 启动后端项目
223+ 
224+**前置条件**:安装Java jdk (建议使用v8及以上版本) 和 maven 3.X。
225+ 
226+**步骤 1:**使用idea打开项目,并配置好maven
227+ 
228+**步骤 2:**将pom.xml文件中的的<scope>provided</scope>注释,刷新maven
229+ 
230+![4](doc/4.png)
231+ 
232+**步骤 3:**注释application.yml中的
233+ 
234+```
235+context:
236+ listener:
237+ classes: org.opengauss.plugin.alertcenter.listener.PluginListener
238+```
239+ 
240+![5](doc/5.png)
241+ 
242+ 
243+ 
244+**步骤 4:**修改application-dev.yml中数据库信息,改为自己开发环境的数据库
245+ 
246+![6](doc/6.png)
247+ 
248+ 
249+ 
250+**步骤 5:**启动ObservabilityPluginBootApplication的main函数,启动时,带上--spring.profiles.active=dev,启动时可能会报一些错误,但不会影响系统启动。
251+ 
252+![7](doc/7.png)
253+ 
254+ 
255+ 
256+**步骤 6(可忽略):**报错的原因是:解析用户密码的工具类来自平台,由于是单独启动,无法使用该工具类,导致解码失败。要解决该错误,可以修改下面画圈部分(不修改也不会影响主体流程,但是连接prometheus的服务器功能不可用,无法修改prometheus的配置)
257+ 
258+![8](doc/8.png)
259+ 
260+**步骤 7(可忽略):**将密码改为明文,写死在代码中,然后重新启动项目
261+ 
262+![9](doc/9.png)
263+ 
264+ 
265+ 
266+ 
267+ 
268+#### 4.2.2 启动前端项目
269+ 
270+**前置条件:**安装 node.js,建议使用 v16 以上版本。
271+ 
272+**步骤 1:**下载依赖包
273+ 
274+```
275+ 任选以下一种命令:
276+ npm install
277+ yarn install
278+```
279+ 
280+**步骤 2:**启动前端项目
281+ 
282+```
283+ 根据package.json配置任选以下一种命令:
284+ npm run dev
285+ npm run lint
286+ npm run lint:prettier
287+```
288+ 
289+**步骤 3:** 根据启动提示进入对应的URL地址,如下图所示:
290+ 
291+![](doc/10.png)
292+ 
293+ 
294+ 
295+## 5 使用告警通知插件(alert-center)
296+ 
297+### 5.1 概述
298+ 
299+​ 本章详细介绍如何使用告警通知插件(alert-center) 的功能。
300+ 
301+### 5.2 告警记录
302+ 
303+​ 当触发告警后,Prometheus将告警信息推送到平台,平台将告警信息记录下来,并在告警记录页面展示出来。同时,告警记录支持查看告警详细以及告警的相关指标的关联视图。
304+ 
305+#### 5.2.1 告警记录列表
306+ 
307+**步骤 1:**点击DataKit菜单【监控告警】-【告警记录】,进入告警记录列表,显示告警统计信息、饼图和列表
308+ 
309+**说明:**【集群/实例】、【告警时间范围】选项为可选
310+ 
311+​ 更改【集群/实例】或者【告警时间范围】,将根据条件查询数据
312+ 
313+​ 选择数据,点击【**标记为未读**】,将数据标记为未读
314+ 
315+​ 选择数据,点击【**标记为已读**】,将数据标记为已读
316+ 
317+| 配置项 | 必填 | 组件形式 | 数据类型限制 | 配置说明 |
318+| ------------ | ---- | -------- | ------------ | ------------------- |
319+| 集群/实例 | 否 | 下拉框 | - | |
320+| 告警时间范围 | 否 | 下拉框 | - | yyyy-MM-dd HH:mm:ss |
321+ 
322+![11](doc/11.png)
323+ 
324+#### 5.2.2告警详情
325+ 
326+**步骤 1:**点击上面的告警记录列表数据中的【**查看**】按钮,进入告警详情
327+ 
328+![12](doc/12.png)
329+ 
330+ 
331+ 
332+### 5.3 告警配置
333+ 
334+​ 为实例配置告警模板(包含多个告警规则),并展示在告警配置页面中。同时将实例的告警规则配置到prometheus配置文件中,prometheus将根据配置的规则对实例进行监控。
335+ 
336+#### 5.3.1 告警配置列表显示
337+ 
338+**步骤 1:**点击DataKit菜单【监控告警】-【告警配置】,进入告警配置列表,显示告警配置列表
339+ 
340+**说明:** 【实例名称】条件可选
341+ 
342+​ 单击 查询图标将根据条件查询数据。
343+ 
344+| 配置项 | 必填 | 组件形式 | 数据类型限制 | 配置说明 |
345+| -------- | ---- | -------- | ------------ | -------- |
346+| 实例名称 | 否 | 输入框 | - | |
347+ 
348+![13](doc/13.png)
349+ 
350+ 
351+ 
352+#### 5.3.2 告警配置
353+ 
354+**步骤 1:** 在上面的告警配置列表中,选择数据,点击列表上方的【**配置**】按钮,或者直接列表点击【**配置**】按钮,进入告警配置页面。
355+ 
356+![14](doc/14.png)
357+ 
358+ 
359+ 
360+**步骤 2:**告警配置可根据告警模板进行配置,也可以直接选择告警规则进行配置,下面使用告警规则进行配置
361+ 
362+![15](doc/15.png)
363+ 
364+![16](doc/16.png)
365+ 
366+ 
367+ 
368+### 5.4 告警模板
369+ 
370+​ 告警模板统一组合多个告警规则,为实例提供统一配置告警规则的模板。同时支持对告警规则的再次编辑(比如对告警规则的阈值更改)。告警模板展示模板列表,支持对模板进行增删改查。
371+ 
372+#### 5.4.1 告警模板列表
373+ 
374+**步骤 1:**点击DataKit菜单【监控告警】-【告警模板】,进入告警模板列表,显示告警模板列表
375+ 
376+**说明:** 【模板名称】和【规则名称】条件均可选
377+ 
378+​ 单击 查询图标将根据条件查询数据。
379+ 
380+| 配置项 | 必填 | 组件形式 | 数据类型限制 | 配置说明 |
381+| -------- | ---- | -------- | ------------ | -------- |
382+| 模板名称 | 否 | 输入框 | - | |
383+| 规则名称 | 否 | 输入框 | - | |
384+ 
385+ 
386+ 
387+![17](doc/17.png)
388+ 
389+#### 5.4.2 新增或者修改模板
390+ 
391+**步骤 1:**在上面的告警模板页面左侧的告警模板列表中,点击【**新增**】或者直接点击列表中的【**编辑**】按钮,进入编辑页面
392+ 
393+![18](doc/18.png)
394+ 
395+**步骤 2(可省略):**对告警规则再次编辑,可修改告警级别、告警内容、告警规则阈值、告警描述、告警通知、是否重复通知、是否静默、通知方式等等
396+ 
397+![18](doc/19.png)
398+ 
399+#### 5.4.3 删除告警模板
400+ 
401+**步骤 1:**在5.4.1的告警模板页面左侧的告警模板列表中,点击列表中的【删除】按钮,弹出删除提示,点击【确定】按钮,即可删除告警模板
402+ 
403+![18](doc/20.png)
404+ 
405+ 
406+ 
407+### 5.5 告警规则
408+ 
409+​ 告警规则主要内置多种规则,提供给告警模板进行配置。告警规则页面展示告警规则列表,并能支持查看功能。
410+ 
411+#### 5.5.1 告警规则列表
412+ 
413+ **步骤 1:**点击DataKit菜单【监控告警】-【告警规则】,进入告警规则页面,显示告警规则列表
414+ 
415+**说明:** 【规则名称】、【规则类型】、【告警级别】条件均可选
416+ 
417+​ 单击 查询图标将根据条件查询数据。
418+ 
419+| 配置项 | 必填 | 组件形式 | 数据类型限制 | 配置说明 |
420+| -------- | ---- | -------- | ------------ | -------- |
421+| 规则名称 | 否 | 输入框 | - | |
422+| 规则类型 | 否 | 下拉框 | - | |
423+| 告警级别 | 否 | 下拉框 | - | |
424+ 
425+ 
426+ 
427+![21](doc/21.png)
428+ 
429+#### 5.5.2 告警规则详细
430+ 
431+ **步骤 1:**点击5.5.1告警规则列表中的某个数据的【**查看**】按钮,进入规则详细页面
432+ 
433+![22](doc/22.png)
434+ 
435+ 
436+ 
437+### 5.6 通知模板
438+ 
439+​ 对告警通知的内容进行个性化包装,当出现告警时,将包装后的内容发给用户。支持增删改查,用于通知方式设置。
440+ 
441+#### 5.6.1 通知模板列表
442+ 
443+ **步骤 1:**点击DataKit菜单【通知配置】-【通知模板】,进入通知模板页面,显示通知模板列表
444+ 
445+**说明:** 【模板名称】、【模板类型】条件均可选
446+ 
447+​ 单击 查询图标将根据条件查询数据。
448+ 
449+| 配置项 | 必填 | 组件形式 | 数据类型限制 | 配置说明 |
450+| -------- | ---- | -------- | ------------ | -------- |
451+| 模板名称 | 否 | 输入框 | - | |
452+| 模板类型 | 否 | 下拉框 | - | |
453+ 
454+ 
455+ 
456+![23](doc/23.png)
457+ 
458+ 
459+ 
460+#### 5.6.2 新增/修改通知模板
461+ 
462+**步骤 1:**点击5.6.1 通知模板页面中的【**新增**】按钮或者点击通知模板列表中的【**修改**】按钮,进入模板修改页面
463+ 
464+![24](doc/24.png)
465+ 
466+ 
467+ 
468+#### 5.6.3 删除告警模板
469+ 
470+**步骤 1:**点击5.6.1 通知模板列表中的【**删除**】按钮,弹出删除提示,点击【确定】按钮,即可对通知模板进行删除
471+ 
472+![25](doc/25.png)
473+ 
474+ 
475+ 
476+### 5.7 通知方式
477+ 
478+​ 配置通知人的相关信息(邮件、钉钉、企业微信)和通知模板,为告警规则提供通知方式,支持增删改查。
479+ 
480+#### 5.6.1 通知方式列表
481+ 
482+ **步骤 1:**点击DataKit菜单【通知配置】-【通知方式】,默认选择【通知方式】TAB,进入通知方式页面,显示通知方式列表
483+ 
484+**说明:** 【通知方式名称】、【发送方式】条件均可选
485+ 
486+​ 更改【发送方式】条件将查询数据;
487+ 
488+​ 单击 查询图标将根据条件查询数据。
489+ 
490+| 配置项 | 必填 | 组件形式 | 数据类型限制 | 配置说明 |
491+| ------------ | ---- | -------- | ------------ | -------- |
492+| 通知方式名称 | 否 | 输入框 | - | |
493+| 发送方式 | 否 | 下拉框 | - | |
494+ 
495+ 
496+ 
497+![26](doc/26.png)
498+ 
499+ 
500+ 
501+#### 5.6.2 新增/修改通知方式
502+ 
503+ **步骤 1:**点击5.6.2 通知方式页面中的【**新增**】按钮或者点击列表中的【**修改**】按钮,进入通知方式修改页面
504+ 
505+![27](doc/27.png)
506+ 
507+ 
508+ 
509+#### 5.6.3 删除通知方式
510+ 
511+ **步骤 1:**点击5.6.2 通知方式列表中的【**删除**】按钮,弹出删除提示,点击【确定】按钮,即可对通知方式进行删除
512+ 
513+![28](doc/28.png)
514+ 
515+ 
516+ 
517+### 5.8 通知渠道
518+ 
519+​ 配置发送人的邮箱、企业微信、钉钉等等,目前只支持邮箱。
520+ 
521+#### 5.6.1 通知渠道页面
522+ 
523+ **步骤 1:**点击DataKit菜单【通知配置】-【通知方式】,选择【通知渠道】Tab,进入通知渠道页面
524+ 
525+**步骤 2:**修改相关配置,点击【**确定**】按钮,可修改通知渠道
526+ 
527+![29](doc/29.png)
528+ 
529+ 
530+ 
531+**步骤 3:**点击右边的【**测试**】按钮,对通知渠道进行测试,目前只支持邮箱
532+ 
533+![30](doc/30.png)
534+ 
535+ 
536+ 
537+## 6 FAQS
538+ 
539+1、第一次安装平台时,需要设置告警接口,IP为平台IP,端口后为后端的端口号
540+ 
541+![31](doc/31.png)
542+ 
543+![32](doc/32.png)
Aplugins/alert-center/doc/1.png+0-0
Aplugins/alert-center/doc/10.png+0-0
Aplugins/alert-center/doc/11.png+0-0
Aplugins/alert-center/doc/12.png+0-0
Aplugins/alert-center/doc/13.png+0-0
Aplugins/alert-center/doc/14.png+0-0
Aplugins/alert-center/doc/15.png+0-0
Aplugins/alert-center/doc/16.png+0-0
Aplugins/alert-center/doc/17.png+0-0
Aplugins/alert-center/doc/18.png+0-0
Aplugins/alert-center/doc/19.png+0-0
Aplugins/alert-center/doc/2.png+0-0
Aplugins/alert-center/doc/20.png+0-0
Aplugins/alert-center/doc/21.png+0-0
Aplugins/alert-center/doc/22.png+0-0
Aplugins/alert-center/doc/23.png+0-0
Aplugins/alert-center/doc/24.png+0-0
Aplugins/alert-center/doc/25.png+0-0
Aplugins/alert-center/doc/26.png+0-0
Aplugins/alert-center/doc/27.png+0-0
Aplugins/alert-center/doc/28.png+0-0
Aplugins/alert-center/doc/29.png+0-0
Aplugins/alert-center/doc/3.png+0-0
Aplugins/alert-center/doc/30.png+0-0
Aplugins/alert-center/doc/31.png+0-0
Aplugins/alert-center/doc/32.png+0-0
Aplugins/alert-center/doc/4.png+0-0
Aplugins/alert-center/doc/5.png+0-0
Aplugins/alert-center/doc/6.png+0-0
Aplugins/alert-center/doc/7.png+0-0
Aplugins/alert-center/doc/8.png+0-0
Aplugins/alert-center/doc/9.png+0-0
Aplugins/alert-center/pom.xml+358-0
@@ -0,0 +1,358 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<project xmlns="http://maven.apache.org/POM/4.0.0"
3+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+ <modelVersion>4.0.0</modelVersion>
6+ 
7+ <groupId>org.opengauss</groupId>
8+ <artifactId>alert-center</artifactId>
9+ <version>5.0.0</version>
10+ <packaging>jar</packaging>
11+ 
12+ <properties>
13+ 
14+ <!--config-->
15+ <plugin-id>alert-center</plugin-id>
16+ <npm.run.script>buildNoTest</npm.run.script>
17+ <web.build.skip>false</web.build.skip>
18+ <web.clean.skip>false</web.clean.skip>
19+ 
20+ <java.version>11</java.version>
21+ <maven.compiler.source>11</maven.compiler.source>
22+ <maven.compiler.target>11</maven.compiler.target>
23+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
24+ 
25+ <!-- dependency version -->
26+ <spring-brick.version>3.1.0</spring-brick.version>
27+ <junit.version>4.13</junit.version>
28+ <mybatis-plus.version>3.5.2</mybatis-plus.version>
29+ <fastjson.version>1.2.78</fastjson.version>
30+ <lombok.version>1.18.24</lombok.version>
31+ <hutool.version>5.0.3</hutool.version>
32+ </properties>
33+ 
34+ <dependencies>
35+ <!-- same as framework -->
36+ <dependency>
37+ <groupId>org.springframework.boot</groupId>
38+ <artifactId>spring-boot-starter</artifactId>
39+ </dependency>
40+ <dependency>
41+ <groupId>org.opengauss</groupId>
42+ <artifactId>visualtool-framework</artifactId>
43+ <version>5.0.0</version>
44+ <scope>provided</scope>
45+ <exclusions>
46+ <!--druid-spring-boot-starter removed-->
47+ <exclusion>
48+ <groupId>org.springframework.boot</groupId>
49+ <artifactId>spring-boot-starter-data-redis</artifactId>
50+ </exclusion>
51+ </exclusions>
52+ </dependency>
53+ <dependency>
54+ <groupId>com.sun.mail</groupId>
55+ <artifactId>jakarta.mail</artifactId>
56+ <version>2.0.1</version>
57+ </dependency>
58+ <dependency>
59+ <groupId>jakarta.activation</groupId>
60+ <artifactId>jakarta.activation-api</artifactId>
61+ <version>2.0.1</version>
62+ </dependency>
63+<!-- <dependency>-->
64+<!-- <groupId>org.springframework.boot</groupId>-->
65+<!-- <artifactId>spring-boot-starter-mail</artifactId>-->
66+<!-- <exclusions>-->
67+<!-- <exclusion>-->
68+<!-- <groupId>com.sun.activation</groupId>-->
69+<!-- <artifactId>jakarta.activation</artifactId>-->
70+<!-- </exclusion>-->
71+<!-- </exclusions>-->
72+<!-- </dependency>-->
73+ 
74+ <dependency>
75+ <groupId>org.springframework.boot</groupId>
76+ <artifactId>spring-boot-starter-validation</artifactId>
77+ </dependency>
78+ <dependency>
79+ <groupId>com.gitee.starblues</groupId>
80+ <artifactId>spring-brick-bootstrap</artifactId>
81+ <version>${spring-brick.version}</version>
82+ </dependency>
83+ 
84+ <dependency>
85+ <groupId>com.gitee.starblues</groupId>
86+ <artifactId>spring-brick-maven-packager</artifactId>
87+ <version>${spring-brick.version}</version>
88+ </dependency>
89+ <dependency>
90+ <groupId>junit</groupId>
91+ <artifactId>junit</artifactId>
92+ <scope>test</scope>
93+ </dependency>
94+ 
95+ <dependency>
96+ <groupId>org.projectlombok</groupId>
97+ <artifactId>lombok</artifactId>
98+ <scope>provided</scope>
99+ </dependency>
100+ <!-- same as framework -->
101+ 
102+ <!-- common -->
103+ <dependency>
104+ <groupId>org.springframework.boot</groupId>
105+ <artifactId>spring-boot-starter-test</artifactId>
106+ <scope>test</scope>
107+ </dependency>
108+ <dependency>
109+ <groupId>com.alibaba</groupId>
110+ <artifactId>fastjson</artifactId>
111+ <version>${fastjson.version}</version>
112+ </dependency>
113+ <dependency>
114+ <groupId>cn.hutool</groupId>
115+ <artifactId>hutool-all</artifactId>
116+ <version>${hutool.version}</version>
117+ </dependency>
118+ <!-- common -->
119+ 
120+ <!-- data -->
121+ <dependency>
122+ <groupId>com.baomidou</groupId>
123+ <artifactId>mybatis-plus-boot-starter</artifactId>
124+ <version>${mybatis-plus.version}</version>
125+ </dependency>
126+ <dependency>
127+ <groupId>com.alibaba</groupId>
128+ <artifactId>druid-spring-boot-starter</artifactId>
129+ <version>1.2.8</version>
130+ </dependency>
131+ <dependency>
132+ <groupId>com.baomidou</groupId>
133+ <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
134+ <version>${mybatis-plus.version}</version>
135+ </dependency>
136+ <dependency>
137+ <groupId>org.postgresql</groupId>
138+ <artifactId>postgresql</artifactId>
139+ <version>42.2.24</version>
140+ </dependency>
141+ <dependency>
142+ <groupId>org.opengauss</groupId>
143+ <artifactId>opengauss-jdbc</artifactId>
144+ <version>3.0.0</version>
145+ </dependency>
146+ <dependency>
147+ <groupId>com.jcraft</groupId>
148+ <artifactId>jsch</artifactId>
149+ <version>0.1.55</version>
150+ </dependency>
151+ <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
152+ <dependency>
153+ <groupId>org.xerial</groupId>
154+ <artifactId>sqlite-jdbc</artifactId>
155+ <version>3.39.3.0</version>
156+ </dependency>
157+ <!-- https://mvnrepository.com/artifact/org.apache.sshd/sshd-scp -->
158+ <dependency>
159+ <groupId>org.apache.sshd</groupId>
160+ <artifactId>sshd-sftp</artifactId>
161+ <version>2.9.2</version>
162+ </dependency>
163+ <dependency>
164+ <groupId>org.hibernate.validator</groupId>
165+ <artifactId>hibernate-validator</artifactId>
166+ </dependency>
167+ </dependencies>
168+ 
169+ <dependencyManagement>
170+ <!-- Compiler properties -->
171+ <dependencies>
172+ <dependency>
173+ <groupId>org.springframework.boot</groupId>
174+ <artifactId>spring-boot-dependencies</artifactId>
175+ <version>2.5.6</version>
176+ <type>pom</type>
177+ <scope>import</scope>
178+ </dependency>
179+ </dependencies>
180+ </dependencyManagement>
181+ 
182+ <repositories>
183+ <repository>
184+ <id>gitee-eb-repo</id>
185+ <name>The Maven Repository on Gitee</name>
186+ <url>https://gitee.com/agile-adept-team/spring-brick-eb/tree/master/maven-repo</url>
187+ </repository>
188+ </repositories>
189+ 
190+ <profiles>
191+ <profile>
192+ <id>dev</id>
193+ <properties>
194+ <profiles.active>dev</profiles.active>
195+ <swagger.scope>compile</swagger.scope>
196+ </properties>
197+ <activation>
198+ <activeByDefault>true</activeByDefault>
199+ </activation>
200+ </profile>
201+ <profile>
202+ <id>prod</id>
203+ <properties>
204+ <profiles.active>prod</profiles.active>
205+ <swagger.scope>provided</swagger.scope>
206+ </properties>
207+ </profile>
208+ </profiles>
209+ 
210+ <build>
211+ <pluginManagement>
212+ <plugins>
213+ <plugin>
214+ <groupId>com.gitee.starblues</groupId>
215+ <artifactId>spring-brick-maven-packager</artifactId>
216+ <version>${spring-brick.version}</version>
217+ </plugin>
218+ </plugins>
219+ </pluginManagement>
220+ <plugins>
221+ <plugin>
222+ <groupId>com.gitee.starblues</groupId>
223+ <artifactId>spring-brick-maven-packager</artifactId>
224+ <configuration>
225+ <mode>prod</mode>
226+ <pluginInfo>
227+ <id>${plugin-id}</id>
228+ <bootstrapClass>org.opengauss.plugin.alertcenter.ObservabilityPluginApplication</bootstrapClass>
229+ <version>${project.version}</version>
230+ <provider>ncti-gba</provider>
231+ <description>Intelligent OPS</description>
232+ </pluginInfo>
233+ </configuration>
234+ <executions>
235+ <execution>
236+ <goals>
237+ <goal>repackage</goal>
238+ </goals>
239+ </execution>
240+ </executions>
241+ </plugin>
242+ <plugin>
243+ <groupId>org.apache.maven.plugins</groupId>
244+ <artifactId>maven-compiler-plugin</artifactId>
245+ <version>3.1</version>
246+ <configuration>
247+ <source>${java.version}</source>
248+ <target>${java.version}</target>
249+ <encoding>${project.build.sourceEncoding}</encoding>
250+ </configuration>
251+ </plugin>
252+ <plugin>
253+ <groupId>org.apache.maven.plugins</groupId>
254+ <artifactId>maven-surefire-plugin</artifactId>
255+ <configuration>
256+ <skip>true</skip>
257+ </configuration>
258+ </plugin>
259+ 
260+ <plugin>
261+ <groupId>org.apache.maven.plugins</groupId>
262+ <artifactId>maven-clean-plugin</artifactId>
263+ <version>3.2.0</version>
264+ <configuration>
265+ <filesets>
266+ <fileset>
267+ <directory>web-ui/dist</directory>
268+ <includes>
269+ <include>**/*</include>
270+ </includes>
271+ <followSymlinks>false</followSymlinks>
272+ </fileset>
273+ <fileset>
274+ <directory>${basedir}/src/main/resources/resources</directory>
275+ <includes>
276+ <include>**/*</include>
277+ </includes>
278+ <followSymlinks>false</followSymlinks>
279+ </fileset>
280+ </filesets>
281+ <skip>${web.clean.skip}</skip>
282+ <failOnError>true</failOnError>
283+ </configuration>
284+ </plugin>
285+ 
286+ <plugin>
287+ <groupId>org.codehaus.mojo</groupId>
288+ <artifactId>exec-maven-plugin</artifactId>
289+ <version>1.6.0</version>
290+ <configuration>
291+ <skip>false</skip>
292+ </configuration>
293+ <executions>
294+ <execution>
295+ <id>exec-npm-install</id>
296+ <phase>generate-resources</phase>
297+ <goals>
298+ <goal>exec</goal>
299+ </goals>
300+ <configuration>
301+ <executable>npm</executable>
302+ <arguments>
303+ <argument>install</argument>
304+ </arguments>
305+ <workingDirectory>web-ui</workingDirectory>
306+ </configuration>
307+ </execution>
308+ 
309+ <execution>
310+ <id>exec-npm-run-build</id>
311+ <phase>generate-resources</phase>
312+ <goals>
313+ <goal>exec</goal>
314+ </goals>
315+ <configuration>
316+ <skip>${web.build.skip}</skip>
317+ <executable>npm</executable>
318+ <arguments>
319+ <argument>run</argument>
320+ <argument>${npm.run.script}</argument>
321+ </arguments>
322+ <workingDirectory>web-ui</workingDirectory>
323+ </configuration>
324+ </execution>
325+ </executions>
326+ </plugin>
327+ 
328+ <plugin>
329+ <groupId>org.apache.maven.plugins</groupId>
330+ <artifactId>maven-resources-plugin</artifactId>
331+ <configuration>
332+ <delimiters>@</delimiters>
333+ <useDefaultDelimiters>false</useDefaultDelimiters>
334+ </configuration>
335+ <version>2.5</version>
336+ <executions>
337+ <execution>
338+ <id>copy-web</id>
339+ <phase>generate-resources</phase>
340+ <goals>
341+ <goal>copy-resources</goal>
342+ </goals>
343+ <configuration>
344+ <outputDirectory>${basedir}/src/main/resources</outputDirectory>
345+ <resources>
346+ <resource>
347+ <directory>web-ui/dist</directory>
348+ <targetPath>resources</targetPath>
349+ </resource>
350+ </resources>
351+ </configuration>
352+ </execution>
353+ </executions>
354+ </plugin>
355+ </plugins>
356+ </build>
357+ 
358+</project>
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/ObservabilityPluginApplication.java+31-0
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter;
6+ 
7+import com.gitee.starblues.bootstrap.SpringPluginBootstrap;
8+import org.springframework.boot.autoconfigure.SpringBootApplication;
9+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
10+import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
11+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
12+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
13+import org.springframework.scheduling.annotation.EnableScheduling;
14+ 
15+/**
16+ * @Author wuyuebin
17+ * @Date 2023/4/7 16:24
18+ * @Description
19+ */
20+@SpringBootApplication(exclude = {
21+ DataSourceAutoConfiguration.class,
22+ HibernateJpaAutoConfiguration.class,
23+ RedisAutoConfiguration.class,
24+ RedisRepositoriesAutoConfiguration.class
25+})
26+@EnableScheduling
27+public class ObservabilityPluginApplication extends SpringPluginBootstrap {
28+ public static void main(String[] args) {
29+ new ObservabilityPluginApplication().run(args);
30+ }
31+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/ObservabilityPluginBootApplication.java+49-0
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter;
6+ 
7+import org.mybatis.spring.annotation.MapperScan;
8+import org.opengauss.admin.system.plugin.facade.HostFacade;
9+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
10+import org.springframework.boot.SpringApplication;
11+import org.springframework.boot.autoconfigure.SpringBootApplication;
12+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
13+import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
14+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
15+import org.springframework.context.annotation.ComponentScan;
16+import org.springframework.context.annotation.FilterType;
17+import org.springframework.context.annotation.Profile;
18+import org.springframework.scheduling.annotation.EnableScheduling;
19+ 
20+/**
21+ * @Author wuyuebin
22+ * @Date 2023/4/7 16:36
23+ * @Description
24+ */
25+@Profile({"dev", "gba"})
26+@EnableScheduling
27+@SpringBootApplication(exclude = {
28+ HibernateJpaAutoConfiguration.class,
29+ RedisAutoConfiguration.class,
30+ RedisRepositoriesAutoConfiguration.class})
31+@ComponentScan(basePackages = {"org.opengauss.plugin.alertcenter",
32+ "org.opengauss.admin.system.service.ops",
33+ "org.opengauss.admin.common.utils.ops",
34+ "org.opengauss.admin.common.core.handler.ops.cache"},
35+ basePackageClasses = {HostFacade.class,
36+ HostUserFacade.class},
37+ excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes =
38+ {org.opengauss.admin.system.plugin.facade.MenuFacade.class,
39+ org.opengauss.admin.system.plugin.facade.MonitorToolsFacade.class,
40+ org.opengauss.admin.system.plugin.facade.PluginFacade.class,
41+ org.opengauss.admin.system.plugin.facade.SysSettingFacade.class,
42+ org.opengauss.admin.system.plugin.facade.TaskFacade.class,
43+ org.opengauss.admin.system.plugin.facade.WsFacade.class})})
44+@MapperScan({"org.opengauss.plugin.alertcenter.mapper", "org.opengauss.admin.system.mapper"})
45+public class ObservabilityPluginBootApplication {
46+ public static void main(String[] args) {
47+ SpringApplication.run(ObservabilityPluginBootApplication.class, args);
48+ }
49+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/AlertApplicationRunner.java+29-0
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config;
6+ 
7+import lombok.extern.slf4j.Slf4j;
8+import org.opengauss.plugin.alertcenter.service.PrometheusService;
9+import org.springframework.beans.factory.annotation.Autowired;
10+import org.springframework.boot.ApplicationArguments;
11+import org.springframework.boot.ApplicationRunner;
12+import org.springframework.stereotype.Component;
13+ 
14+/**
15+ * @author wuyuebin
16+ * @date 2023/4/27 16:17
17+ * @description 初始化操作
18+ */
19+@Component
20+@Slf4j
21+public class AlertApplicationRunner implements ApplicationRunner {
22+ @Autowired
23+ private PrometheusService prometheusService;
24+ 
25+ @Override
26+ public void run(ApplicationArguments args) throws Exception {
27+ prometheusService.initPrometheusConfig();
28+ }
29+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/DataSourceConfig.java+43-0
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config;
6+ 
7+import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
8+import com.gitee.starblues.bootstrap.PluginContextHolder;
9+import com.gitee.starblues.spring.environment.EnvironmentProvider;
10+import org.springframework.boot.context.properties.ConfigurationProperties;
11+import org.springframework.boot.jdbc.DataSourceBuilder;
12+import org.springframework.context.annotation.Bean;
13+import org.springframework.context.annotation.Configuration;
14+import org.springframework.context.annotation.Profile;
15+ 
16+import javax.sql.DataSource;
17+ 
18+/**
19+ * @Author wuyuebin
20+ * @Date 2023/4/13 09:39
21+ * @Description
22+ */
23+@Configuration
24+public class DataSourceConfig {
25+ @Bean
26+ @ConfigurationProperties(prefix = "spring.datasource")
27+ @Profile("!dev")
28+ public DataSource dataSource() {
29+ EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider();
30+ // read config from dataKit platform
31+ String url = environmentProvider.getString("spring.datasource.url");
32+ String username = environmentProvider.getString("spring.datasource.username");
33+ String password = environmentProvider.getString("spring.datasource.password");
34+ String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name");
35+ 
36+ DataSource primary = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username)
37+ .password(password).build();
38+ DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
39+ dataSource.addDataSource("primary", primary);
40+ dataSource.setPrimary("primary");
41+ return dataSource;
42+ }
43+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/MybatisPlusConfig.java+50-0
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config;
6+ 
7+import com.baomidou.mybatisplus.annotation.DbType;
8+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
9+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
10+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
11+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
12+import org.springframework.context.annotation.Bean;
13+import org.springframework.context.annotation.Configuration;
14+import org.springframework.transaction.annotation.EnableTransactionManagement;
15+ 
16+/**
17+ * Description: mybatis-plus configuration
18+ *
19+ * @author xielibo
20+ * @version 1.0
21+ * @date: 2021/9/28 5:54 PM
22+ * @since JDK 1.8
23+ */
24+@EnableTransactionManagement(proxyTargetClass = true)
25+@Configuration
26+public class MybatisPlusConfig {
27+ @Bean
28+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
29+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
30+ interceptor.addInnerInterceptor(paginationInnerInterceptor());
31+ interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
32+ interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
33+ return interceptor;
34+ }
35+ 
36+ public PaginationInnerInterceptor paginationInnerInterceptor() {
37+ PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
38+ paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL);
39+ paginationInnerInterceptor.setMaxLimit(-1L);
40+ return paginationInnerInterceptor;
41+ }
42+ 
43+ public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
44+ return new OptimisticLockerInnerInterceptor();
45+ }
46+ 
47+ public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
48+ return new BlockAttackInnerInterceptor();
49+ }
50+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/PluginExtensionInfoConfig.java+36-0
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config;
6+ 
7+import com.gitee.starblues.core.PluginExtensionInfo;
8+ 
9+import java.util.HashMap;
10+import java.util.Map;
11+ 
12+/**
13+ * @author wuyuebin
14+ * @date 2023/4/13 11:37
15+ * @description
16+ */
17+public class PluginExtensionInfoConfig implements PluginExtensionInfo {
18+ @Override
19+ public Map<String, Object> extensionInfo() {
20+ Map<String, Object> map = new HashMap<>();
21+ map.put("logo",
22+ "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdml"
23+ + "ld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tl"
24+ + "LXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIG"
25+ + "NsYXNzPSJmZWF0aGVyIGZlYXRoZXItZGF0YWJhc2UiPjxlbGxpcHNlIGN4PSIxMiIgY3k9IjUiIHJ4"
26+ + "PSI5IiByeT0iMyI+PC9lbGxpcHNlPjxwYXRoIGQ9Ik0yMSAxMmMwIDEuNjYtNCAzLTkgM3MtOS0xLj"
27+ + "M0LTktMyI+PC9wYXRoPjxwYXRoIGQ9Ik0zIDV2MTRjMCAxLjY2IDQgMyA5IDNzOS0xLjM0IDktM1Y1"
28+ + "Ij48L3BhdGg+PC9zdmc+");
29+ map.put("theme", "light");
30+ map.put("pluginType", 1);
31+ map.put("isNeedConfigured", 0);
32+ map.put("configAttrs", "[{\"attrCode\":\"esHost\",\"attrLabel\":\"ES服务器\"},"
33+ + "{\"attrCode\":\"esPort\",\"attrLabel\":\"端口\"}]");
34+ return map;
35+ }
36+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/annotation/AlertContentParam.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config.annotation;
6+ 
7+import java.lang.annotation.ElementType;
8+import java.lang.annotation.Retention;
9+import java.lang.annotation.RetentionPolicy;
10+import java.lang.annotation.Target;
11+ 
12+/**
13+ * @author wuyuebin
14+ * @date 2023/6/16 14:53
15+ * @description 常驻参数说明
16+ */
17+@Target(ElementType.FIELD)
18+@Retention(RetentionPolicy.RUNTIME)
19+public @interface AlertContentParam {
20+ String name() default "";
21+ 
22+ String preVal() default "";
23+ 
24+ boolean isI18nPreVal() default false;
25+ 
26+ String[] group() default {};
27+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/properties/AlertProperty.java+31-0
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config.properties;
6+ 
7+import lombok.Data;
8+import org.springframework.boot.context.properties.ConfigurationProperties;
9+import org.springframework.boot.context.properties.NestedConfigurationProperty;
10+import org.springframework.stereotype.Component;
11+ 
12+import java.util.List;
13+ 
14+/**
15+ * @author wuyuebin
16+ * @date 2023/4/27 15:21
17+ * @description
18+ */
19+@ConfigurationProperties(prefix = "alert")
20+@Component
21+@Data
22+public class AlertProperty {
23+ private String ruleFilePrefix;
24+ 
25+ private String ruleFileSuffix;
26+ @NestedConfigurationProperty
27+ private AlertmanagerProperty alertmanager;
28+ 
29+ @NestedConfigurationProperty
30+ private List<RuleItemProperty> ruleItems;
31+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/properties/AlertmanagerProperty.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config.properties;
6+ 
7+import lombok.Data;
8+ 
9+/**
10+ * @author wuyuebin
11+ * @date 2023/4/27 15:35
12+ * @description
13+ */
14+@Data
15+public class AlertmanagerProperty {
16+ private String pathPrefix;
17+ private String apiVersion;
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/properties/RuleItemExpProperty.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config.properties;
6+ 
7+import lombok.Data;
8+ 
9+/**
10+ * @author wuyuebin
11+ * @date 2023/5/29 10:51
12+ * @description
13+ */
14+@Data
15+public class RuleItemExpProperty {
16+ private String type;
17+ private String exp;
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/properties/RuleItemParamProperty.java+19-0
@@ -0,0 +1,19 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config.properties;
6+ 
7+import lombok.Data;
8+ 
9+/**
10+ * @author wuyuebin
11+ * @date 2023/5/29 10:59
12+ * @description
13+ */
14+@Data
15+public class RuleItemParamProperty {
16+ private String paramName;
17+ private String paramValue;
18+ private Integer paramOrder;
19+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/config/properties/RuleItemProperty.java+25-0
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.config.properties;
6+ 
7+import lombok.Data;
8+import org.springframework.boot.context.properties.NestedConfigurationProperty;
9+ 
10+import java.util.List;
11+ 
12+/**
13+ * @author wuyuebin
14+ * @date 2023/5/29 10:46
15+ * @description
16+ */
17+@Data
18+public class RuleItemProperty {
19+ private String name;
20+ private String unit;
21+ @NestedConfigurationProperty
22+ private List<RuleItemExpProperty> ruleExps;
23+ @NestedConfigurationProperty
24+ private List<RuleItemParamProperty> params;
25+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/constant/CommonConstants.java+35-0
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.constant;
6+ 
7+/**
8+ * @author wuyuebin
9+ * @date 2023/4/27 17:49
10+ * @description
11+ */
12+public class CommonConstants {
13+ public static final String PROMETHEUS_YML = "/prometheus.yml";
14+ public static final Integer IS_DELETE = 1;
15+ public static final Integer IS_NOT_DELETE = 0;
16+ public static final Integer UNREAD_STATUS = 0;
17+ public static final Integer READ_STATUS = 1;
18+ public static final Integer FIRING_STATUS = 0;
19+ public static final Integer RECOVER_STATUS = 1;
20+ public static final String SERIOUS = "serious";
21+ public static final String WARN = "warn";
22+ public static final String INFO = "info";
23+ public static final String DELIMITER = ",";
24+ public static final Integer IS_NOT_REPEAT = 0;
25+ public static final Integer IS_REPEAT = 1;
26+ public static final Integer IS_NOT_SILENCE = 0;
27+ public static final Integer IS_SILENCE = 1;
28+ public static final String EMAIL = "email";
29+ public static final String WE_COM = "WeCom";
30+ public static final String DING_TALK = "DingTalk";
31+ public static final Integer ENABLE = 1;
32+ public static final Integer DISABLE = 0;
33+ public static final String LINE_FEED = "\n";
34+ public static final String SLASH = "/";
35+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/constant/OSType.java+28-0
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.constant;
6+ 
7+import lombok.Getter;
8+import lombok.RequiredArgsConstructor;
9+ 
10+import java.util.Arrays;
11+import java.util.List;
12+import java.util.stream.Collectors;
13+ 
14+@RequiredArgsConstructor
15+@Getter
16+public enum OSType {
17+ DEFAULT("CentOS 7.6"), KYLIN_V10("kylinv10"), OPENEULER_203LTS("openEuler 20.3LTS");
18+ private final String osType;
19+ 
20+ /**
21+ * Get the list of supported server operating systems
22+ *
23+ * @return Return to the list of server operating systems
24+ */
25+ public static List<String> getAllOSType() {
26+ return Arrays.stream(OSType.values()).map(OSType::getOsType).collect(Collectors.toList());
27+ }
28+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/AlertApiController.java+60-0
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import cn.hutool.json.JSONObject;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.plugin.alertcenter.model.api.AlertApiReq;
10+import org.opengauss.plugin.alertcenter.service.AlertApiService;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.web.bind.annotation.PostMapping;
13+import org.springframework.web.bind.annotation.RequestBody;
14+import org.springframework.web.bind.annotation.RequestMapping;
15+import org.springframework.web.bind.annotation.RestController;
16+ 
17+import javax.validation.Valid;
18+import java.util.ArrayList;
19+import java.util.List;
20+ 
21+/**
22+ * @author wuyuebin
23+ * @date 2023/4/28 08:44
24+ * @description
25+ */
26+@RestController
27+@RequestMapping("/alertCenter/api/v1")
28+public class AlertApiController {
29+ @Autowired
30+ private AlertApiService alertApiService;
31+ 
32+ /**
33+ * 这是prometheus推送数据的接口
34+ * prometheus固定推送的接口是:/%pre/api/%v/alerts
35+ * prometheus配置文件中的path_prefix(%pre)是: /alertCenter
36+ * prometheus配置文件中的api_version(%v)是: v1
37+ * <p>
38+ * appcation.yml中配置的context-path是/alertCenter/api/v1
39+ * <p>
40+ * 注:context-path的"/alertCenter""v1" 必须和prometheus配置文件中的path_prefix、api_version匹配,
41+ * 后续如果需要修改context-path,需要去修改prometheus的配置或本接口的url路径
42+ *
43+ * @param paramList 告警信息
44+ * @return 返回成功信息
45+ */
46+ @PostMapping("/alerts")
47+ public AjaxResult alerts(@RequestBody @Valid List<JSONObject> paramList) {
48+ List<AlertApiReq> alertApiReqList = new ArrayList<>();
49+ for (JSONObject jsonObject : paramList) {
50+ jsonObject.put("startsAt", jsonObject.getStr("startsAt").replace("T", " ").substring(0, 19));
51+ jsonObject.put("endsAt", jsonObject.getStr("endsAt").replace("T", " ").substring(0, 19));
52+ AlertApiReq alertApiReq = jsonObject.toBean(AlertApiReq.class);
53+ alertApiReq.setStartsAt(alertApiReq.getStartsAt().plusHours(8));
54+ alertApiReq.setEndsAt(alertApiReq.getEndsAt().plusHours(8));
55+ alertApiReqList.add(alertApiReq);
56+ }
57+ alertApiService.alerts(alertApiReqList);
58+ return AjaxResult.success();
59+ }
60+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/AlertClusterNodeConfController.java+59-0
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import org.opengauss.admin.common.core.domain.AjaxResult;
8+import org.opengauss.plugin.alertcenter.dto.AlertClusterNodeConfDto;
9+import org.opengauss.plugin.alertcenter.entity.AlertClusterNodeConf;
10+import org.opengauss.plugin.alertcenter.model.AlertClusterNodeAndTemplateReq;
11+import org.opengauss.plugin.alertcenter.model.AlertClusterNodeConfReq;
12+import org.opengauss.plugin.alertcenter.service.AlertClusterNodeConfService;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.web.bind.annotation.GetMapping;
15+import org.springframework.web.bind.annotation.PathVariable;
16+import org.springframework.web.bind.annotation.PostMapping;
17+import org.springframework.web.bind.annotation.RequestBody;
18+import org.springframework.web.bind.annotation.RequestMapping;
19+import org.springframework.web.bind.annotation.RestController;
20+ 
21+import javax.validation.Valid;
22+import java.util.List;
23+ 
24+/**
25+ * @author wuyuebin
26+ * @date 2023/4/26 15:57
27+ * @description
28+ */
29+@RestController
30+@RequestMapping("/alertCenter/api/v1/alertClusterNodeConf")
31+public class AlertClusterNodeConfController extends BaseController {
32+ @Autowired
33+ private AlertClusterNodeConfService alertClusterNodeConfService;
34+ 
35+ @GetMapping
36+ public AjaxResult getList() {
37+ List<AlertClusterNodeConfDto> list = alertClusterNodeConfService.getList();
38+ return AjaxResult.success(list);
39+ }
40+ 
41+ @GetMapping("/clusterNode/{clusterNodeId}")
42+ public AjaxResult getByClusterNodeId(@PathVariable String clusterNodeId) {
43+ AlertClusterNodeConf alertClusterNodeConf = alertClusterNodeConfService.getByClusterNodeId(clusterNodeId);
44+ return AjaxResult.success(alertClusterNodeConf);
45+ }
46+ 
47+ @PostMapping("")
48+ public AjaxResult saveClusterNodeConf(@Valid @RequestBody AlertClusterNodeConfReq alertClusterNodeConfReq) {
49+ alertClusterNodeConfService.saveClusterNodeConf(alertClusterNodeConfReq);
50+ return AjaxResult.success();
51+ }
52+ 
53+ @PostMapping("/alertTemplate")
54+ public AjaxResult saveAlertTemplateAndConfig(
55+ @Valid @RequestBody AlertClusterNodeAndTemplateReq clusterNodeAndTemplateReq) {
56+ alertClusterNodeConfService.saveAlertTemplateAndConfig(clusterNodeAndTemplateReq);
57+ return AjaxResult.success();
58+ }
59+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/AlertConfigController.java+46-0
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.plugin.alertcenter.entity.AlertConfig;
10+import org.opengauss.plugin.alertcenter.service.AlertConfigService;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.web.bind.annotation.GetMapping;
13+import org.springframework.web.bind.annotation.PostMapping;
14+import org.springframework.web.bind.annotation.RequestBody;
15+import org.springframework.web.bind.annotation.RequestMapping;
16+import org.springframework.web.bind.annotation.RestController;
17+ 
18+import javax.validation.Valid;
19+import java.util.List;
20+ 
21+/**
22+ * @author wuyuebin
23+ * @date 2023/6/9 10:47
24+ * @description
25+ */
26+@RestController
27+@RequestMapping("/alertCenter/api/v1/alertConf")
28+public class AlertConfigController {
29+ @Autowired
30+ private AlertConfigService alertConfigService;
31+ 
32+ @GetMapping
33+ public AjaxResult getAlertConf() {
34+ List<AlertConfig> list = alertConfigService.list();
35+ if (CollectionUtil.isEmpty(list)) {
36+ return AjaxResult.success(new AlertConfig());
37+ }
38+ return AjaxResult.success(list.get(0));
39+ }
40+ 
41+ @PostMapping
42+ public AjaxResult saveAlertConf(@Valid @RequestBody AlertConfig alertConfig) {
43+ alertConfigService.saveAlertConf(alertConfig);
44+ return AjaxResult.success();
45+ }
46+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/AlertRecordController.java+78-0
@@ -0,0 +1,78 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.admin.common.core.page.TableDataInfo;
10+import org.opengauss.plugin.alertcenter.dto.AlertRecordDto;
11+import org.opengauss.plugin.alertcenter.dto.AlertRelationDto;
12+import org.opengauss.plugin.alertcenter.dto.AlertStatisticsDto;
13+import org.opengauss.plugin.alertcenter.model.AlertRecordReq;
14+import org.opengauss.plugin.alertcenter.model.AlertStatisticsReq;
15+import org.opengauss.plugin.alertcenter.service.AlertRecordService;
16+import org.springframework.beans.factory.annotation.Autowired;
17+import org.springframework.web.bind.annotation.GetMapping;
18+import org.springframework.web.bind.annotation.PathVariable;
19+import org.springframework.web.bind.annotation.PostMapping;
20+import org.springframework.web.bind.annotation.RequestMapping;
21+import org.springframework.web.bind.annotation.RequestParam;
22+import org.springframework.web.bind.annotation.RestController;
23+ 
24+import java.util.List;
25+ 
26+/**
27+ * @author wuyuebin
28+ * @date 2023/4/14 15:03
29+ * @description
30+ */
31+@RestController
32+@RequestMapping("/alertCenter/api/v1/alertRecord")
33+public class AlertRecordController extends BaseController {
34+ @Autowired
35+ private AlertRecordService alertRecordService;
36+ 
37+ @GetMapping("")
38+ public TableDataInfo alertRecordListPage(AlertRecordReq alertRecordReq) {
39+ Page<AlertRecordDto> page = alertRecordService.getListPage(alertRecordReq, startPage());
40+ return getDataTable(page);
41+ }
42+ 
43+ @GetMapping("/statistics")
44+ public AjaxResult alertRecordStatistics(AlertStatisticsReq alertStatisticsReq) {
45+ AlertStatisticsDto statisticsDto = alertRecordService.alertRecordStatistics(alertStatisticsReq);
46+ return AjaxResult.success(statisticsDto);
47+ }
48+ 
49+ @GetMapping("/{id}")
50+ public AjaxResult getById(@PathVariable Long id) {
51+ AlertRecordDto alertRecordDto = alertRecordService.getById(id);
52+ return AjaxResult.success(alertRecordDto);
53+ }
54+ 
55+ @PostMapping("/markAsUnread")
56+ public AjaxResult markAsUnread(@RequestParam String ids) {
57+ String result = alertRecordService.markAsUnread(ids);
58+ return AjaxResult.success(result);
59+ }
60+ 
61+ /**
62+ * 处理告警信息,将未读状态改为已读
63+ *
64+ * @param ids 多个,逗号分隔
65+ * @return success
66+ */
67+ @PostMapping("/markAsRead")
68+ public AjaxResult markAsRead(@RequestParam String ids) {
69+ String result = alertRecordService.markAsRead(ids);
70+ return AjaxResult.success(result);
71+ }
72+ 
73+ @GetMapping("/{id}/relation")
74+ public AjaxResult getRelationData(@PathVariable Long id) {
75+ List<AlertRelationDto> relationData = alertRecordService.getRelationData(id);
76+ return AjaxResult.success(relationData);
77+ }
78+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/AlertRuleController.java+51-0
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.admin.common.core.page.TableDataInfo;
10+import org.opengauss.plugin.alertcenter.dto.AlertRuleDto;
11+import org.opengauss.plugin.alertcenter.model.RuleReq;
12+import org.opengauss.plugin.alertcenter.service.AlertRuleService;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.web.bind.annotation.GetMapping;
15+import org.springframework.web.bind.annotation.PathVariable;
16+import org.springframework.web.bind.annotation.RequestMapping;
17+import org.springframework.web.bind.annotation.RestController;
18+ 
19+/**
20+ * @author wuyuebin
21+ * @date 2023/4/13 17:39
22+ * @description
23+ */
24+@RestController
25+@RequestMapping("/alertCenter/api/v1/alertRule")
26+public class AlertRuleController extends BaseController {
27+ @Autowired
28+ private AlertRuleService alertRuleService;
29+ 
30+ @GetMapping("")
31+ public TableDataInfo getRuleListPage(RuleReq ruleReq) {
32+ Page<AlertRuleDto> ruleIPage = alertRuleService.getRulePage(ruleReq, startPage());
33+ return getDataTable(ruleIPage);
34+ }
35+ 
36+ @GetMapping("/ruleList")
37+ public AjaxResult getRuleList() {
38+ return AjaxResult.success(alertRuleService.getRuleList());
39+ }
40+ 
41+ 
42+ @GetMapping("/{ruleId}")
43+ public AjaxResult getRuleById(@PathVariable Long ruleId) {
44+ return AjaxResult.success(alertRuleService.getRuleById(ruleId));
45+ }
46+ 
47+ @GetMapping("/ruleItem/properties")
48+ public AjaxResult getRuleItemProperties() {
49+ return AjaxResult.success(alertRuleService.getRuleItemProperties());
50+ }
51+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/AlertTemplateController.java+96-0
@@ -0,0 +1,96 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.admin.common.core.page.TableDataInfo;
10+import org.opengauss.plugin.alertcenter.dto.AlertTemplateDto;
11+import org.opengauss.plugin.alertcenter.dto.AlertTemplateRuleDto;
12+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
13+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
14+import org.opengauss.plugin.alertcenter.model.AlertTemplateReq;
15+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleService;
16+import org.opengauss.plugin.alertcenter.service.AlertTemplateService;
17+import org.springframework.beans.factory.annotation.Autowired;
18+import org.springframework.web.bind.annotation.DeleteMapping;
19+import org.springframework.web.bind.annotation.GetMapping;
20+import org.springframework.web.bind.annotation.PathVariable;
21+import org.springframework.web.bind.annotation.PostMapping;
22+import org.springframework.web.bind.annotation.RequestBody;
23+import org.springframework.web.bind.annotation.RequestMapping;
24+import org.springframework.web.bind.annotation.RequestParam;
25+import org.springframework.web.bind.annotation.RestController;
26+ 
27+import javax.validation.Valid;
28+import java.util.List;
29+ 
30+/**
31+ * @author wuyuebin
32+ * @date 2023/4/14 17:03
33+ * @description 告警模板相关
34+ */
35+@RestController
36+@RequestMapping("/alertCenter/api/v1/alertTemplate")
37+public class AlertTemplateController extends BaseController {
38+ @Autowired
39+ private AlertTemplateService templateService;
40+ @Autowired
41+ private AlertTemplateRuleService templateRuleService;
42+ 
43+ @GetMapping("")
44+ public TableDataInfo getTemplatePage(String templateName) {
45+ Page<AlertTemplate> page = templateService.getTemplatePage(templateName, startPage());
46+ return getDataTable(page);
47+ }
48+ 
49+ @GetMapping("/list")
50+ public AjaxResult getTemplateList() {
51+ List<AlertTemplate> list = templateService.getTemplateList();
52+ return AjaxResult.success(list);
53+ }
54+ 
55+ @GetMapping("/{id}")
56+ public AjaxResult getTemplateDto(@PathVariable Long id) {
57+ AlertTemplateDto templateDto = templateService.getTemplate(id);
58+ return AjaxResult.success(templateDto);
59+ }
60+ 
61+ @GetMapping("/{id}/rule")
62+ public TableDataInfo getTemplateRulePage(@PathVariable Long id, String ruleName) {
63+ Page<AlertTemplateRuleDto> page = templateService.getTemplateRulePage(id, ruleName, startPage());
64+ return getDataTable(page);
65+ }
66+ 
67+ @GetMapping("/{id}/rule/list")
68+ public AjaxResult getTemplateRuleListById(@PathVariable Long id) {
69+ List<AlertTemplateRuleDto> list = templateService.getTemplateRuleListById(id);
70+ return AjaxResult.success(list);
71+ }
72+ 
73+ @GetMapping("/ruleList/{templateRuleId}")
74+ public AjaxResult getTemplateRule(@PathVariable Long templateRuleId) {
75+ AlertTemplateRule alertTemplateRule = templateRuleService.getTemplateRule(templateRuleId);
76+ return AjaxResult.success(alertTemplateRule);
77+ }
78+ 
79+ @PostMapping("")
80+ public AjaxResult saveTemplate(@RequestBody @Valid AlertTemplateReq templateReq) {
81+ AlertTemplate alertTemplate = templateService.saveTemplate(templateReq);
82+ return AjaxResult.success(alertTemplate);
83+ }
84+ 
85+ @DeleteMapping
86+ public AjaxResult delTemplate(@RequestParam Long id) {
87+ templateService.delTemplate(id);
88+ return AjaxResult.success();
89+ }
90+ 
91+ @PostMapping("/templateRule")
92+ public AjaxResult saveTemplateRule(@RequestBody @Valid AlertTemplateRule alertTemplateRule) {
93+ AlertTemplateRuleDto templateRuleDto = templateRuleService.saveTemplateRule(alertTemplateRule);
94+ return AjaxResult.success(templateRuleDto);
95+ }
96+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/BaseController.java+41-0
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import com.baomidou.mybatisplus.core.metadata.IPage;
8+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
9+import org.opengauss.admin.common.core.page.TableDataInfo;
10+import org.opengauss.admin.common.enums.ResponseCode;
11+import org.opengauss.admin.common.utils.ServletUtils;
12+import org.opengauss.admin.common.utils.StringUtils;
13+ 
14+/**
15+ * @author wuyuebin
16+ * @date 2023/5/29 09:14
17+ * @description
18+ */
19+public class BaseController {
20+ protected Page startPage() {
21+ Page page = new Page();
22+ Integer pageNum = ServletUtils.getParameterToInt("pageNum");
23+ Integer pageSize = ServletUtils.getParameterToInt("pageSize");
24+ if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) {
25+ page.setCurrent((long) pageNum);
26+ page.setSize((long) pageSize);
27+ page.setOptimizeCountSql(false);
28+ page.setMaxLimit(500L);
29+ }
30+ return page;
31+ }
32+ 
33+ protected TableDataInfo getDataTable(IPage<?> page) {
34+ TableDataInfo rspData = new TableDataInfo();
35+ rspData.setCode(ResponseCode.SUCCESS.code());
36+ rspData.setMsg(ResponseCode.SUCCESS.msg());
37+ rspData.setRows(page.getRecords());
38+ rspData.setTotal(page.getTotal());
39+ return rspData;
40+ }
41+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/EnvironmentController.java+49-0
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import org.opengauss.admin.common.core.domain.AjaxResult;
8+import org.opengauss.plugin.alertcenter.service.EnvironmentService;
9+import org.springframework.beans.factory.annotation.Autowired;
10+import org.springframework.web.bind.annotation.GetMapping;
11+import org.springframework.web.bind.annotation.RequestMapping;
12+import org.springframework.web.bind.annotation.RequestParam;
13+import org.springframework.web.bind.annotation.RestController;
14+import java.util.List;
15+import java.util.Map;
16+ 
17+/**
18+ * @author wuyuebin
19+ * @date 2023/5/29 17:36
20+ * @description
21+ */
22+@RestController
23+@RequestMapping("/alertCenter/api/v1/environment")
24+public class EnvironmentController {
25+ @Autowired
26+ private EnvironmentService environmentService;
27+ 
28+ @GetMapping(value = "/cluster")
29+ public AjaxResult cluster() {
30+ List cluster = environmentService.cluster();
31+ return AjaxResult.success(cluster);
32+ }
33+ 
34+ @GetMapping("/checkPrometheus")
35+ public AjaxResult checkPrometheus() {
36+ environmentService.checkPrometheus();
37+ return AjaxResult.success();
38+ }
39+ 
40+ /**
41+ * @param type 分 alert和 notify,alert表示查询告警内容常驻参数,notify表示通知内容的常驻参数
42+ * @return 返回常驻参数
43+ */
44+ @GetMapping("/alertContentParam")
45+ public AjaxResult getAlertContentParam(@RequestParam String type) {
46+ Map<String, Map<String, String>> map = environmentService.getAlertContentParam(type);
47+ return AjaxResult.success(map);
48+ }
49+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/NotifyConfigController.java+48-0
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import org.opengauss.admin.common.core.domain.AjaxResult;
8+import org.opengauss.plugin.alertcenter.entity.NotifyConfig;
9+import org.opengauss.plugin.alertcenter.model.NotifyConfigReq;
10+import org.opengauss.plugin.alertcenter.service.NotifyConfigService;
11+import org.springframework.beans.factory.annotation.Autowired;
12+import org.springframework.web.bind.annotation.GetMapping;
13+import org.springframework.web.bind.annotation.PostMapping;
14+import org.springframework.web.bind.annotation.RequestBody;
15+import org.springframework.web.bind.annotation.RequestMapping;
16+import org.springframework.web.bind.annotation.RestController;
17+ 
18+import java.util.List;
19+ 
20+/**
21+ * @author wuyuebin
22+ * @date 2023/6/1 01:48
23+ * @description
24+ */
25+@RestController
26+@RequestMapping("/alertCenter/api/v1/notifyConfig")
27+public class NotifyConfigController {
28+ @Autowired
29+ private NotifyConfigService notifyConfigService;
30+ 
31+ @GetMapping("/list")
32+ public AjaxResult getAllList() {
33+ List<NotifyConfig> list = notifyConfigService.getAllList();
34+ return AjaxResult.success(list);
35+ }
36+ 
37+ @PostMapping("/list")
38+ public AjaxResult saveList(@RequestBody List<NotifyConfig> list) {
39+ notifyConfigService.saveList(list);
40+ return AjaxResult.success();
41+ }
42+ 
43+ @PostMapping("/testConfig")
44+ public AjaxResult testConfig(@RequestBody NotifyConfigReq notifyConfigReq) {
45+ notifyConfigService.testConfig(notifyConfigReq);
46+ return AjaxResult.success();
47+ }
48+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/NotifyTemplateController.java+63-0
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.admin.common.core.page.TableDataInfo;
10+import org.opengauss.plugin.alertcenter.entity.NotifyTemplate;
11+import org.opengauss.plugin.alertcenter.service.NotifyTemplateService;
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.validation.annotation.Validated;
14+import org.springframework.web.bind.annotation.DeleteMapping;
15+import org.springframework.web.bind.annotation.GetMapping;
16+import org.springframework.web.bind.annotation.PathVariable;
17+import org.springframework.web.bind.annotation.PostMapping;
18+import org.springframework.web.bind.annotation.RequestBody;
19+import org.springframework.web.bind.annotation.RequestMapping;
20+import org.springframework.web.bind.annotation.RestController;
21+import java.util.List;
22+ 
23+/**
24+ * @author wuyuebin
25+ * @date 2023/5/31 10:20
26+ * @description
27+ */
28+@RestController
29+@RequestMapping("/alertCenter/api/v1/notifyTemplate")
30+public class NotifyTemplateController extends BaseController {
31+ @Autowired
32+ private NotifyTemplateService notifyTemplateService;
33+ 
34+ @GetMapping
35+ public TableDataInfo getListPage(String notifyTemplateName, String notifyTemplateType) {
36+ Page page = notifyTemplateService.getListPage(notifyTemplateName, notifyTemplateType, startPage());
37+ return getDataTable(page);
38+ }
39+ 
40+ @GetMapping("/list")
41+ public AjaxResult getList(String notifyTemplateType) {
42+ List<NotifyTemplate> list = notifyTemplateService.getList(notifyTemplateType);
43+ return AjaxResult.success(list);
44+ }
45+ 
46+ @GetMapping("/{id}")
47+ public AjaxResult getById(@PathVariable Long id) {
48+ NotifyTemplate notifyTemplate = notifyTemplateService.getById(id);
49+ return AjaxResult.success(notifyTemplate);
50+ }
51+ 
52+ @PostMapping
53+ public AjaxResult saveTemplate(@Validated @RequestBody NotifyTemplate notifyTemplate) {
54+ notifyTemplateService.saveTemplate(notifyTemplate);
55+ return AjaxResult.success();
56+ }
57+ 
58+ @DeleteMapping("/{id}")
59+ public AjaxResult delById(@PathVariable Long id) {
60+ notifyTemplateService.delById(id);
61+ return AjaxResult.success();
62+ }
63+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/controller/NotifyWayController.java+64-0
@@ -0,0 +1,64 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.controller;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import org.opengauss.admin.common.core.domain.AjaxResult;
9+import org.opengauss.admin.common.core.page.TableDataInfo;
10+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
11+import org.opengauss.plugin.alertcenter.service.NotifyWayService;
12+import org.springframework.beans.factory.annotation.Autowired;
13+import org.springframework.web.bind.annotation.DeleteMapping;
14+import org.springframework.web.bind.annotation.GetMapping;
15+import org.springframework.web.bind.annotation.PathVariable;
16+import org.springframework.web.bind.annotation.PostMapping;
17+import org.springframework.web.bind.annotation.RequestBody;
18+import org.springframework.web.bind.annotation.RequestMapping;
19+import org.springframework.web.bind.annotation.RestController;
20+ 
21+import javax.validation.Valid;
22+import java.util.List;
23+ 
24+/**
25+ * @author wuyuebin
26+ * @date 2023/5/25 15:49
27+ * @description
28+ */
29+@RestController
30+@RequestMapping("/alertCenter/api/v1/notifyWay")
31+public class NotifyWayController extends BaseController {
32+ @Autowired
33+ private NotifyWayService notifyWayService;
34+ 
35+ @GetMapping
36+ public TableDataInfo getListPage(String name, String notifyType) {
37+ Page page = notifyWayService.getListPage(name, notifyType, startPage());
38+ return getDataTable(page);
39+ }
40+ 
41+ @GetMapping("/list")
42+ public AjaxResult getList(String notifyType) {
43+ List<NotifyWay> notifyWayList = notifyWayService.getList(notifyType);
44+ return AjaxResult.success(notifyWayList);
45+ }
46+ 
47+ @GetMapping("/{id}")
48+ public AjaxResult getById(@PathVariable Long id) {
49+ NotifyWay notifyWay = notifyWayService.getById(id);
50+ return AjaxResult.success(notifyWay);
51+ }
52+ 
53+ @PostMapping
54+ public AjaxResult saveNotifyWay(@Valid @RequestBody NotifyWay notifyWay) {
55+ notifyWayService.saveNotifyWay(notifyWay);
56+ return AjaxResult.success();
57+ }
58+ 
59+ @DeleteMapping("/{id}")
60+ public AjaxResult delById(@PathVariable Long id) {
61+ notifyWayService.delById(id);
62+ return AjaxResult.success();
63+ }
64+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertClusterNodeConfDto.java+25-0
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
8+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
9+import lombok.Data;
10+import lombok.experimental.Accessors;
11+ 
12+/**
13+ * @author wuyuebin
14+ * @date 2023/5/22 10:36
15+ * @description
16+ */
17+@Data
18+@Accessors(chain = true)
19+public class AlertClusterNodeConfDto {
20+ private String clusterNodeId; // 实例ID
21+ private String nodeName;
22+ @JsonSerialize(using = ToStringSerializer.class)
23+ private Long templateId;
24+ private String templateName;
25+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertContentParamDto.java+53-0
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.config.annotation.AlertContentParam;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/2 23:39
14+ * @description
15+ */
16+@Data
17+@Accessors(chain = true)
18+public class AlertContentParamDto {
19+ @AlertContentParam(name = "alertContentParam.nodeName",
20+ preVal = "test/127.0.0.1:5432(MASTER)",
21+ group = {"alert", "notify"})
22+ private String nodeName;
23+ @AlertContentParam(name = "alertContentParam.hostname",
24+ preVal = "centos",
25+ group = {"alert", "notify"})
26+ private String hostname;
27+ @AlertContentParam(name = "alertContentParam.hostIp",
28+ preVal = "127.0.0.1",
29+ group = {"alert", "notify"})
30+ private String hostIp;
31+ @AlertContentParam(name = "alertContentParam.port",
32+ preVal = "5432",
33+ group = {"alert", "notify"})
34+ private String port;
35+ @AlertContentParam(name = "alertContentParam.alertTime",
36+ preVal = "2023-01-01 00:00:00",
37+ group = {"alert", "notify"})
38+ private String alertTime;
39+ @AlertContentParam(name = "alertContentParam.alertStatus",
40+ preVal = "alerting",
41+ isI18nPreVal = true,
42+ group = {"alert", "notify"})
43+ private String alertStatus;
44+ @AlertContentParam(name = "alertContentParam.level",
45+ preVal = "serious",
46+ isI18nPreVal = true,
47+ group = {"alert", "notify"})
48+ private String level;
49+ @AlertContentParam(name = "alertContentParam.content",
50+ preVal = "The CPU usage rate is over 90%.",
51+ group = {"notify"})
52+ private String content;
53+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertRecordDto.java+23-0
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.entity.AlertRecord;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/17 10:44
14+ * @description
15+ */
16+@Data
17+@Accessors(chain = true)
18+public class AlertRecordDto extends AlertRecord {
19+ private String clusterNodeName;
20+ private String clusterId;
21+ private String nodeRole;
22+ private String hostIpAndPort;
23+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertRelationDto.java+45-0
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import com.fasterxml.jackson.annotation.JsonFormat;
8+import lombok.Data;
9+import lombok.experimental.Accessors;
10+import org.springframework.format.annotation.DateTimeFormat;
11+ 
12+import java.time.LocalDateTime;
13+ 
14+/**
15+ * @author wuyuebin
16+ * @date 2023/5/18 19:06
17+ * @description
18+ */
19+@Data
20+@Accessors(chain = true)
21+public class AlertRelationDto {
22+ private String name;
23+ 
24+ private Number[][] datas;
25+ 
26+ private String limitValue;
27+ 
28+ private String unit;
29+ 
30+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
31+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
32+ private LocalDateTime startTime;
33+ 
34+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
35+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
36+ private LocalDateTime endTime;
37+ 
38+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
39+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
40+ private LocalDateTime minTime;
41+ 
42+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
43+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
44+ private LocalDateTime maxTime;
45+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertRuleConfigDto.java+53-0
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import com.fasterxml.jackson.annotation.JsonProperty;
8+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
9+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
10+import lombok.Data;
11+ 
12+import java.util.List;
13+ 
14+/**
15+ * @author wuyuebin
16+ * @date 2023/4/28 01:28
17+ * @description
18+ */
19+@Data
20+public class AlertRuleConfigDto {
21+ private List<Group> groups;
22+ 
23+ @Data
24+ public static class Group {
25+ private String name;
26+ private List<Rule> rules;
27+ 
28+ @Data
29+ public static class Rule {
30+ private String alert;
31+ private String expr;
32+ @JsonProperty("for")
33+ private String aliasFor;
34+ private Labels labels;
35+ private Annotations annotations;
36+ 
37+ @Data
38+ public static class Labels {
39+ private String level;
40+ @JsonSerialize(using = ToStringSerializer.class)
41+ private Long templateId;
42+ @JsonSerialize(using = ToStringSerializer.class)
43+ private Long templateRuleId;
44+ }
45+ 
46+ @Data
47+ public static class Annotations {
48+ private String summary;
49+ private String description;
50+ }
51+ }
52+ }
53+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertRuleDto.java+20-0
@@ -0,0 +1,20 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.entity.AlertRule;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/15 17:37
14+ * @description
15+ */
16+@Data
17+@Accessors(chain = true)
18+public class AlertRuleDto extends AlertRule {
19+ private String ruleExpDesc;
20+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertStatisticsDto.java+26-0
@@ -0,0 +1,26 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+ 
10+/**
11+ * @author wuyuebin
12+ * @date 2023/4/14 14:50
13+ * @description
14+ */
15+@Data
16+@Accessors(chain = true)
17+public class AlertStatisticsDto {
18+ private Integer totalNum = 0;
19+ private Integer unReadNum = 0;
20+ private Integer readNum = 0;
21+ private Integer firingNum = 0;
22+ private Integer recoverNum = 0;
23+ private Integer seriousNum = 0;
24+ private Integer warnNum = 0;
25+ private Integer infoNum = 0;
26+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertTemplateDto.java+22-0
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
10+ 
11+import java.util.List;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/5/15 18:48
16+ * @description
17+ */
18+@Data
19+@Accessors(chain = true)
20+public class AlertTemplateDto extends AlertTemplate {
21+ List<AlertTemplateRuleDto> templateRuleDtoList;
22+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/AlertTemplateRuleDto.java+43-0
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import cn.hutool.core.util.StrUtil;
9+import lombok.Data;
10+import lombok.experimental.Accessors;
11+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
12+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
13+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
14+ 
15+import java.util.List;
16+import java.util.stream.Collectors;
17+ 
18+/**
19+ * @author wuyuebin
20+ * @date 2023/5/13 16:18
21+ * @description
22+ */
23+@Data
24+@Accessors(chain = true)
25+public class AlertTemplateRuleDto extends AlertTemplateRule {
26+ private String ruleExpDesc;
27+ 
28+ public void buildRuleExpDesc() {
29+ this.ruleExpDesc = this.getAlertRuleItemList().stream().map(item -> {
30+ List<String> paramVals =
31+ item.getItemParamList().stream().filter(item0 -> item0.getItemId().equals(item.getId())).map(
32+ item0 -> item0.getParamValue()).collect(Collectors.toList());
33+ return "[" + item.getRuleMark() + "]:" + MessageSourceUtil.get(item.getRuleExpName())
34+ + (CollectionUtil.isNotEmpty(paramVals)
35+ ? "(" + paramVals.stream().collect(Collectors.joining(CommonConstants.DELIMITER)) + ")" : "") + " "
36+ + (item.getAction().equals("normal")
37+ ? MessageSourceUtil.get("rule.ruleItem.normalAction") + " " + item.getOperate() + item.getLimitValue()
38+ + (StrUtil.isNotBlank(item.getUnit()) ? item.getUnit() : "") : item.getAction().equals(
39+ "increase") ? MessageSourceUtil.get("rule.ruleItem.increaseAction") : MessageSourceUtil.get(
40+ "rule.ruleItem.decreaseAction"));
41+ }).collect(Collectors.joining("<br/>"));
42+ }
43+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/NotifyWayDto.java+20-0
@@ -0,0 +1,20 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/6/1 00:55
14+ * @description
15+ */
16+@Data
17+@Accessors(chain = true)
18+public class NotifyWayDto extends NotifyWay {
19+ private String notifyTemplateName;
20+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/PrometheusConfigDto.java+116-0
@@ -0,0 +1,116 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import com.fasterxml.jackson.annotation.JsonProperty;
8+import lombok.Data;
9+ 
10+import java.util.List;
11+import java.util.Map;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/4/27 17:21
16+ * @description alerting:
17+ * alertmanagers:
18+ * - static_configs:
19+ * - targets:
20+ * - alertmanager: 9093
21+ * global:
22+ * evaluation_interval: 15s
23+ * scrape_interval: 15s
24+ * rule_files: null
25+ * scrape_configs:
26+ * - job_name: prometheus
27+ * static_configs:
28+ * - targets:
29+ * - localhost:9090
30+ */
31+@Data
32+public class PrometheusConfigDto {
33+ private Global global;
34+ private Alert alerting;
35+ @JsonProperty("rule_files")
36+ private List<String> ruleFiles;
37+ @JsonProperty("scrape_configs")
38+ private List<Job> scrapeConfigs;
39+ 
40+ @Data
41+ public static class Global {
42+ @JsonProperty("scrape_interval")
43+ private String scrapeInterval;
44+ @JsonProperty("evaluation_interval")
45+ private String evaluationInterval;
46+ @JsonProperty("scrape_timeout")
47+ private String scrapeTimeout;
48+ }
49+ 
50+ @Data
51+ public static class Alert {
52+ private List<Alertmanager> alertmanagers;
53+ 
54+ @Data
55+ public static class Alertmanager {
56+ @JsonProperty("api_version")
57+ private String apiVersion;
58+ @JsonProperty("path_prefix")
59+ private String pathPrefix;
60+ private String scheme = "http";
61+ @JsonProperty("static_configs")
62+ private List<Conf> staticConfigs;
63+ @JsonProperty("follow_redirects")
64+ private Boolean isFollowRedirects;
65+ @JsonProperty("enable_http2")
66+ private Boolean isEnableHttp2;
67+ private String timeout;
68+ 
69+ @Data
70+ public static class Conf {
71+ private List<String> targets;
72+ }
73+ }
74+ }
75+ 
76+ @Data
77+ public static class Job {
78+ @JsonProperty("job_name")
79+ private String jobName;
80+ @JsonProperty("static_configs")
81+ private List<Conf> staticConfigs;
82+ @JsonProperty("honor_timestamps")
83+ private Boolean isHonorTimestamps;
84+ @JsonProperty("scrape_interval")
85+ private String scrapeInterval;
86+ @JsonProperty("scrape_timeout")
87+ private String scrapeTimeout;
88+ @JsonProperty("metrics_path")
89+ private String metricsPath;
90+ private String scheme;
91+ @JsonProperty("follow_redirects")
92+ private Boolean isFollowRedirects;
93+ @JsonProperty("enable_http2")
94+ private Boolean isEnableHttp2;
95+ 
96+ @Data
97+ public static class Conf {
98+ private List<String> targets;
99+ private Map<String, String> labels;
100+ }
101+ 
102+ @Override
103+ public int hashCode() {
104+ return jobName.hashCode();
105+ }
106+ 
107+ @Override
108+ public boolean equals(Object obj) {
109+ if (!(obj instanceof Job)) {
110+ return false;
111+ }
112+ Job job = (Job) obj;
113+ return job.getJobName().equals(this.jobName);
114+ }
115+ }
116+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/PrometheusEnvDto.java+29-0
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.entity.AlertConfig;
10+ 
11+import java.util.List;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/6/10 11:08
16+ * @description
17+ */
18+@Data
19+@Accessors(chain = true)
20+public class PrometheusEnvDto {
21+ private String promIp;
22+ private Integer hostPort;
23+ private Integer promPort;
24+ private String promUsername;
25+ private String promPasswd;
26+ private String path;
27+ private List<AlertConfig> configList;
28+ private List<AlertConfig> delConfigList;
29+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/dto/RuleItemPropertyDto.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.dto;
6+ 
7+import lombok.Data;
8+import org.opengauss.plugin.alertcenter.config.properties.RuleItemProperty;
9+ 
10+/**
11+ * @author wuyuebin
12+ * @date 2023/5/29 11:07
13+ * @description
14+ */
15+@Data
16+public class RuleItemPropertyDto extends RuleItemProperty {
17+ private String i18nName;
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertClusterNodeConf.java+34-0
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
10+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
11+import lombok.Data;
12+import lombok.experimental.Accessors;
13+ 
14+import java.sql.Timestamp;
15+ 
16+/**
17+ * @author wuyuebin
18+ * @date 2023/4/26 16:11
19+ * @description 实例告警配置类
20+ */
21+@Accessors(chain = true)
22+@Data
23+@TableName("alert_cluster_node_conf")
24+public class AlertClusterNodeConf {
25+ @TableId
26+ @JsonSerialize(using = ToStringSerializer.class)
27+ private Long id;
28+ private String clusterNodeId;
29+ @JsonSerialize(using = ToStringSerializer.class)
30+ private Long templateId;
31+ private Timestamp updateTime;
32+ private Timestamp createTime;
33+ private Integer isDeleted;
34+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertConfig.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableName;
8+import lombok.Data;
9+import lombok.experimental.Accessors;
10+ 
11+import javax.validation.constraints.NotBlank;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/6/6 16:13
16+ * @description
17+ */
18+@Data
19+@Accessors(chain = true)
20+@TableName("alert_config")
21+public class AlertConfig {
22+ private Long id;
23+ @NotBlank
24+ private String alertIp;
25+ @NotBlank
26+ private String alertPort;
27+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertRecord.java+53-0
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
12+import lombok.Data;
13+import lombok.experimental.Accessors;
14+ 
15+import java.time.LocalDateTime;
16+ 
17+/**
18+ * @author wuyuebin
19+ * @date 2023/4/14 10:52
20+ * @description
21+ */
22+@Data
23+@Accessors(chain = true)
24+@TableName("alert_record")
25+public class AlertRecord {
26+ @TableId
27+ @JsonSerialize(using = ToStringSerializer.class)
28+ private Long id;
29+ private String clusterNodeId;
30+ @JsonSerialize(using = ToStringSerializer.class)
31+ private Long templateId;
32+ private String templateName;
33+ @JsonSerialize(using = ToStringSerializer.class)
34+ private Long templateRuleId;
35+ private String templateRuleName;
36+ private String templateRuleType;
37+ private String level;
38+ private String notifyWayIds;
39+ private String notifyWayNames;
40+ private Integer alertStatus;
41+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
42+ private LocalDateTime startTime;
43+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
44+ private LocalDateTime endTime;
45+ private Long duration;
46+ private String alertContent;
47+ private Integer recordStatus;
48+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
49+ private LocalDateTime createTime;
50+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
51+ private LocalDateTime updateTime;
52+ private Integer isDeleted;
53+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertRule.java+58-0
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableField;
8+import com.baomidou.mybatisplus.annotation.TableId;
9+import com.baomidou.mybatisplus.annotation.TableName;
10+import com.fasterxml.jackson.annotation.JsonFormat;
11+import com.fasterxml.jackson.annotation.JsonProperty;
12+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
13+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
14+import lombok.Data;
15+import lombok.experimental.Accessors;
16+import org.springframework.format.annotation.DateTimeFormat;
17+ 
18+import java.sql.Timestamp;
19+import java.time.LocalDateTime;
20+import java.util.List;
21+ 
22+/**
23+ * @author wuyuebin
24+ * @date 2023/5/9 10:04
25+ * @description
26+ */
27+@Data
28+@Accessors(chain = true)
29+@TableName("alert_rule")
30+public class AlertRule {
31+ @TableId
32+ @JsonProperty("ruleId")
33+ @JsonSerialize(using = ToStringSerializer.class)
34+ private Long id;
35+ private String alertDesc;
36+ private String notifyWayIds;
37+ private String alertNotify;
38+ private Timestamp silenceEndTime;
39+ private Timestamp silenceStartTime;
40+ private Integer isSilence;
41+ private Integer isRepeat;
42+ private String notifyDurationUnit;
43+ private Integer notifyDuration;
44+ private String ruleContent;
45+ private String ruleExpComb;
46+ private String ruleType;
47+ private String level;
48+ private String ruleName;
49+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
51+ private LocalDateTime updateTime;
52+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
53+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
54+ private LocalDateTime createTime;
55+ private Integer isDeleted;
56+ @TableField(exist = false)
57+ private List<AlertRuleItem> alertRuleItemList;
58+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertRuleItem.java+54-0
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableField;
8+import com.baomidou.mybatisplus.annotation.TableId;
9+import com.baomidou.mybatisplus.annotation.TableName;
10+import com.fasterxml.jackson.annotation.JsonFormat;
11+import com.fasterxml.jackson.annotation.JsonProperty;
12+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
13+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
14+import lombok.Data;
15+import lombok.experimental.Accessors;
16+import org.springframework.format.annotation.DateTimeFormat;
17+ 
18+import java.time.LocalDateTime;
19+import java.util.List;
20+ 
21+/**
22+ * @author wuyuebin
23+ * @date 2023/5/9 11:19
24+ * @description
25+ */
26+@Data
27+@Accessors(chain = true)
28+@TableName("alert_rule_item")
29+public class AlertRuleItem {
30+ @TableId
31+ @JsonProperty("ruleItemId")
32+ @JsonSerialize(using = ToStringSerializer.class)
33+ private Long id;
34+ @JsonSerialize(using = ToStringSerializer.class)
35+ private Long ruleId;
36+ private String ruleMark;
37+ private String ruleExpName;
38+ private String action;
39+ private String operate;
40+ private String limitValue;
41+ private String unit;
42+ private String ruleExp;
43+ private String ruleExpParam;
44+ private String ruleItemDesc;
45+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
46+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
47+ private LocalDateTime updateTime;
48+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
49+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50+ private LocalDateTime createTime;
51+ private Integer isDeleted;
52+ @TableField(exist = false)
53+ private List<AlertRuleItemParam> itemParamList;
54+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertRuleItemParam.java+44-0
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.annotation.JsonProperty;
11+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
12+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
13+import lombok.Data;
14+import lombok.experimental.Accessors;
15+import org.springframework.format.annotation.DateTimeFormat;
16+ 
17+import java.time.LocalDateTime;
18+ 
19+/**
20+ * @author wuyuebin
21+ * @date 2023/5/29 01:27
22+ * @description
23+ */
24+@Data
25+@Accessors(chain = true)
26+@TableName("alert_rule_item_param")
27+public class AlertRuleItemParam {
28+ @TableId
29+ @JsonProperty("ruleItemParamId")
30+ @JsonSerialize(using = ToStringSerializer.class)
31+ private Long id;
32+ @JsonSerialize(using = ToStringSerializer.class)
33+ private Long itemId;
34+ private String paramName;
35+ private String paramValue;
36+ private String paramOrder;
37+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
38+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
39+ private LocalDateTime updateTime;
40+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
41+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
42+ private LocalDateTime createTime;
43+ private Integer isDeleted;
44+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertTemplate.java+38-0
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
12+import lombok.Data;
13+import lombok.experimental.Accessors;
14+import org.springframework.format.annotation.DateTimeFormat;
15+ 
16+import java.time.LocalDateTime;
17+ 
18+/**
19+ * @author wuyuebin
20+ * @date 2023/4/28 02:25
21+ * @description
22+ */
23+@Data
24+@Accessors(chain = true)
25+@TableName("alert_template")
26+public class AlertTemplate {
27+ @TableId
28+ @JsonSerialize(using = ToStringSerializer.class)
29+ private Long id;
30+ private String templateName;
31+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
32+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
33+ private LocalDateTime updateTime;
34+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
35+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
36+ private LocalDateTime createTime;
37+ private Integer isDeleted;
38+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertTemplateRule.java+83-0
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableField;
8+import com.baomidou.mybatisplus.annotation.TableId;
9+import com.baomidou.mybatisplus.annotation.TableName;
10+import com.fasterxml.jackson.annotation.JsonFormat;
11+import com.fasterxml.jackson.annotation.JsonProperty;
12+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
13+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
14+import lombok.Data;
15+import lombok.experimental.Accessors;
16+import org.springframework.format.annotation.DateTimeFormat;
17+ 
18+import javax.validation.Valid;
19+import javax.validation.constraints.NotBlank;
20+import javax.validation.constraints.NotNull;
21+import javax.validation.constraints.Size;
22+import java.time.LocalDateTime;
23+import java.util.List;
24+ 
25+/**
26+ * @author wuyuebin
27+ * @date 2023/4/28 02:29
28+ * @description
29+ */
30+@Data
31+@Accessors(chain = true)
32+@TableName("alert_template_rule")
33+public class AlertTemplateRule {
34+ @TableId
35+ @JsonProperty("templateRuleId")
36+ @JsonSerialize(using = ToStringSerializer.class)
37+ private Long id;
38+ @JsonSerialize(using = ToStringSerializer.class)
39+ private Long templateId;
40+ private String alertDesc;
41+ @NotBlank
42+ private String notifyWayIds;
43+ @NotBlank
44+ private String alertNotify;
45+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
46+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
47+ private LocalDateTime silenceEndTime;
48+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
49+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50+ private LocalDateTime silenceStartTime;
51+ @NotNull
52+ private Integer isSilence;
53+ @NotNull
54+ private Integer isRepeat;
55+ @NotBlank
56+ private String notifyDurationUnit;
57+ @NotNull
58+ private Integer notifyDuration;
59+ @NotBlank
60+ private String ruleContent;
61+ @NotBlank
62+ private String ruleExpComb;
63+ @NotBlank
64+ private String ruleType;
65+ @NotBlank
66+ private String level;
67+ @NotBlank
68+ private String ruleName;
69+ @JsonSerialize(using = ToStringSerializer.class)
70+ private Long ruleId;
71+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
72+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
73+ private LocalDateTime updateTime;
74+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
75+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
76+ private LocalDateTime createTime;
77+ private Integer isDeleted;
78+ @TableField(exist = false)
79+ @NotNull
80+ @Valid
81+ @Size(min = 1)
82+ private List<AlertTemplateRuleItem> alertRuleItemList;
83+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertTemplateRuleItem.java+62-0
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableField;
8+import com.baomidou.mybatisplus.annotation.TableId;
9+import com.baomidou.mybatisplus.annotation.TableName;
10+import com.fasterxml.jackson.annotation.JsonFormat;
11+import com.fasterxml.jackson.annotation.JsonProperty;
12+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
13+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
14+import lombok.Data;
15+import lombok.experimental.Accessors;
16+import org.springframework.format.annotation.DateTimeFormat;
17+ 
18+import javax.validation.constraints.NotBlank;
19+import java.time.LocalDateTime;
20+import java.util.List;
21+ 
22+/**
23+ * @author wuyuebin
24+ * @date 2023/4/28 02:36
25+ * @description
26+ */
27+@Data
28+@Accessors(chain = true)
29+@TableName("alert_template_rule_item")
30+public class AlertTemplateRuleItem {
31+ @TableId
32+ @JsonProperty("templateRuleItemId")
33+ @JsonSerialize(using = ToStringSerializer.class)
34+ private Long id;
35+ @JsonSerialize(using = ToStringSerializer.class)
36+ private Long templateRuleId;
37+ @JsonSerialize(using = ToStringSerializer.class)
38+ private Long ruleItemId;
39+ @NotBlank
40+ private String ruleMark;
41+ @NotBlank
42+ private String ruleExpName;
43+ @NotBlank
44+ private String action;
45+ @NotBlank
46+ private String operate;
47+ @NotBlank
48+ private String limitValue;
49+ private String unit;
50+ private String ruleExp;
51+ private String ruleExpParam;
52+ private String ruleItemDesc;
53+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
54+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
55+ private LocalDateTime updateTime;
56+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
57+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
58+ private LocalDateTime createTime;
59+ private Integer isDeleted;
60+ @TableField(exist = false)
61+ private List<AlertTemplateRuleItemParam> itemParamList;
62+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/AlertTemplateRuleItemParam.java+44-0
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.annotation.JsonProperty;
11+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
12+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
13+import lombok.Data;
14+import lombok.experimental.Accessors;
15+import org.springframework.format.annotation.DateTimeFormat;
16+ 
17+import java.time.LocalDateTime;
18+ 
19+/**
20+ * @author wuyuebin
21+ * @date 2023/5/29 01:27
22+ * @description
23+ */
24+@Data
25+@Accessors(chain = true)
26+@TableName("alert_template_rule_item_param")
27+public class AlertTemplateRuleItemParam {
28+ @TableId
29+ @JsonProperty("templateRuleItemParamId")
30+ @JsonSerialize(using = ToStringSerializer.class)
31+ private Long id;
32+ @JsonSerialize(using = ToStringSerializer.class)
33+ private Long itemId;
34+ private String paramName;
35+ private String paramValue;
36+ private String paramOrder;
37+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
38+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
39+ private LocalDateTime updateTime;
40+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
41+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
42+ private LocalDateTime createTime;
43+ private Integer isDeleted;
44+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/NctigbaEnv.java+38-0
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.IdType;
8+import com.baomidou.mybatisplus.annotation.TableField;
9+import com.baomidou.mybatisplus.annotation.TableId;
10+import com.baomidou.mybatisplus.annotation.TableName;
11+import lombok.Data;
12+import lombok.experimental.Accessors;
13+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
14+ 
15+@Data
16+@Accessors(chain = true)
17+@TableName("nctigba_env")
18+public class NctigbaEnv {
19+ @TableId(type = IdType.ASSIGN_UUID)
20+ String id;
21+ String hostid;
22+ Type type;
23+ String username;
24+ String path;
25+ Integer port;
26+ String nodeid;
27+ @TableField(exist = false)
28+ OpsHostEntity host;
29+ 
30+ public enum Type {
31+ PROMETHEUS, NODE_EXPORTER, OPENGAUSS_EXPORTER,
32+ ELASTICSEARCH, FILEBEAT,
33+ AGENT,
34+ PROMETHEUS_PKG, NODE_EXPORTER_PKG, OPENGAUSS_EXPORTER_PKG,
35+ ELASTICSEARCH_PKG, FILEBEAT_PKG,
36+ AGENT_PKG,
37+ }
38+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/NotifyConfig.java+71-0
@@ -0,0 +1,71 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.fasterxml.jackson.annotation.JsonFormat;
9+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
10+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
11+import lombok.Data;
12+import lombok.experimental.Accessors;
13+import org.springframework.format.annotation.DateTimeFormat;
14+ 
15+import javax.validation.constraints.NotBlank;
16+import javax.validation.constraints.NotNull;
17+import java.time.LocalDateTime;
18+ 
19+/**
20+ * @author wuyuebin
21+ * @date 2023/5/7 14:01
22+ * @description
23+ */
24+@Data
25+@Accessors(chain = true)
26+public class NotifyConfig {
27+ @TableId
28+ @JsonSerialize(using = ToStringSerializer.class)
29+ private Long id;
30+ @NotBlank(groups = DefaultGroup.class)
31+ private String type;
32+ @NotBlank(groups = EmailGroup.class)
33+ private String email;
34+ @NotBlank(groups = EmailGroup.class)
35+ private String sender;
36+ @NotBlank(groups = EmailGroup.class)
37+ private String sever;
38+ @NotNull(groups = EmailGroup.class)
39+ private Integer port;
40+ @NotBlank(groups = EmailGroup.class)
41+ private String account;
42+ @NotBlank(groups = EmailGroup.class)
43+ private String passwd;
44+ @NotBlank(groups = {WeComGroup.class, DingTalkGroup.class})
45+ private String agentId;
46+ @NotBlank(groups = {WeComGroup.class, DingTalkGroup.class})
47+ private String appKey;
48+ @NotBlank(groups = {WeComGroup.class, DingTalkGroup.class})
49+ private String secret;
50+ @NotNull(groups = DefaultGroup.class)
51+ private Integer enable;
52+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
53+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
54+ private LocalDateTime createTime;
55+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
56+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
57+ private LocalDateTime updateTime;
58+ private Integer isDeleted;
59+ 
60+ public interface DefaultGroup {
61+ }
62+ 
63+ public interface EmailGroup {
64+ }
65+ 
66+ public interface WeComGroup {
67+ }
68+ 
69+ public interface DingTalkGroup {
70+ }
71+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/NotifyMessage.java+55-0
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
12+import lombok.Data;
13+import lombok.experimental.Accessors;
14+ 
15+import java.time.LocalDateTime;
16+ 
17+/**
18+ * @author wuyuebin
19+ * @date 2023/5/6 14:19
20+ * @description
21+ */
22+@Data
23+@Accessors(chain = true)
24+@TableName("notify_message")
25+public class NotifyMessage {
26+ @TableId
27+ @JsonSerialize(using = ToStringSerializer.class)
28+ private Long id;
29+ 
30+ @JsonSerialize(using = ToStringSerializer.class)
31+ private Long recordId;
32+ 
33+ private String messageType;
34+ 
35+ private String title;
36+ 
37+ private String content;
38+ 
39+ private String email;
40+ 
41+ private String personId;
42+ 
43+ private String deptId;
44+ 
45+ private String webhook;
46+ 
47+ private Integer status;
48+ 
49+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50+ private LocalDateTime createTime;
51+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
52+ private LocalDateTime updateTime;
53+ 
54+ private Integer isDeleted;
55+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/NotifyTemplate.java+54-0
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
12+import lombok.Data;
13+import lombok.experimental.Accessors;
14+import org.springframework.format.annotation.DateTimeFormat;
15+ 
16+import javax.validation.constraints.NotBlank;
17+import java.time.LocalDateTime;
18+ 
19+/**
20+ * @author wuyuebin
21+ * @date 2023/5/6 14:27
22+ * @description
23+ */
24+@Data
25+@Accessors(chain = true)
26+@TableName("notify_template")
27+public class NotifyTemplate {
28+ @TableId
29+ @JsonSerialize(using = ToStringSerializer.class)
30+ private Long id;
31+ 
32+ @NotBlank
33+ private String notifyTemplateName;
34+ 
35+ private String notifyTemplateDesc;
36+ 
37+ @NotBlank
38+ private String notifyTitle;
39+ 
40+ @NotBlank
41+ private String notifyContent;
42+ 
43+ @NotBlank
44+ private String notifyTemplateType;
45+ 
46+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
47+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
48+ private LocalDateTime createTime;
49+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
51+ private LocalDateTime updateTime;
52+ 
53+ private Integer isDeleted = 0;
54+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/entity/NotifyWay.java+53-0
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.entity;
6+ 
7+import com.baomidou.mybatisplus.annotation.TableId;
8+import com.baomidou.mybatisplus.annotation.TableName;
9+import com.fasterxml.jackson.annotation.JsonFormat;
10+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
12+import lombok.Data;
13+import lombok.experimental.Accessors;
14+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
15+import org.springframework.format.annotation.DateTimeFormat;
16+ 
17+import javax.validation.constraints.NotBlank;
18+import javax.validation.constraints.NotNull;
19+import java.time.LocalDateTime;
20+ 
21+/**
22+ * @author wuyuebin
23+ * @date 2023/5/9 11:24
24+ * @description
25+ */
26+@Accessors(chain = true)
27+@Data
28+@TableName("notify_way")
29+public class NotifyWay {
30+ @TableId
31+ @JsonSerialize(using = ToStringSerializer.class)
32+ private Long id;
33+ @NotBlank
34+ private String name;
35+ @NotBlank
36+ private String notifyType;
37+ private String phone;
38+ private String email;
39+ private String personId;
40+ private String deptId;
41+ @NotNull
42+ @JsonSerialize(using = ToStringSerializer.class)
43+ private Long notifyTemplateId;
44+ 
45+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
46+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
47+ private LocalDateTime createTime;
48+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
49+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50+ private LocalDateTime updateTime;
51+ 
52+ private Integer isDeleted = CommonConstants.IS_NOT_DELETE;
53+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/listener/PluginListener.java+61-0
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.listener;
6+ 
7+import com.gitee.starblues.spring.MainApplicationContext;
8+import com.gitee.starblues.spring.SpringBeanFactory;
9+import org.opengauss.admin.common.core.vo.MenuVo;
10+import org.opengauss.admin.system.plugin.facade.MenuFacade;
11+import org.springframework.boot.context.event.ApplicationReadyEvent;
12+import org.springframework.context.ApplicationEvent;
13+import org.springframework.context.ApplicationListener;
14+import org.springframework.context.event.ContextClosedEvent;
15+ 
16+/**
17+ * @Author wuyuebin
18+ * @Date 2023/4/13 09:57
19+ * @Description
20+ */
21+public class PluginListener implements ApplicationListener<ApplicationEvent> {
22+ private final String pluginId = "alert-center";
23+ 
24+ @Override
25+ public void onApplicationEvent(ApplicationEvent event) {
26+ if (event instanceof ApplicationReadyEvent) {
27+ MainApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext().getBean(
28+ MainApplicationContext.class);
29+ SpringBeanFactory factory = context.getSpringBeanFactory();
30+ MenuFacade menuFacade = factory.getBean(MenuFacade.class);
31+ if (menuFacade != null) {
32+ MenuVo firstMenu = menuFacade.savePluginMenu(pluginId, "监控告警", "Alert Center", 6, "alert");
33+ menuFacade.savePluginMenu(pluginId, "告警记录", "Alert Record", 22, "vem/alert/alertRecord",
34+ firstMenu.getMenuId());
35+ menuFacade.savePluginRoute(pluginId, "告警详细", "Alert Record Detail", "vem/alert/recordDetail",
36+ firstMenu.getMenuId());
37+ menuFacade.savePluginMenu(pluginId, "告警配置", "Alert Config", 23, "vem/alert/AlertClusterNodeConf",
38+ firstMenu.getMenuId());
39+ menuFacade.savePluginMenu(pluginId, "告警模板", "Alert Template", 24, "vem/alert/alertTemplate",
40+ firstMenu.getMenuId());
41+ menuFacade.savePluginMenu(pluginId, "告警规则", "Alert Rule", 25, "vem/alert/alertRule",
42+ firstMenu.getMenuId());
43+ MenuVo secondMenu = menuFacade.savePluginMenu(pluginId, "通知配置", "Notify Configuration", 7,
44+ "notify");
45+ menuFacade.savePluginMenu(pluginId, "通知模板", "Notify Template", 26, "vem/notify/notifyTemplate",
46+ secondMenu.getMenuId());
47+ menuFacade.savePluginMenu(pluginId, "通知方式", "Notify Way", 27, "vem/notify/notifyWay",
48+ secondMenu.getMenuId());
49+ }
50+ }
51+ if (event instanceof ContextClosedEvent) {
52+ MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext().getBean(
53+ MainApplicationContext.class);
54+ SpringBeanFactory factory = context.getSpringBeanFactory();
55+ MenuFacade menuFacade = factory.getBean(MenuFacade.class);
56+ if (menuFacade != null) {
57+ menuFacade.deletePluginMenu(pluginId);
58+ }
59+ }
60+ }
61+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertClusterNodeConfMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertClusterNodeConf;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/4/26 16:45
14+ * @description
15+ */
16+@Mapper
17+public interface AlertClusterNodeConfMapper extends BaseMapper<AlertClusterNodeConf> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertConfigMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertConfig;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/6/6 16:14
14+ * @description
15+ */
16+@Mapper
17+public interface AlertConfigMapper extends BaseMapper<AlertConfig> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertRecordMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertRecord;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/2 10:23
14+ * @description
15+ */
16+@Mapper
17+public interface AlertRecordMapper extends BaseMapper<AlertRecord> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertRuleItemMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertRuleItem;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/9 11:30
14+ * @description
15+ */
16+@Mapper
17+public interface AlertRuleItemMapper extends BaseMapper<AlertRuleItem> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertRuleItemParamMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertRuleItemParam;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/6/5 16:49
14+ * @description
15+ */
16+@Mapper
17+public interface AlertRuleItemParamMapper extends BaseMapper<AlertRuleItemParam> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertRuleMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertRule;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/9 10:07
14+ * @description
15+ */
16+@Mapper
17+public interface AlertRuleMapper extends BaseMapper<AlertRule> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertTemplateMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/4/28 02:42
14+ * @description
15+ */
16+@Mapper
17+public interface AlertTemplateMapper extends BaseMapper<AlertTemplate> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertTemplateRuleItemMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/4/28 02:44
14+ * @description
15+ */
16+@Mapper
17+public interface AlertTemplateRuleItemMapper extends BaseMapper<AlertTemplateRuleItem> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertTemplateRuleItemParamMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItemParam;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/6/5 16:49
14+ * @description
15+ */
16+@Mapper
17+public interface AlertTemplateRuleItemParamMapper extends BaseMapper<AlertTemplateRuleItemParam> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/AlertTemplateRuleMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/4/28 02:43
14+ * @description
15+ */
16+@Mapper
17+public interface AlertTemplateRuleMapper extends BaseMapper<AlertTemplateRule> {
18+}
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/SearchMapper.javaplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/NctigbaEnvMapper.java+8-14
@@ -1,19 +1,13 @@
1-package com.nctigba.observability.instance.mapper;1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
2 6 
3import com.baomidou.mybatisplus.core.mapper.BaseMapper;7import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4-import com.nctigba.observability.instance.entity.Searc;
5- 
6import org.apache.ibatis.annotations.Mapper;8import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.NctigbaEnv;
7 10 
8-/**
9- * <p>
10- * Mapper interface
11- * </p>
12- *
13- * @author auto
14- * @since 2022-09-05
15- */
16@Mapper11@Mapper
17-public interface SearchMapper extends BaseMapper<Searc> {12+public interface NctigbaEnvMapper extends BaseMapper<NctigbaEnv> {
18- 13+}
19-}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/NotifyConfigMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.NotifyConfig;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/7 14:06
14+ * @description
15+ */
16+@Mapper
17+public interface NotifyConfigMapper extends BaseMapper<NotifyConfig> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/NotifyMessageMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.NotifyMessage;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/6 16:08
14+ * @description
15+ */
16+@Mapper
17+public interface NotifyMessageMapper extends BaseMapper<NotifyMessage> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/NotifyTemplateMapper.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
8+import org.apache.ibatis.annotations.Mapper;
9+import org.opengauss.plugin.alertcenter.entity.NotifyTemplate;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/6 15:55
14+ * @description
15+ */
16+@Mapper
17+public interface NotifyTemplateMapper extends BaseMapper<NotifyTemplate> {
18+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/mapper/NotifyWayMapper.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.mapper;
6+ 
7+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
8+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
9+import com.baomidou.mybatisplus.core.toolkit.Constants;
10+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
11+import org.apache.ibatis.annotations.Mapper;
12+import org.apache.ibatis.annotations.Param;
13+import org.apache.ibatis.annotations.Select;
14+import org.opengauss.plugin.alertcenter.dto.NotifyWayDto;
15+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
16+ 
17+/**
18+ * @author wuyuebin
19+ * @date 2023/5/9 11:35
20+ * @description
21+ */
22+@Mapper
23+public interface NotifyWayMapper extends BaseMapper<NotifyWay> {
24+ @Select("select t1.*,t2.notify_template_name from notify_way t1 left join notify_template t2 "
25+ + "on t1.notify_template_id = t2.id ${ew.customSqlSegment}")
26+ Page<NotifyWayDto> selectDtoPage(Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper);
27+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/AlertClusterNodeAndTemplateReq.java+31-0
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+ 
10+import javax.validation.Valid;
11+import javax.validation.constraints.NotBlank;
12+import javax.validation.constraints.NotNull;
13+import java.util.List;
14+ 
15+/**
16+ * @author wuyuebin
17+ * @date 2023/5/23 18:30
18+ * @description
19+ */
20+@Data
21+@Accessors(chain = true)
22+public class AlertClusterNodeAndTemplateReq {
23+ @NotBlank
24+ private String clusterNodeIds;
25+ @NotBlank
26+ private String templateName;
27+ 
28+ @NotNull
29+ @Valid
30+ private List<AlertTemplateRuleReq> templateRuleReqList;
31+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/AlertClusterNodeConfReq.java+23-0
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+ 
9+import javax.validation.constraints.NotBlank;
10+import javax.validation.constraints.NotNull;
11+ 
12+/**
13+ * @author wuyuebin
14+ * @date 2023/4/26 16:40
15+ * @description
16+ */
17+@Data
18+public class AlertClusterNodeConfReq {
19+ @NotBlank
20+ private String clusterNodeIds; // 实例nodeId,多个,逗号分隔
21+ @NotNull
22+ private Long templateId; // 告警模板ID
23+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/AlertRecordReq.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+ 
9+/**
10+ * @author wuyuebin
11+ * @date 2023/4/14 14:55
12+ * @description
13+ */
14+@Data
15+public class AlertRecordReq {
16+ private String clusterNodeId;
17+ 
18+ /**
19+ * 平台处理get请求时,无法将string(yyyy-MM-dd HH:mm:ss)参数转化为日期
20+ */
21+ private String startTime; // yyyy-MM-dd HH:mm:ss
22+ 
23+ private String endTime; // yyyy-MM-dd HH:mm:ss
24+ private String recordStatus; // 多个,逗号分隔
25+ private String alertStatus; // 多个,逗号分隔
26+ private String alertLevel; // 多个,逗号分隔
27+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/AlertStatisticsReq.java+24-0
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+ 
9+/**
10+ * @author wuyuebin
11+ * @date 2023/4/14 14:56
12+ * @description
13+ */
14+@Data
15+public class AlertStatisticsReq {
16+ private String clusterNodeId;
17+ 
18+ /**
19+ * 平台处理get请求时,无法将string(yyyy-MM-dd HH:mm:ss)参数转化为日期
20+ */
21+ private String startTime; // yyyy-MM-dd HH:mm:ss
22+ 
23+ private String endTime; // yyyy-MM-dd HH:mm:ss
24+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/AlertTemplateReq.java+30-0
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+ 
9+import javax.validation.Valid;
10+import javax.validation.constraints.NotBlank;
11+import javax.validation.constraints.NotNull;
12+import javax.validation.constraints.Size;
13+import java.util.List;
14+ 
15+/**
16+ * @author wuyuebin
17+ * @date 2023/5/17 00:07
18+ * @description
19+ */
20+@Data
21+public class AlertTemplateReq {
22+ private Long id;
23+ @NotBlank
24+ private String templateName;
25+ 
26+ @Valid
27+ @NotNull
28+ @Size(min = 1)
29+ private List<AlertTemplateRuleReq> templateRuleReqList;
30+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/AlertTemplateRuleReq.java+21-0
@@ -0,0 +1,21 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+ 
9+import javax.validation.constraints.NotNull;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/5/17 00:10
14+ * @description
15+ */
16+@Data
17+public class AlertTemplateRuleReq {
18+ private Long templateRuleId;
19+ @NotNull
20+ private Long ruleId;
21+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/NotifyConfigReq.java+20-0
@@ -0,0 +1,20 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+import lombok.experimental.Accessors;
9+import org.opengauss.plugin.alertcenter.entity.NotifyConfig;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/6/2 03:09
14+ * @description
15+ */
16+@Data
17+@Accessors(chain = true)
18+public class NotifyConfigReq extends NotifyConfig {
19+ private Long notifyWayId;
20+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/ResInfo.java+20-0
@@ -0,0 +1,20 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.AllArgsConstructor;
8+import lombok.Data;
9+ 
10+@Data
11+@AllArgsConstructor
12+public class ResInfo {
13+ private int exitStatus;
14+ private String outRes;
15+ private String errRes;
16+ 
17+ public ResInfo() {
18+ this.exitStatus = -2147483648;
19+ }
20+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/RuleReq.java+19-0
@@ -0,0 +1,19 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model;
6+ 
7+import lombok.Data;
8+ 
9+/**
10+ * @author wuyuebin
11+ * @date 2023/5/9 09:58
12+ * @description
13+ */
14+@Data
15+public class RuleReq {
16+ private String ruleName;
17+ private String ruleType;
18+ private String level;
19+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/api/AlertApiReq.java+29-0
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model.api;
6+ 
7+import com.fasterxml.jackson.annotation.JsonFormat;
8+import lombok.Data;
9+ 
10+import java.time.LocalDateTime;
11+import java.util.Map;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/4/30 01:56
16+ * @description prometheus推送的告警信息
17+ */
18+@Data
19+public class AlertApiReq {
20+ private AlertLabels labels;
21+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
22+ private LocalDateTime startsAt;
23+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
24+ private LocalDateTime endsAt;
25+ 
26+ private Map annotations;
27+ 
28+ private String generatorURL;
29+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/model/api/AlertLabels.java+25-0
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.model.api;
6+ 
7+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
8+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
9+import lombok.Data;
10+ 
11+/**
12+ * @author wuyuebin
13+ * @date 2023/4/30 01:59
14+ * @description prometheus推送的告警信息额外信息
15+ */
16+@Data
17+public class AlertLabels {
18+ private String alertname;
19+ private String instance; // 对应cluster_node_id
20+ private String level;
21+ @JsonSerialize(using = ToStringSerializer.class)
22+ private Long templateId;
23+ @JsonSerialize(using = ToStringSerializer.class)
24+ private Long templateRuleId;
25+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/schedule/AlertSchedule.java+51-0
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.schedule;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
9+import lombok.extern.slf4j.Slf4j;
10+import org.opengauss.plugin.alertcenter.entity.NotifyMessage;
11+import org.opengauss.plugin.alertcenter.mapper.NotifyMessageMapper;
12+import org.opengauss.plugin.alertcenter.service.EmailService;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.scheduling.annotation.Scheduled;
15+import org.springframework.stereotype.Component;
16+ 
17+import java.util.List;
18+import java.util.stream.Collectors;
19+ 
20+/**
21+ * @author wuyuebin
22+ * @date 2023/5/6 16:43
23+ * @description
24+ */
25+@Component
26+@Slf4j
27+public class AlertSchedule {
28+ @Autowired
29+ private NotifyMessageMapper notifyMessageMapper;
30+ 
31+ @Autowired
32+ private EmailService emailService;
33+ 
34+ @Scheduled(cron = "0 0/1 * * * ?")
35+ public void alertNotifySchedule() {
36+ List<NotifyMessage> notifyMessages = notifyMessageMapper.selectList(
37+ Wrappers.<NotifyMessage>lambdaQuery().eq(NotifyMessage::getStatus, 0).in(NotifyMessage::getIsDeleted,
38+ 0));
39+ if (CollectionUtil.isEmpty(notifyMessages)) {
40+ return;
41+ }
42+ List<NotifyMessage> emailMessages = notifyMessages.stream().filter(
43+ item -> item.getMessageType().equals("email")).collect(Collectors.toList());
44+ List<NotifyMessage> weComMessages = notifyMessages.stream().filter(
45+ item -> item.getMessageType().equals("WeCom")).collect(Collectors.toList());
46+ List<NotifyMessage> dingTalkMessages = notifyMessages.stream().filter(
47+ item -> item.getMessageType().equals("DingTalk")).collect(Collectors.toList());
48+ 
49+ emailService.send(emailMessages);
50+ }
51+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertApiService.java+214-0
@@ -0,0 +1,214 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import cn.hutool.core.util.StrUtil;
9+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
10+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
11+import lombok.extern.slf4j.Slf4j;
12+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
13+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
14+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
15+import org.opengauss.admin.common.exception.ServiceException;
16+import org.opengauss.admin.system.plugin.facade.HostFacade;
17+import org.opengauss.admin.system.service.ops.IOpsClusterNodeService;
18+import org.opengauss.admin.system.service.ops.IOpsClusterService;
19+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
20+import org.opengauss.plugin.alertcenter.dto.AlertContentParamDto;
21+import org.opengauss.plugin.alertcenter.entity.AlertRecord;
22+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
23+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
24+import org.opengauss.plugin.alertcenter.entity.NotifyMessage;
25+import org.opengauss.plugin.alertcenter.entity.NotifyTemplate;
26+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
27+import org.opengauss.plugin.alertcenter.mapper.AlertRecordMapper;
28+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateMapper;
29+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleMapper;
30+import org.opengauss.plugin.alertcenter.mapper.NotifyMessageMapper;
31+import org.opengauss.plugin.alertcenter.mapper.NotifyTemplateMapper;
32+import org.opengauss.plugin.alertcenter.mapper.NotifyWayMapper;
33+import org.opengauss.plugin.alertcenter.model.api.AlertApiReq;
34+import org.opengauss.plugin.alertcenter.model.api.AlertLabels;
35+import org.opengauss.plugin.alertcenter.utils.TextParser;
36+import org.springframework.beans.factory.annotation.Autowired;
37+import org.springframework.stereotype.Service;
38+import org.springframework.transaction.annotation.Transactional;
39+import org.springframework.util.CollectionUtils;
40+ 
41+import java.time.Duration;
42+import java.time.LocalDateTime;
43+import java.time.format.DateTimeFormatter;
44+import java.util.Arrays;
45+import java.util.List;
46+import java.util.stream.Collectors;
47+ 
48+/**
49+ * @author wuyuebin
50+ * @date 2023/5/2 10:05
51+ * @description
52+ */
53+@Service
54+@Slf4j
55+public class AlertApiService {
56+ @Autowired
57+ private AlertRecordMapper alertRecordMapper;
58+ @Autowired
59+ private AlertTemplateMapper templateMapper;
60+ @Autowired
61+ private AlertTemplateRuleMapper templateRuleMapper;
62+ @Autowired
63+ private NotifyWayMapper notifyWayMapper;
64+ @Autowired
65+ private NotifyTemplateMapper notifyTemplateMapper;
66+ @Autowired
67+ private NotifyMessageMapper notifyMessageMapper;
68+ @Autowired(required = false)
69+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
70+ private IOpsClusterService clusterService;
71+ @Autowired(required = false)
72+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
73+ private IOpsClusterNodeService clusterNodeService;
74+ @Autowired(required = false)
75+ @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
76+ private HostFacade hostFacade;
77+ 
78+ @Transactional
79+ public void alerts(List<AlertApiReq> alertApiReqList) {
80+ for (AlertApiReq alertApiReq : alertApiReqList) {
81+ AlertLabels labels = alertApiReq.getLabels();
82+ Long templateRuleId = labels.getTemplateRuleId();
83+ AlertTemplateRule alertTemplateRule = templateRuleMapper.selectById(templateRuleId);
84+ if (alertTemplateRule == null) {
85+ return;
86+ }
87+ String notifyWayIds = alertTemplateRule.getNotifyWayIds();
88+ String notifyWayNames = "";
89+ if (StrUtil.isNotBlank(notifyWayIds)) {
90+ List<NotifyWay> notifyWays = notifyWayMapper.selectBatchIds(
91+ Arrays.asList(notifyWayIds.split(CommonConstants.DELIMITER)));
92+ if (CollectionUtil.isNotEmpty(notifyWays)) {
93+ notifyWayNames = notifyWays.stream().map(item -> item.getName()).collect(
94+ Collectors.joining(CommonConstants.DELIMITER));
95+ }
96+ }
97+ String clusterNodeId = labels.getInstance();
98+ AlertContentParamDto contentParamDto = setAndGetAlertContentParamDto(clusterNodeId,
99+ alertApiReq.getStartsAt(), labels.getLevel());
100+ 
101+ AlertRecord alertRecord = saveAndGetRecord(alertApiReq, notifyWayNames, alertTemplateRule, contentParamDto);
102+ contentParamDto.setContent(alertRecord.getAlertContent());
103+ // 添加消息
104+ Integer isRepeat = alertTemplateRule.getIsRepeat(); // 是否重复告警
105+ if (isRepeat == CommonConstants.IS_NOT_REPEAT) {
106+ Long count = notifyMessageMapper.selectCount(
107+ Wrappers.<NotifyMessage>lambdaQuery().eq(NotifyMessage::getRecordId, alertRecord.getId()));
108+ if (count > 0) {
109+ continue;
110+ }
111+ }
112+ Integer isSilence = alertTemplateRule.getIsSilence(); // 是否静默
113+ LocalDateTime now = LocalDateTime.now();
114+ if (isSilence == CommonConstants.IS_SILENCE && now.isAfter(alertTemplateRule.getSilenceStartTime())
115+ && now.isBefore(alertTemplateRule.getSilenceEndTime())) {
116+ continue;
117+ }
118+ saveNotifyMessage(notifyWayIds, contentParamDto, alertRecord.getId());
119+ }
120+ }
121+ 
122+ private AlertRecord saveAndGetRecord(
123+ AlertApiReq alertApiReq, String notifyWayNames, AlertTemplateRule alertTemplateRule,
124+ AlertContentParamDto contentParamDto) {
125+ String clusterNodeId = alertApiReq.getLabels().getInstance();
126+ Long templateId = alertApiReq.getLabels().getTemplateId();
127+ List<AlertRecord> alertRecords = alertRecordMapper.selectList(Wrappers.<AlertRecord>lambdaQuery()
128+ .eq(AlertRecord::getClusterNodeId, clusterNodeId).eq(AlertRecord::getTemplateId, templateId)
129+ .eq(AlertRecord::getTemplateRuleId, alertTemplateRule.getId()).eq(AlertRecord::getStartTime,
130+ alertApiReq.getStartsAt())
131+ .orderByDesc(AlertRecord::getUpdateTime));
132+ AlertRecord alertRecord = null;
133+ if (CollectionUtils.isEmpty(alertRecords)) {
134+ alertRecord = new AlertRecord();
135+ AlertTemplate alertTemplate = templateMapper.selectById(templateId);
136+ alertRecord.setClusterNodeId(clusterNodeId).setTemplateId(templateId).setTemplateRuleId(
137+ alertTemplateRule.getId()).setStartTime(alertApiReq.getStartsAt()).setEndTime(
138+ alertApiReq.getEndsAt()).setCreateTime(LocalDateTime.now());
139+ alertRecord.setDuration(
140+ Duration.between(alertRecord.getStartTime(), alertRecord.getEndTime()).toSeconds());
141+ alertRecord.setTemplateName(alertTemplate.getTemplateName()).setTemplateRuleName(
142+ alertTemplateRule.getRuleName())
143+ .setTemplateRuleType(alertTemplateRule.getRuleType()).setLevel(alertTemplateRule.getLevel())
144+ .setNotifyWayIds(alertTemplateRule.getNotifyWayIds()).setNotifyWayNames(notifyWayNames);
145+ String ruleContent = alertTemplateRule.getRuleContent();
146+ TextParser textParser = new TextParser();
147+ alertRecord.setAlertContent(textParser.parse(ruleContent, contentParamDto));
148+ alertRecordMapper.insert(alertRecord);
149+ } else {
150+ alertRecord = alertRecords.get(0);
151+ if (alertRecord.getAlertStatus() == CommonConstants.RECOVER_STATUS) {
152+ return alertRecord;
153+ }
154+ alertRecord.setNotifyWayIds(alertTemplateRule.getNotifyWayIds()).setNotifyWayNames(notifyWayNames);
155+ alertRecord.setAlertStatus(alertRecord.getEndTime().equals(
156+ alertApiReq.getEndsAt()) ? CommonConstants.RECOVER_STATUS : CommonConstants.FIRING_STATUS);
157+ alertRecord.setEndTime(alertApiReq.getEndsAt());
158+ alertRecord.setDuration(
159+ Duration.between(alertRecord.getStartTime(), alertRecord.getEndTime()).toSeconds());
160+ alertRecord.setUpdateTime(LocalDateTime.now());
161+ alertRecordMapper.updateById(alertRecord);
162+ }
163+ return alertRecord;
164+ }
165+ 
166+ private AlertContentParamDto setAndGetAlertContentParamDto(
167+ String clusterNodeId, LocalDateTime alertTime, String level) {
168+ OpsClusterNodeEntity opsClusterNodeEntity = clusterNodeService.getById(clusterNodeId);
169+ if (opsClusterNodeEntity == null) {
170+ throw new ServiceException("cluster node is not found");
171+ }
172+ OpsHostEntity opsHost = hostFacade.getById(opsClusterNodeEntity.getHostId());
173+ if (opsHost == null) {
174+ throw new ServiceException("host is not found");
175+ }
176+ OpsClusterEntity opsClusterEntity = clusterService.getById(opsClusterNodeEntity.getClusterId());
177+ if (opsClusterEntity == null) {
178+ throw new ServiceException("cluster is not found");
179+ }
180+ AlertContentParamDto contentParamDto = new AlertContentParamDto();
181+ String nodeName =
182+ opsClusterEntity.getClusterId() + "/" + opsHost.getPublicIp() + ":" + opsClusterEntity.getPort()
183+ + "(" + opsClusterNodeEntity.getClusterRole() + ")";
184+ contentParamDto.setHostname(opsHost.getHostname()).setNodeName(nodeName).setPort(
185+ opsClusterEntity.getPort() != null ? opsHost.getPort().toString() : "").setHostIp(
186+ opsHost.getPublicIp()).setAlertTime(
187+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(alertTime)).setLevel(
188+ level);
189+ return contentParamDto;
190+ }
191+ 
192+ private void saveNotifyMessage(String notifyWayIds, AlertContentParamDto contentParamDto, Long alertRecordId) {
193+ if (StrUtil.isBlank(notifyWayIds)) {
194+ return;
195+ }
196+ String[] notifyWayIdArr = notifyWayIds.split(CommonConstants.DELIMITER);
197+ for (String notifyWayId : notifyWayIdArr) {
198+ NotifyMessage notifyMessage = new NotifyMessage();
199+ NotifyWay notifyWay = notifyWayMapper.selectById(Integer.valueOf(notifyWayId));
200+ Long notifyTemplateId = notifyWay.getNotifyTemplateId();
201+ NotifyTemplate notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId);
202+ notifyMessage.setMessageType(notifyWay.getNotifyType());
203+ notifyMessage.setTitle(notifyTemplate.getNotifyTitle());
204+ String content = new TextParser().parse(notifyTemplate.getNotifyContent(), contentParamDto);
205+ notifyMessage.setContent(content);
206+ notifyMessage.setEmail(notifyWay.getEmail());
207+ notifyMessage.setPersonId(notifyWay.getPersonId());
208+ notifyMessage.setDeptId(notifyWay.getDeptId());
209+ notifyMessage.setCreateTime(LocalDateTime.now());
210+ notifyMessage.setRecordId(alertRecordId);
211+ notifyMessageMapper.insert(notifyMessage);
212+ }
213+ }
214+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertClusterNodeConfService.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.service.IService;
8+import org.opengauss.plugin.alertcenter.dto.AlertClusterNodeConfDto;
9+import org.opengauss.plugin.alertcenter.entity.AlertClusterNodeConf;
10+import org.opengauss.plugin.alertcenter.model.AlertClusterNodeAndTemplateReq;
11+import org.opengauss.plugin.alertcenter.model.AlertClusterNodeConfReq;
12+import java.util.List;
13+ 
14+/**
15+ * @author wuyuebin
16+ * @date 2023/5/22 10:46
17+ * @description
18+ */
19+public interface AlertClusterNodeConfService extends IService<AlertClusterNodeConf> {
20+ void saveClusterNodeConf(AlertClusterNodeConfReq alertClusterNodeConfReq);
21+ 
22+ AlertClusterNodeConf getByClusterNodeId(String clusterNodeId);
23+ 
24+ void saveAlertTemplateAndConfig(AlertClusterNodeAndTemplateReq clusterNodeAndTemplateReq);
25+ 
26+ List<AlertClusterNodeConfDto> getList();
27+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertConfigService.java+17-0
@@ -0,0 +1,17 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.service.IService;
8+import org.opengauss.plugin.alertcenter.entity.AlertConfig;
9+ 
10+/**
11+ * @author wuyuebin
12+ * @date 2023/6/6 17:18
13+ * @description
14+ */
15+public interface AlertConfigService extends IService<AlertConfig> {
16+ void saveAlertConf(AlertConfig alertConfig);
17+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertRecordService.java+35-0
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import com.baomidou.mybatisplus.extension.service.IService;
9+import org.opengauss.plugin.alertcenter.dto.AlertRecordDto;
10+import org.opengauss.plugin.alertcenter.dto.AlertRelationDto;
11+import org.opengauss.plugin.alertcenter.dto.AlertStatisticsDto;
12+import org.opengauss.plugin.alertcenter.entity.AlertRecord;
13+import org.opengauss.plugin.alertcenter.model.AlertRecordReq;
14+import org.opengauss.plugin.alertcenter.model.AlertStatisticsReq;
15+ 
16+import java.util.List;
17+ 
18+/**
19+ * @author wuyuebin
20+ * @date 2023/5/17 10:35
21+ * @description
22+ */
23+public interface AlertRecordService extends IService<AlertRecord> {
24+ Page<AlertRecordDto> getListPage(AlertRecordReq alertRecordReq, Page page);
25+ 
26+ AlertStatisticsDto alertRecordStatistics(AlertStatisticsReq alertStatisticsReq);
27+ 
28+ String markAsRead(String ids);
29+ 
30+ AlertRecordDto getById(Long id);
31+ 
32+ List<AlertRelationDto> getRelationData(Long id);
33+ 
34+ String markAsUnread(String ids);
35+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertRuleService.java+29-0
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import com.baomidou.mybatisplus.extension.service.IService;
9+import org.opengauss.plugin.alertcenter.dto.AlertRuleDto;
10+import org.opengauss.plugin.alertcenter.dto.RuleItemPropertyDto;
11+import org.opengauss.plugin.alertcenter.entity.AlertRule;
12+import org.opengauss.plugin.alertcenter.model.RuleReq;
13+ 
14+import java.util.List;
15+ 
16+/**
17+ * @author wuyuebin
18+ * @date 2023/5/9 10:08
19+ * @description
20+ */
21+public interface AlertRuleService extends IService<AlertRule> {
22+ Page<AlertRuleDto> getRulePage(RuleReq ruleReq, Page page);
23+ 
24+ AlertRule getRuleById(Long id);
25+ 
26+ List<RuleItemPropertyDto> getRuleItemProperties();
27+ 
28+ List<AlertRuleDto> getRuleList();
29+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertTemplateRuleItemParamService.java+16-0
@@ -0,0 +1,16 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.service.IService;
8+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItemParam;
9+ 
10+/**
11+ * @author wuyuebin
12+ * @date 2023/6/5 19:24
13+ * @description
14+ */
15+public interface AlertTemplateRuleItemParamService extends IService<AlertTemplateRuleItemParam> {
16+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertTemplateRuleItemService.java+19-0
@@ -0,0 +1,19 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.service.IService;
8+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
9+ 
10+import java.util.List;
11+ 
12+/**
13+ * @author wuyuebin
14+ * @date 2023/5/24 10:40
15+ * @description
16+ */
17+public interface AlertTemplateRuleItemService extends IService<AlertTemplateRuleItem> {
18+ void saveOrUpdateList(List<AlertTemplateRuleItem> templateRuleItems);
19+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertTemplateRuleService.java+24-0
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.service.IService;
8+import org.opengauss.plugin.alertcenter.dto.AlertTemplateRuleDto;
9+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
10+ 
11+import java.util.List;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/5/24 10:31
16+ * @description
17+ */
18+public interface AlertTemplateRuleService extends IService<AlertTemplateRule> {
19+ AlertTemplateRule getTemplateRule(Long templateRuleId);
20+ 
21+ AlertTemplateRuleDto saveTemplateRule(AlertTemplateRule alertTemplateRule);
22+ 
23+ List<AlertTemplateRuleDto> getDtoListByTemplateId(Long templateId);
24+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/AlertTemplateService.java+35-0
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import com.baomidou.mybatisplus.extension.service.IService;
9+import org.opengauss.plugin.alertcenter.dto.AlertTemplateDto;
10+import org.opengauss.plugin.alertcenter.dto.AlertTemplateRuleDto;
11+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
12+import org.opengauss.plugin.alertcenter.model.AlertTemplateReq;
13+ 
14+import java.util.List;
15+ 
16+/**
17+ * @author wuyuebin
18+ * @date 2023/5/12 11:40
19+ * @description
20+ */
21+public interface AlertTemplateService extends IService<AlertTemplate> {
22+ Page<AlertTemplate> getTemplatePage(String templateName, Page page);
23+ 
24+ Page<AlertTemplateRuleDto> getTemplateRulePage(Long templateId, String ruleName, Page page);
25+ 
26+ AlertTemplateDto getTemplate(Long id);
27+ 
28+ AlertTemplate saveTemplate(AlertTemplateReq templateReq);
29+ 
30+ List<AlertTemplate> getTemplateList();
31+ 
32+ List<AlertTemplateRuleDto> getTemplateRuleListById(Long templateId);
33+ 
34+ void delTemplate(Long id);
35+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/EmailService.java+183-0
@@ -0,0 +1,183 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
9+import jakarta.mail.Authenticator;
10+import jakarta.mail.Message;
11+import jakarta.mail.MessagingException;
12+import jakarta.mail.PasswordAuthentication;
13+import jakarta.mail.Session;
14+import jakarta.mail.Transport;
15+import jakarta.mail.internet.AddressException;
16+import jakarta.mail.internet.InternetAddress;
17+import jakarta.mail.internet.MimeMessage;
18+import jakarta.mail.internet.MimeUtility;
19+import lombok.extern.slf4j.Slf4j;
20+import org.opengauss.admin.common.exception.ServiceException;
21+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
22+import org.opengauss.plugin.alertcenter.entity.NotifyConfig;
23+import org.opengauss.plugin.alertcenter.entity.NotifyMessage;
24+import org.opengauss.plugin.alertcenter.mapper.NotifyConfigMapper;
25+import org.opengauss.plugin.alertcenter.mapper.NotifyMessageMapper;
26+import org.springframework.beans.factory.annotation.Autowired;
27+import org.springframework.stereotype.Service;
28+ 
29+import java.io.UnsupportedEncodingException;
30+import java.util.List;
31+import java.util.Properties;
32+import java.util.stream.Stream;
33+ 
34+/**
35+ * @author wuyuebin
36+ * @date 2023/5/7 13:24
37+ * @description
38+ */
39+@Service
40+@Slf4j
41+public class EmailService {
42+ @Autowired
43+ private NotifyConfigMapper notifyConfigMapper;
44+ 
45+ @Autowired
46+ private NotifyMessageMapper notifyMessageMapper;
47+ 
48+ private Properties applyProperties(NotifyConfig notifyConfig) {
49+ Properties properties = new Properties();
50+ // 邮件服务器
51+ properties.setProperty("mail.smtp.host", notifyConfig.getSever());
52+ // 端口号
53+ properties.setProperty("mail.smtp.port", notifyConfig.getPort() + "");
54+ // 需要身份验证
55+ properties.setProperty("mail.smtp.auth", "true");
56+ // 发送邮件协议
57+ properties.setProperty("mail.transport.protocol", "smtp");
58+ properties.setProperty("mail.user", notifyConfig.getSender());
59+ properties.setProperty("mail.from", notifyConfig.getAccount());
60+ return properties;
61+ }
62+ 
63+ public void send(NotifyMessage notifyMessage) {
64+ if (notifyMessage == null) {
65+ return;
66+ }
67+ List<NotifyConfig> emailConfigList = notifyConfigMapper.selectList(
68+ Wrappers.<NotifyConfig>lambdaQuery().eq(NotifyConfig::getEnable, 1).eq(NotifyConfig::getType, "email"));
69+ if (CollectionUtil.isEmpty(emailConfigList)) {
70+ return;
71+ }
72+ NotifyConfig notifyConfig = emailConfigList.get(0);
73+ Properties properties = this.applyProperties(notifyConfig);
74+ try {
75+ Session session = Session.getInstance(properties, new Authenticator() {
76+ @Override
77+ protected PasswordAuthentication getPasswordAuthentication() {
78+ return new PasswordAuthentication(notifyConfig.getAccount(), notifyConfig.getPasswd());
79+ }
80+ });
81+ MimeMessage mimeMessage = new MimeMessage(session);
82+ String[] receiverArr = notifyMessage.getEmail().split(",");
83+ InternetAddress[] addresses = Stream.of(receiverArr).map(item -> {
84+ try {
85+ return new InternetAddress(item);
86+ } catch (AddressException e) {
87+ throw new ServiceException("receiver exception:" + e.getMessage());
88+ }
89+ }).toArray(InternetAddress[]::new);
90+ mimeMessage.setFrom(new InternetAddress(
91+ MimeUtility.encodeWord(notifyConfig.getSender()) + " <" + notifyConfig.getAccount() + ">"));
92+ mimeMessage.setRecipients(Message.RecipientType.TO, addresses);
93+ mimeMessage.setSubject(notifyMessage.getTitle());
94+ mimeMessage.setContent(
95+ "<div style='white-space: pre-line;'> " + notifyMessage.getContent().replaceAll(
96+ CommonConstants.LINE_FEED, "<br/>") + "</div>", "text/html;charset=UTF-8");
97+ Transport.send(mimeMessage);
98+ notifyMessage.setStatus(1);
99+ notifyMessageMapper.updateById(notifyMessage);
100+ } catch (MessagingException | UnsupportedEncodingException | ServiceException e) {
101+ notifyMessage.setStatus(2);
102+ notifyMessageMapper.updateById(notifyMessage);
103+ log.error("the email send fail:", e);
104+ }
105+ }
106+ 
107+ public void send(List<NotifyMessage> notifyMessageList) {
108+ if (CollectionUtil.isEmpty(notifyMessageList)) {
109+ return;
110+ }
111+ List<NotifyConfig> emailConfigList = notifyConfigMapper.selectList(
112+ Wrappers.<NotifyConfig>lambdaQuery().eq(NotifyConfig::getEnable, 1).eq(NotifyConfig::getType, "email"));
113+ if (CollectionUtil.isEmpty(emailConfigList)) {
114+ return;
115+ }
116+ NotifyConfig notifyConfig = emailConfigList.get(0);
117+ Properties properties = this.applyProperties(notifyConfig);
118+ for (NotifyMessage notifyMessage : notifyMessageList) {
119+ try {
120+ Session session = Session.getInstance(properties, new Authenticator() {
121+ @Override
122+ protected PasswordAuthentication getPasswordAuthentication() {
123+ return new PasswordAuthentication(notifyConfig.getAccount(), notifyConfig.getPasswd());
124+ }
125+ });
126+ MimeMessage mimeMessage = new MimeMessage(session);
127+ String[] receiverArr = notifyMessage.getEmail().split(",");
128+ InternetAddress[] addresses = Stream.of(receiverArr).map(item -> {
129+ try {
130+ return new InternetAddress(item);
131+ } catch (AddressException e) {
132+ throw new ServiceException("receiver exception:" + e.getMessage());
133+ }
134+ }).toArray(InternetAddress[]::new);
135+ mimeMessage.setFrom(new InternetAddress(
136+ MimeUtility.encodeWord(notifyConfig.getSender()) + " <" + notifyConfig.getAccount() + ">"));
137+ mimeMessage.setRecipients(Message.RecipientType.TO, addresses);
138+ mimeMessage.setSubject(notifyMessage.getTitle());
139+ mimeMessage.setContent(
140+ "<div style='white-space: pre-line;'> " + notifyMessage.getContent().replaceAll(
141+ CommonConstants.LINE_FEED, "<br/>") + "</div>", "text/html;charset=UTF-8");
142+ Transport.send(mimeMessage);
143+ notifyMessage.setStatus(1);
144+ notifyMessageMapper.updateById(notifyMessage);
145+ } catch (MessagingException | UnsupportedEncodingException | ServiceException e) {
146+ notifyMessage.setStatus(2);
147+ notifyMessageMapper.updateById(notifyMessage);
148+ log.error("the email send fail:", e);
149+ }
150+ }
151+ }
152+ 
153+ public void sendTest(NotifyConfig notifyConfig, String receiver, String title, String content) {
154+ Properties properties = this.applyProperties(notifyConfig);
155+ try {
156+ Session session = Session.getInstance(properties, new Authenticator() {
157+ @Override
158+ protected PasswordAuthentication getPasswordAuthentication() {
159+ return new PasswordAuthentication(notifyConfig.getAccount(), notifyConfig.getPasswd());
160+ }
161+ });
162+ MimeMessage mimeMessage = new MimeMessage(session);
163+ String[] receiverArr = receiver.split(",");
164+ InternetAddress[] addresses = Stream.of(receiverArr).map(item -> {
165+ try {
166+ return new InternetAddress(item);
167+ } catch (AddressException e) {
168+ throw new ServiceException("receiver exception:" + e.getMessage());
169+ }
170+ }).toArray(InternetAddress[]::new);
171+ mimeMessage.setFrom(new InternetAddress(
172+ MimeUtility.encodeWord(notifyConfig.getSender()) + " <" + notifyConfig.getAccount() + ">"));
173+ mimeMessage.setRecipients(Message.RecipientType.TO, addresses);
174+ mimeMessage.setSubject(title);
175+ mimeMessage.setContent(
176+ "<div style='white-space: pre-line;'> " + content.replaceAll(CommonConstants.LINE_FEED, "<br/>")
177+ + "</div>", "text/html;charset=UTF-8");
178+ Transport.send(mimeMessage);
179+ } catch (MessagingException | UnsupportedEncodingException | ServiceException e) {
180+ throw new ServiceException("test sentTest exception:" + e.getMessage());
181+ }
182+ }
183+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/EnvironmentService.java+73-0
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
9+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
10+import org.opengauss.admin.common.exception.ServiceException;
11+import org.opengauss.admin.system.plugin.facade.OpsFacade;
12+import org.opengauss.plugin.alertcenter.config.annotation.AlertContentParam;
13+import org.opengauss.plugin.alertcenter.dto.AlertContentParamDto;
14+import org.opengauss.plugin.alertcenter.entity.NctigbaEnv;
15+import org.opengauss.plugin.alertcenter.mapper.NctigbaEnvMapper;
16+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
17+import org.springframework.beans.factory.annotation.Autowired;
18+import org.springframework.stereotype.Service;
19+ 
20+import java.lang.reflect.Field;
21+import java.util.Arrays;
22+import java.util.HashMap;
23+import java.util.List;
24+import java.util.Map;
25+ 
26+/**
27+ * @author wuyuebin
28+ * @date 2023/6/16 15:42
29+ * @description
30+ */
31+@Service
32+public class EnvironmentService {
33+ @Autowired
34+ private NctigbaEnvMapper envMapper;
35+ 
36+ @Autowired
37+ @AutowiredType(AutowiredType.Type.MAIN_PLUGIN)
38+ private OpsFacade opsFacade;
39+ 
40+ public List cluster() {
41+ return opsFacade.listCluster();
42+ }
43+ 
44+ public void checkPrometheus() {
45+ List<NctigbaEnv> env = envMapper
46+ .selectList(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, NctigbaEnv.Type.PROMETHEUS));
47+ if (CollectionUtil.isEmpty(env)) {
48+ throw new ServiceException("the promethues is uninstall");
49+ }
50+ }
51+ 
52+ public Map<String, Map<String, String>> getAlertContentParam(String type) {
53+ MessageSourceUtil.reset();
54+ Map<String, Map<String, String>> map = new HashMap<>();
55+ Class<AlertContentParamDto> clazz = AlertContentParamDto.class;
56+ Field[] declaredFields = clazz.getDeclaredFields();
57+ for (Field field : declaredFields) {
58+ if (field.isAnnotationPresent(AlertContentParam.class)) {
59+ Map<String, String> param = new HashMap<>();
60+ AlertContentParam annotation = field.getAnnotation(AlertContentParam.class);
61+ List<String> groups = Arrays.asList(annotation.group());
62+ if (!groups.contains(type)) {
63+ continue;
64+ }
65+ boolean isI18nPreVal = annotation.isI18nPreVal();
66+ param.put("name", MessageSourceUtil.get(annotation.name()));
67+ param.put("preVal", isI18nPreVal ? MessageSourceUtil.get(annotation.preVal()) : annotation.preVal());
68+ map.put(field.getName(), param);
69+ }
70+ }
71+ return map;
72+ }
73+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/NotifyConfigService.java+24-0
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.service.IService;
8+import org.opengauss.plugin.alertcenter.entity.NotifyConfig;
9+import org.opengauss.plugin.alertcenter.model.NotifyConfigReq;
10+ 
11+import java.util.List;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/6/1 01:46
16+ * @description
17+ */
18+public interface NotifyConfigService extends IService<NotifyConfig> {
19+ List<NotifyConfig> getAllList();
20+ 
21+ void saveList(List<NotifyConfig> list);
22+ 
23+ void testConfig(NotifyConfigReq notifyConfigReq);
24+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/NotifyTemplateService.java+26-0
@@ -0,0 +1,26 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service;
6+ 
7+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8+import com.baomidou.mybatisplus.extension.service.IService;
9+import org.opengauss.plugin.alertcenter.entity.NotifyTemplate;
10+ 
11+import java.util.List;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/5/31 10:16
16+ * @description
17+ */
18+public interface NotifyTemplateService extends IService<NotifyTemplate> {
19+ Page getListPage(String notifyTemplateName, String notifyTemplateType, Page page);
20+ 
21+ void saveTemplate(NotifyTemplate notifyTemplate);
22+ 
23+ void delById(Long id);
24+ 
25+ List<NotifyTemplate> getList(String notifyTemplateType);
26+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/NotifyWayService.java+28-0
@@ -0,0 +1,28 @@
1+ 
2+/*
3+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
4+ */
5+ 
6+package org.opengauss.plugin.alertcenter.service;
7+ 
8+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
9+import com.baomidou.mybatisplus.extension.service.IService;
10+import org.opengauss.plugin.alertcenter.dto.NotifyWayDto;
11+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
12+ 
13+import java.util.List;
14+ 
15+/**
16+ * @author wuyuebin
17+ * @date 2023/5/25 15:52
18+ * @description
19+ */
20+public interface NotifyWayService extends IService<NotifyWay> {
21+ List<NotifyWay> getList(String notifyType);
22+ 
23+ Page<NotifyWayDto> getListPage(String name, String notifyType, Page page);
24+ 
25+ void saveNotifyWay(NotifyWay notifyWay);
26+ 
27+ void delById(Long id);
28+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/PrometheusService.java+516-0
@@ -0,0 +1,516 @@
1+ 
2+/*
3+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
4+ */
5+ 
6+package org.opengauss.plugin.alertcenter.service;
7+ 
8+import cn.hutool.core.collection.CollectionUtil;
9+import cn.hutool.core.io.FileUtil;
10+import cn.hutool.core.util.StrUtil;
11+import cn.hutool.http.HttpUtil;
12+import cn.hutool.json.JSONArray;
13+import cn.hutool.json.JSONObject;
14+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
15+import com.fasterxml.jackson.databind.ObjectMapper;
16+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
17+import lombok.extern.slf4j.Slf4j;
18+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
19+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity;
20+import org.opengauss.admin.common.exception.ServiceException;
21+import org.opengauss.admin.system.plugin.facade.HostFacade;
22+import org.opengauss.admin.system.plugin.facade.HostUserFacade;
23+import org.opengauss.admin.system.service.ops.impl.EncryptionUtils;
24+import org.opengauss.plugin.alertcenter.config.properties.AlertProperty;
25+import org.opengauss.plugin.alertcenter.config.properties.AlertmanagerProperty;
26+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
27+import org.opengauss.plugin.alertcenter.dto.AlertRuleConfigDto;
28+import org.opengauss.plugin.alertcenter.dto.PrometheusConfigDto;
29+import org.opengauss.plugin.alertcenter.dto.PrometheusEnvDto;
30+import org.opengauss.plugin.alertcenter.entity.AlertClusterNodeConf;
31+import org.opengauss.plugin.alertcenter.entity.AlertConfig;
32+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
33+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
34+import org.opengauss.plugin.alertcenter.entity.NctigbaEnv;
35+import org.opengauss.plugin.alertcenter.mapper.AlertClusterNodeConfMapper;
36+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemMapper;
37+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleMapper;
38+import org.opengauss.plugin.alertcenter.mapper.NctigbaEnvMapper;
39+import org.opengauss.plugin.alertcenter.utils.SshSession;
40+import org.opengauss.plugin.alertcenter.utils.YamlUtil;
41+import org.springframework.beans.factory.annotation.Autowired;
42+import org.springframework.stereotype.Service;
43+import org.springframework.util.CollectionUtils;
44+ 
45+import java.io.File;
46+import java.io.IOException;
47+import java.math.BigDecimal;
48+import java.nio.file.Files;
49+import java.time.Instant;
50+import java.time.LocalDateTime;
51+import java.time.ZoneOffset;
52+import java.util.ArrayList;
53+import java.util.Arrays;
54+import java.util.Collection;
55+import java.util.HashMap;
56+import java.util.List;
57+import java.util.Map;
58+import java.util.stream.Collectors;
59+ 
60+/**
61+ * @author wuyuebin
62+ * @date 2023/5/19 09:50
63+ * @description 用于请求prometheus数据或者修改其配置
64+ */
65+@Service
66+@Slf4j
67+public class PrometheusService {
68+ @Autowired
69+ private AlertTemplateRuleMapper alertTemplateRuleMapper;
70+ 
71+ @Autowired
72+ private AlertTemplateRuleItemMapper alertTemplateRuleItemMapper;
73+ 
74+ @Autowired
75+ private NctigbaEnvMapper envMapper;
76+ 
77+ @Autowired
78+ private AlertProperty alertProperty;
79+ 
80+ @Autowired
81+ private AlertConfigService alertConfigService;
82+ 
83+ @Autowired
84+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
85+ private EncryptionUtils encryptionUtils;
86+ 
87+ @Autowired
88+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
89+ private HostFacade hostFacade;
90+ 
91+ @Autowired
92+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
93+ private HostUserFacade hostUserFacade;
94+ 
95+ @Autowired
96+ private AlertClusterNodeConfMapper clusterNodeConfMapper;
97+ 
98+ private PrometheusEnvDto prometheusEnvDto;
99+ 
100+ private ObjectMapper objectMapper = new ObjectMapper();
101+ 
102+ private void initPrometheusEnvDto() {
103+ List<AlertConfig> alertConfigs = alertConfigService.list();
104+ if (prometheusEnvDto == null) {
105+ prometheusEnvDto = new PrometheusEnvDto();
106+ }
107+ prometheusEnvDto.setConfigList(alertConfigs);
108+ NctigbaEnv promEnv = envMapper.selectOne(
109+ Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, NctigbaEnv.Type.PROMETHEUS));
110+ if (promEnv == null) {
111+ throw new ServiceException("uninstall the prometheus");
112+ }
113+ if (StrUtil.isBlank(promEnv.getPath())) {
114+ throw new ServiceException("uninstall the prometheus");
115+ }
116+ if (StrUtil.isBlank(promEnv.getHostid())) {
117+ throw new ServiceException("host id is unknown");
118+ }
119+ OpsHostEntity promeHost = hostFacade.getById(promEnv.getHostid());
120+ List<OpsHostUserEntity> hostUserList = hostUserFacade.listHostUserByHostId(promEnv.getHostid());
121+ if (CollectionUtil.isEmpty(hostUserList)) {
122+ throw new ServiceException("host user is null");
123+ }
124+ List<OpsHostUserEntity> promUserList = hostUserList.stream().filter(
125+ item -> item.getUsername().equals(promEnv.getUsername())).collect(Collectors.toList());
126+ if (CollectionUtil.isEmpty(promUserList)) {
127+ throw new ServiceException("the host user Prometheus is not exist");
128+ }
129+ OpsHostUserEntity promUser = promUserList.get(0);
130+ // The code comments need to be uncommented in the development environment: encryptionUtils.refreshKeyPair()
131+ prometheusEnvDto.setPromIp(promeHost.getPublicIp()).setHostPort(promeHost.getPort()).setPromPort(
132+ promEnv.getPort()).setPromUsername(promUser.getUsername()).setPromPasswd(
133+ encryptionUtils.decrypt(promUser.getPassword())).setPath(promEnv.getPath());
134+ }
135+ 
136+ private PrometheusConfigDto getPromConfig() {
137+ // String promYmlStr = session.execute("cat " + promEnv.getPath() + CommonConstants.PROMETHEUS_YML);
138+ String promYmlStr = "";
139+ String url = "http://" + prometheusEnvDto.getPromIp() + ":" + prometheusEnvDto.getPromPort() + "/api/v1/status"
140+ + "/config";
141+ String result = HttpUtil.get(url);
142+ if (StrUtil.isNotBlank(result)) {
143+ JSONObject resultJson = new JSONObject(result);
144+ String status = resultJson.getStr("status", "");
145+ if (status.equals("success")) {
146+ JSONObject data = resultJson.getJSONObject("data");
147+ promYmlStr = data.getStr("yaml");
148+ }
149+ }
150+ HashMap map = YamlUtil.loadAs(promYmlStr, HashMap.class);
151+ if (map == null) {
152+ throw new NullPointerException("the prometheus config is null");
153+ }
154+ return objectMapper.convertValue(map, PrometheusConfigDto.class);
155+ }
156+ 
157+ private void updatePromConfig(SshSession session, PrometheusConfigDto config) throws IOException {
158+ List<String> ruleFilesConf = config.getRuleFiles();
159+ String ruleFilePrefix = alertProperty.getRuleFilePrefix();
160+ String ruleFileSuffix = alertProperty.getRuleFileSuffix();
161+ String ruleFilePath =
162+ prometheusEnvDto.getPath() + CommonConstants.SLASH + ruleFilePrefix + "*" + ruleFileSuffix;
163+ boolean isConfigChange = false;
164+ if (CollectionUtils.isEmpty(ruleFilesConf)) {
165+ ruleFilesConf = new ArrayList<>();
166+ config.setRuleFiles(ruleFilesConf);
167+ ruleFilesConf.add(ruleFilePath);
168+ isConfigChange = true;
169+ }
170+ if (!ruleFilesConf.contains(ruleFilePath)) {
171+ ruleFilesConf.add(ruleFilePath);
172+ isConfigChange = true;
173+ }
174+ isConfigChange |= checkAndUpdatePromAlertConfig(config);
175+ 
176+ if (isConfigChange) {
177+ uploadFileConfig(session, config);
178+ }
179+ }
180+ 
181+ private boolean checkAndUpdatePromAlertConfig(PrometheusConfigDto config) {
182+ PrometheusConfigDto.Alert alerting = config.getAlerting();
183+ if (alerting == null) {
184+ alerting = new PrometheusConfigDto.Alert();
185+ }
186+ AlertmanagerProperty alertmanagerPro = alertProperty.getAlertmanager();
187+ String apiVersion = alertmanagerPro.getApiVersion();
188+ String pathPrefix = alertmanagerPro.getPathPrefix();
189+ List<PrometheusConfigDto.Alert.Alertmanager.Conf> staticConfigs = new ArrayList<>();
190+ PrometheusConfigDto.Alert.Alertmanager.Conf confNew = new PrometheusConfigDto.Alert.Alertmanager.Conf();
191+ List<String> targets = prometheusEnvDto.getConfigList().stream().map(
192+ item -> item.getAlertIp() + ":" + item.getAlertPort()).collect(Collectors.toList());
193+ confNew.setTargets(targets);
194+ staticConfigs.add(confNew);
195+ PrometheusConfigDto.Alert.Alertmanager alertmanagerNew = new PrometheusConfigDto.Alert.Alertmanager();
196+ alertmanagerNew.setPathPrefix(pathPrefix);
197+ alertmanagerNew.setApiVersion(apiVersion);
198+ alertmanagerNew.setStaticConfigs(staticConfigs);
199+ alertmanagerNew.setScheme("http");
200+ alertmanagerNew.setIsFollowRedirects(true);
201+ alertmanagerNew.setTimeout("10s");
202+ alertmanagerNew.setIsEnableHttp2(true);
203+ List<PrometheusConfigDto.Alert.Alertmanager> alertmanagers = alerting.getAlertmanagers();
204+ if (CollectionUtils.isEmpty(alertmanagers)) {
205+ alertmanagers = new ArrayList<PrometheusConfigDto.Alert.Alertmanager>();
206+ alertmanagers.add(alertmanagerNew);
207+ return true;
208+ } else {
209+ PrometheusConfigDto.Alert.Alertmanager alertmanager = alertmanagers.get(0);
210+ if (!alertmanager.toString().equals(alertmanagerNew.toString())) {
211+ List<PrometheusConfigDto.Alert.Alertmanager.Conf> staticConfigs0 = alertmanager.getStaticConfigs();
212+ if (CollectionUtil.isNotEmpty(staticConfigs0)) {
213+ List<String> targets0 = staticConfigs0.get(0).getTargets();
214+ List<String> delTargets = CollectionUtil.isNotEmpty(
215+ prometheusEnvDto.getDelConfigList()) ? prometheusEnvDto.getDelConfigList().stream().map(
216+ item -> item.getAlertIp() + ":" + item.getAlertPort()).collect(
217+ Collectors.toList()) : new ArrayList<>();
218+ targets0 = targets0.stream().filter(
219+ item -> !delTargets.contains(item) || !targets.contains(item)).collect(Collectors.toList());
220+ targets.addAll(targets0);
221+ }
222+ alertmanagers.set(0, alertmanagerNew);
223+ return true;
224+ }
225+ }
226+ return false;
227+ }
228+ 
229+ private void uploadFileConfig(SshSession session, Object config) throws IOException {
230+ HashMap configMap = objectMapper.convertValue(config, HashMap.class);
231+ File prometheusConfigFile = File.createTempFile("prom", ".tmp");
232+ FileUtil.appendUtf8String(YamlUtil.dump(configMap), prometheusConfigFile);
233+ session.execute("rm " + prometheusEnvDto.getPath() + CommonConstants.PROMETHEUS_YML);
234+ session.upload(prometheusConfigFile.getAbsolutePath(),
235+ prometheusEnvDto.getPath() + CommonConstants.PROMETHEUS_YML);
236+ Files.delete(prometheusConfigFile.toPath());
237+ String res = HttpUtil.post(
238+ "http://" + prometheusEnvDto.getPromIp() + ":" + +prometheusEnvDto.getPromPort() + "/-/reload", "");
239+ log.info(res);
240+ }
241+ 
242+ public void initPrometheusConfig() {
243+ try {
244+ initPrometheusEnvDto();
245+ List<AlertConfig> alertConfigs = prometheusEnvDto.getConfigList();
246+ if (CollectionUtil.isEmpty(alertConfigs)) {
247+ return;
248+ }
249+ SshSession session = SshSession.connect(prometheusEnvDto.getPromIp(), prometheusEnvDto.getHostPort(),
250+ prometheusEnvDto.getPromUsername(), prometheusEnvDto.getPromPasswd());
251+ PrometheusConfigDto config = getPromConfig();
252+ updatePromConfig(session, config);
253+ session.close();
254+ } catch (IOException e) {
255+ log.error("init prometheus configuration fail: ", e);
256+ }
257+ }
258+ 
259+ /**
260+ * Get data from a Prometheus server based on promQL and time
261+ *
262+ * @param url http://IP:9090/api/v1/query_range?
263+ * @param port 9090
264+ * @param query query=promQL &start=1683316800 &end=1683489600 &step=691
265+ *
266+ * step = Math.max(Math.floor(60 * 60 * 1000 / 250000), 1)
267+ * 默认1个小时,this.props.options.range=60 * 60 * 1000
268+ *
269+ * @param startTime startTime = endTime - this.props.options.range / 1000
270+ * @param endTime end = this.getEndTime().valueOf() / 1000
271+ * @return {"status":"success","data":{"resultType":"matrix",
272+ * "result":[{"metric":{"instance":"6bd124fa-4a28-4f29-82a9-a81c880a5b32"},
273+ * "values":[[1683472020,"1.5090909090907871"],[1683472365,"1.418181818182191"],
274+ * [1683472710,"1.418181818182191"],[1683473055,"1.5090909090907871"],[1683473400,"1.4909090909085592"],[
275+ * 1683473745,"1.3454545454549844"],[1683474090,"1.3272727272727707"],[1683474435,"1.5454545454535662"],
276+ * [1683474780,"1.2181818181819466"],[1683475125,"1.2545454545463883"],[1683475470,"1.399999999999153"],
277+ * [1683475815,"1.87272727272682"],[1683476160,"1.836363636364041"],[1683476505,"100"],[1683476850,"100"],
278+ * [1683477195,"1.45454545454578"],[1683477540,"1.5636363636374284"],[1683477885,"1.5454545454535662"],
279+ * [1683478230,"1.3272727272735807"],[1683478575,"1.7454545454546206"],[1683478920,"1.472727272728008"],
280+ * [1683479265,"1.4909090909085592"]]}]}}
281+ */
282+ public Number[][] queryRange(
283+ String url, String port, String query, LocalDateTime startTime,
284+ LocalDateTime endTime) {
285+ Instant startInstant = startTime.toInstant(ZoneOffset.of("+8"));
286+ Instant endInstant = endTime.toInstant(ZoneOffset.of("+8"));
287+ int step = Math.max(Long.valueOf((endInstant.toEpochMilli() - startInstant.toEpochMilli()) / 250000).intValue(),
288+ 1);
289+ long start = startInstant.getEpochSecond();
290+ long end = endInstant.getEpochSecond();
291+ Map<String, Object> paramMap = new HashMap<>();
292+ paramMap.put("query", query);
293+ paramMap.put("start", start);
294+ paramMap.put("end", end);
295+ paramMap.put("step", step);
296+ String httpUrl = "http://" + url + ":" + port + "/api/v1/query_range";
297+ String res = HttpUtil.get(httpUrl, paramMap);
298+ if (StrUtil.isBlank(res)) {
299+ return new Number[0][0];
300+ }
301+ JSONObject resJson = new JSONObject(res);
302+ if (!resJson.getStr("status", "").equals("success")) {
303+ return new Number[0][0];
304+ }
305+ JSONObject data = resJson.getJSONObject("data");
306+ if (data == null) {
307+ return new Number[0][0];
308+ }
309+ JSONArray result = data.getJSONArray("result");
310+ if (CollectionUtil.isEmpty(result)) {
311+ return new Number[0][0];
312+ }
313+ JSONObject metricInfo = result.getJSONObject(0);
314+ if (metricInfo == null) {
315+ return new Number[0][0];
316+ }
317+ JSONArray values = metricInfo.getJSONArray("values");
318+ Number[][] dataResult = new Number[values.size()][2];
319+ for (int i = 0; i < values.size(); i++) {
320+ JSONArray valArr = values.getJSONArray(i);
321+ dataResult[i][0] = valArr.getLong(0) * 1000;
322+ dataResult[i][1] = valArr.getBigDecimal(1).setScale(2, BigDecimal.ROUND_HALF_UP);
323+ }
324+ return dataResult;
325+ }
326+ 
327+ public void updateRuleConfig(Collection<Long> templateIdList) {
328+ if (CollectionUtil.isEmpty(templateIdList)) {
329+ return;
330+ }
331+ initPrometheusEnvDto();
332+ try {
333+ SshSession session = SshSession.connect(prometheusEnvDto.getPromIp(), prometheusEnvDto.getHostPort(),
334+ prometheusEnvDto.getPromUsername(), prometheusEnvDto.getPromPasswd());
335+ for (Long templateId : templateIdList) {
336+ updateRuleConfig(session, templateId);
337+ }
338+ session.close();
339+ } catch (IOException e) {
340+ log.error(e.getMessage(), e);
341+ throw new ServiceException("update the alert rule fail");
342+ }
343+ }
344+ 
345+ 
346+ public void updateRuleConfig(Long templateId) {
347+ if (templateId == null) {
348+ return;
349+ }
350+ // 更新prometheus规则配置文件
351+ try {
352+ initPrometheusEnvDto();
353+ SshSession session = SshSession.connect(prometheusEnvDto.getPromIp(), prometheusEnvDto.getHostPort(),
354+ prometheusEnvDto.getPromUsername(), prometheusEnvDto.getPromPasswd());
355+ updateRuleConfig(session, templateId);
356+ session.close();
357+ } catch (IOException e) {
358+ log.error(e.getMessage(), e);
359+ throw new ServiceException("update the alert rule fail");
360+ }
361+ }
362+ 
363+ private void updateRuleConfig(SshSession session, Long templateId) throws IOException {
364+ List<AlertClusterNodeConf> alertClusterNodeConfs = clusterNodeConfMapper.selectList(
365+ Wrappers.<AlertClusterNodeConf>lambdaQuery().eq(AlertClusterNodeConf::getTemplateId, templateId).eq(
366+ AlertClusterNodeConf::getIsDeleted, CommonConstants.IS_NOT_DELETE));
367+ 
368+ AlertRuleConfigDto config = getAlertRuleConfigDto(session, templateId, alertClusterNodeConfs);
369+ String instances = alertClusterNodeConfs.stream().map(item -> item.getClusterNodeId()).collect(
370+ Collectors.joining("|"));
371+ List<AlertTemplateRule> alertTemplateRules = alertTemplateRuleMapper.selectList(
372+ Wrappers.<AlertTemplateRule>lambdaQuery().eq(AlertTemplateRule::getTemplateId, templateId));
373+ if (checkAndUpdateRuleConfig(instances, config, alertTemplateRules)) {
374+ uploadFileConfig(session, config);
375+ }
376+ }
377+ 
378+ private AlertRuleConfigDto getAlertRuleConfigDto(
379+ SshSession session, Long templateId,
380+ List<AlertClusterNodeConf> alertClusterNodeConfs) throws IOException {
381+ String ruleDir = alertProperty.getRuleFilePrefix().endsWith(CommonConstants.SLASH)
382+ ? alertProperty.getRuleFilePrefix().substring(0,
383+ alertProperty.getRuleFilePrefix().length() - 1) : alertProperty.getRuleFilePrefix();
384+ 
385+ String dirFileStr = session.execute("ls " + prometheusEnvDto.getPath());
386+ String[] dirFileArr = dirFileStr.split(CommonConstants.LINE_FEED);
387+ String dir = prometheusEnvDto.getPath() + CommonConstants.SLASH + alertProperty.getRuleFilePrefix();
388+ AlertRuleConfigDto config = new AlertRuleConfigDto();
389+ if (!Arrays.asList(dirFileArr).contains(ruleDir)) {
390+ if (CollectionUtil.isEmpty(alertClusterNodeConfs)) {
391+ return config;
392+ }
393+ session.execute("mkdir " + dir);
394+ }
395+ String ruleFileStr = session.execute("ls " + dir);
396+ String fileName = "rule_template_" + templateId + alertProperty.getRuleFileSuffix();
397+ String path = dir + fileName;
398+ if (StrUtil.isBlank(ruleFileStr)) {
399+ if (CollectionUtil.isEmpty(alertClusterNodeConfs)) {
400+ return config;
401+ }
402+ session.execute("touch " + path);
403+ } else {
404+ String[] ruleFileArr = ruleFileStr.split(CommonConstants.LINE_FEED);
405+ if (Arrays.asList(ruleFileArr).contains(fileName)) {
406+ if (CollectionUtil.isEmpty(alertClusterNodeConfs)) {
407+ // delete rule_template_xxx.yml
408+ session.execute("rm -f " + path);
409+ HttpUtil.post("http://" + prometheusEnvDto.getPromIp() + ":" + +prometheusEnvDto.getPromPort()
410+ + "/-/reload", "");
411+ return config;
412+ }
413+ } else {
414+ session.execute("touch " + path);
415+ }
416+ }
417+ 
418+ String ruleYmlStr = session.execute("cat " + path);
419+ Map map = YamlUtil.loadAs(ruleYmlStr, HashMap.class);
420+ if (map != null) {
421+ config = objectMapper.convertValue(map, AlertRuleConfigDto.class);
422+ }
423+ return config;
424+ }
425+ 
426+ private boolean checkAndUpdateRuleConfig(
427+ String instances, AlertRuleConfigDto config,
428+ List<AlertTemplateRule> alertTemplateRules) {
429+ List<AlertRuleConfigDto.Group> groups = config.getGroups();
430+ if (groups == null) {
431+ groups = new ArrayList<>();
432+ config.setGroups(groups);
433+ }
434+ boolean isConfigChange = false;
435+ for (AlertTemplateRule alertTemplateRule : alertTemplateRules) {
436+ AlertRuleConfigDto.Group groupNew = templateRuleToPromRuleGroup(alertTemplateRule, instances);
437+ if (groups.size() == 0) {
438+ groups.add(groupNew);
439+ isConfigChange = true;
440+ continue;
441+ }
442+ List<String> groupNames = groups.stream().map(item -> item.getName()).collect(Collectors.toList());
443+ if (!groupNames.contains(groupNew.getName())) {
444+ groups.add(groupNew);
445+ isConfigChange = true;
446+ continue;
447+ }
448+ for (int i = 0; i < groups.size(); i++) {
449+ AlertRuleConfigDto.Group group = groups.get(i);
450+ if (group.getName().equals(groupNew.getName()) && !group.toString().equals(groupNew.toString())) {
451+ groups.set(i, groupNew);
452+ isConfigChange = true;
453+ break;
454+ }
455+ }
456+ }
457+ return isConfigChange;
458+ }
459+ 
460+ private AlertRuleConfigDto.Group templateRuleToPromRuleGroup(
461+ AlertTemplateRule alertTemplateRule,
462+ String instances) {
463+ List<AlertTemplateRuleItem> alertTemplateRuleItems = alertTemplateRuleItemMapper.selectList(
464+ Wrappers.<AlertTemplateRuleItem>lambdaQuery().eq(AlertTemplateRuleItem::getTemplateRuleId,
465+ alertTemplateRule.getId()));
466+ String ruleExpComb = alertTemplateRule.getRuleExpComb();
467+ for (AlertTemplateRuleItem alertTemplateRuleItem : alertTemplateRuleItems) {
468+ ruleExpComb = ruleExpComb.replace(alertTemplateRuleItem.getRuleMark(),
469+ alertTemplateRuleItem.getRuleExp() + " " + alertTemplateRuleItem.getOperate()
470+ + alertTemplateRuleItem.getLimitValue());
471+ }
472+ ruleExpComb = ruleExpComb.replaceAll("\\$\\{instances\\}", instances);
473+ AlertRuleConfigDto.Group groupNew = new AlertRuleConfigDto.Group();
474+ String name = "rule_" + alertTemplateRule.getId();
475+ groupNew.setName(name);
476+ AlertRuleConfigDto.Group.Rule rule = new AlertRuleConfigDto.Group.Rule();
477+ rule.setAliasFor(alertTemplateRule.getNotifyDuration() + alertTemplateRule.getNotifyDurationUnit());
478+ rule.setAlert(name);
479+ rule.setExpr(ruleExpComb);
480+ AlertRuleConfigDto.Group.Rule.Labels labels = new AlertRuleConfigDto.Group.Rule.Labels();
481+ labels.setLevel(alertTemplateRule.getLevel());
482+ labels.setTemplateId(alertTemplateRule.getTemplateId());
483+ labels.setTemplateRuleId(alertTemplateRule.getId());
484+ rule.setLabels(labels);
485+ AlertRuleConfigDto.Group.Rule.Annotations annotations = new AlertRuleConfigDto.Group.Rule.Annotations();
486+ annotations.setSummary(alertTemplateRule.getRuleName());
487+ annotations.setDescription(
488+ StrUtil.isBlank(alertTemplateRule.getAlertDesc()) ? "" : alertTemplateRule.getAlertDesc());
489+ rule.setAnnotations(annotations);
490+ List<AlertRuleConfigDto.Group.Rule> rules = new ArrayList<>();
491+ rules.add(rule);
492+ groupNew.setRules(rules);
493+ return groupNew;
494+ }
495+ 
496+ public void updateAlertConfig(AlertConfig alertConfig, List<AlertConfig> delAlertConfigList) {
497+ try {
498+ if (StrUtil.isBlank(alertConfig.getAlertIp()) && StrUtil.isBlank(alertConfig.getAlertPort())) {
499+ throw new ServiceException("The alert IP or the alert Port is not empty!");
500+ }
501+ initPrometheusEnvDto();
502+ List<AlertConfig> configList = new ArrayList<>();
503+ configList.add(alertConfig);
504+ prometheusEnvDto.setConfigList(configList).setDelConfigList(delAlertConfigList);
505+ 
506+ SshSession session = SshSession.connect(prometheusEnvDto.getPromIp(), prometheusEnvDto.getHostPort(),
507+ prometheusEnvDto.getPromUsername(), prometheusEnvDto.getPromPasswd());
508+ PrometheusConfigDto config = getPromConfig();
509+ updatePromConfig(session, config);
510+ session.close();
511+ } catch (IOException | NullPointerException e) {
512+ log.error(e.getMessage(), e);
513+ throw new ServiceException("update the alert config fail");
514+ }
515+ }
516+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertClusterNodeConfServiceImpl.java+186-0
@@ -0,0 +1,186 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.bean.BeanUtil;
8+import cn.hutool.core.collection.CollectionUtil;
9+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
10+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
11+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
12+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
13+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
14+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
15+import org.opengauss.admin.system.plugin.facade.HostFacade;
16+import org.opengauss.admin.system.service.ops.IOpsClusterNodeService;
17+import org.opengauss.admin.system.service.ops.IOpsClusterService;
18+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
19+import org.opengauss.plugin.alertcenter.dto.AlertClusterNodeConfDto;
20+import org.opengauss.plugin.alertcenter.entity.AlertClusterNodeConf;
21+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
22+import org.opengauss.plugin.alertcenter.mapper.AlertClusterNodeConfMapper;
23+import org.opengauss.plugin.alertcenter.mapper.AlertConfigMapper;
24+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateMapper;
25+import org.opengauss.plugin.alertcenter.mapper.NctigbaEnvMapper;
26+import org.opengauss.plugin.alertcenter.model.AlertClusterNodeAndTemplateReq;
27+import org.opengauss.plugin.alertcenter.model.AlertClusterNodeConfReq;
28+import org.opengauss.plugin.alertcenter.model.AlertTemplateReq;
29+import org.opengauss.plugin.alertcenter.service.AlertClusterNodeConfService;
30+import org.opengauss.plugin.alertcenter.service.AlertTemplateService;
31+import org.opengauss.plugin.alertcenter.service.PrometheusService;
32+import org.springframework.beans.factory.annotation.Autowired;
33+import org.springframework.stereotype.Service;
34+import org.springframework.transaction.annotation.Transactional;
35+ 
36+import java.sql.Timestamp;
37+import java.util.ArrayList;
38+import java.util.Arrays;
39+import java.util.HashSet;
40+import java.util.List;
41+import java.util.Set;
42+import java.util.stream.Collectors;
43+ 
44+/**
45+ * @author wuyuebin
46+ * @date 2023/4/26 16:43
47+ * @description
48+ */
49+@Service
50+public class AlertClusterNodeConfServiceImpl
51+ extends ServiceImpl<AlertClusterNodeConfMapper, AlertClusterNodeConf>
52+ implements AlertClusterNodeConfService {
53+ @Autowired
54+ private AlertTemplateMapper templateMapper;
55+ 
56+ @Autowired
57+ private NctigbaEnvMapper envMapper;
58+ 
59+ @Autowired
60+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
61+ private IOpsClusterService clusterService;
62+ 
63+ @Autowired
64+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
65+ private IOpsClusterNodeService clusterNodeService;
66+ 
67+ @Autowired
68+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
69+ private HostFacade hostFacade;
70+ 
71+ @Autowired
72+ private PrometheusService prometheusService;
73+ 
74+ @Autowired
75+ private AlertTemplateService templateService;
76+ 
77+ @Autowired
78+ private AlertConfigMapper alertConfigMapper;
79+ 
80+ @Transactional
81+ public void saveClusterNodeConf(AlertClusterNodeConfReq alertClusterNodeConfReq) {
82+ Long templateId = alertClusterNodeConfReq.getTemplateId();
83+ String clusterNodeIds = alertClusterNodeConfReq.getClusterNodeIds();
84+ String[] clusterNodeIdArr = clusterNodeIds.split(CommonConstants.DELIMITER);
85+ List<String> notNeedToUpdatedNodeIdList = new ArrayList<>();
86+ Set<Long> needUpdatedTemplateIdSet = new HashSet<>();
87+ // 检查是否已经配置模板
88+ List<AlertClusterNodeConf> oldAlertClusterNodeConfList = this.baseMapper.selectList(
89+ Wrappers.<AlertClusterNodeConf>lambdaQuery().in(AlertClusterNodeConf::getClusterNodeId,
90+ clusterNodeIdArr).eq(AlertClusterNodeConf::getIsDeleted, CommonConstants.IS_NOT_DELETE));
91+ if (CollectionUtil.isNotEmpty(oldAlertClusterNodeConfList)) {
92+ // remove the relation between the cluster node and the template
93+ List<AlertClusterNodeConf> delConfList = oldAlertClusterNodeConfList.stream().filter(
94+ item -> !item.getTemplateId().equals(templateId)).map(item -> {
95+ item.setIsDeleted(CommonConstants.IS_DELETE);
96+ return item;
97+ }).collect(Collectors.toList());
98+ if (CollectionUtil.isNotEmpty(delConfList)) {
99+ Set<Long> set = delConfList.stream().map(item -> item.getTemplateId()).collect(Collectors.toSet());
100+ needUpdatedTemplateIdSet.addAll(set);
101+ this.saveOrUpdateBatch(delConfList);
102+ }
103+ // get the cluster node which is not need to update
104+ List<String> nodeIdList = oldAlertClusterNodeConfList.stream().filter(
105+ item -> item.getTemplateId().equals(templateId)).map(item -> item.getClusterNodeId()).collect(
106+ Collectors.toList());
107+ notNeedToUpdatedNodeIdList.addAll(nodeIdList);
108+ }
109+ List<AlertClusterNodeConf> alertClusterNodeConfList = Arrays.stream(clusterNodeIdArr).filter(
110+ item -> !notNeedToUpdatedNodeIdList.contains(item)).map(
111+ item -> new AlertClusterNodeConf().setClusterNodeId(item)
112+ .setTemplateId(alertClusterNodeConfReq.getTemplateId())
113+ .setCreateTime(new Timestamp(System.currentTimeMillis())).setIsDeleted(
114+ CommonConstants.IS_NOT_DELETE)).collect(Collectors.toList());
115+ 
116+ if (CollectionUtil.isEmpty(alertClusterNodeConfList)) {
117+ return;
118+ }
119+ needUpdatedTemplateIdSet.add(templateId);
120+ // 保存数据库
121+ this.saveBatch(alertClusterNodeConfList);
122+ // 更新prometheus规则配置文件
123+ prometheusService.updateRuleConfig(needUpdatedTemplateIdSet);
124+ }
125+ 
126+ @Override
127+ public AlertClusterNodeConf getByClusterNodeId(String clusterNodeId) {
128+ List<AlertClusterNodeConf> alertClusterNodeConfs = this.baseMapper.selectList(
129+ Wrappers.<AlertClusterNodeConf>lambdaQuery().eq(AlertClusterNodeConf::getClusterNodeId,
130+ clusterNodeId).eq(AlertClusterNodeConf::getIsDeleted, CommonConstants.IS_NOT_DELETE));
131+ if (CollectionUtil.isEmpty(alertClusterNodeConfs)) {
132+ return new AlertClusterNodeConf();
133+ }
134+ return alertClusterNodeConfs.get(0);
135+ }
136+ 
137+ @Override
138+ @Transactional
139+ public void saveAlertTemplateAndConfig(AlertClusterNodeAndTemplateReq clusterNodeAndTemplateReq) {
140+ AlertTemplateReq templateReq = new AlertTemplateReq();
141+ BeanUtil.copyProperties(clusterNodeAndTemplateReq, templateReq);
142+ AlertTemplate alertTemplate = templateService.saveTemplate(templateReq);
143+ AlertClusterNodeConfReq alertClusterNodeConfReq = new AlertClusterNodeConfReq();
144+ alertClusterNodeConfReq.setTemplateId(alertTemplate.getId());
145+ alertClusterNodeConfReq.setClusterNodeIds(clusterNodeAndTemplateReq.getClusterNodeIds());
146+ this.saveClusterNodeConf(alertClusterNodeConfReq);
147+ }
148+ 
149+ @Override
150+ public List<AlertClusterNodeConfDto> getList() {
151+ List<AlertClusterNodeConfDto> dtoList = new ArrayList<>();
152+ List<OpsClusterNodeEntity> list = clusterNodeService.list();
153+ Set<String> hostIdSet = list.stream().map(item -> item.getHostId()).collect(Collectors.toSet());
154+ List<OpsHostEntity> opsHostEntities = hostFacade.listByIds(hostIdSet);
155+ List<String> clusterIdList = list.stream().map(item -> item.getClusterId()).collect(Collectors.toList());
156+ List<OpsClusterEntity> opsClusterEntities = clusterService.listByIds(clusterIdList);
157+ List<String> nodeIdList = list.stream().map(item -> item.getClusterNodeId()).collect(Collectors.toList());
158+ List<AlertClusterNodeConf> alertClusterNodeConfs = this.baseMapper.selectList(
159+ Wrappers.<AlertClusterNodeConf>lambdaQuery().in(CollectionUtil.isNotEmpty(nodeIdList),
160+ AlertClusterNodeConf::getClusterNodeId, nodeIdList).eq(AlertClusterNodeConf::getIsDeleted,
161+ CommonConstants.IS_NOT_DELETE));
162+ for (OpsClusterNodeEntity clusterNode : list) {
163+ OpsHostEntity opsHostEntity =
164+ opsHostEntities.stream().filter(
165+ item -> item.getHostId().equals(clusterNode.getHostId())).findFirst().get();
166+ OpsClusterEntity opsClusterEntity =
167+ opsClusterEntities.stream().filter(
168+ item -> item.getClusterId().equals(clusterNode.getClusterId())).findFirst().get();
169+ String nodeName = opsClusterEntity.getClusterId() + "/" + opsHostEntity.getPublicIp() + ":"
170+ + opsClusterEntity.getPort() + "(" + clusterNode.getClusterRole() + ")";
171+ AlertClusterNodeConfDto clusterNodeConfDto = new AlertClusterNodeConfDto();
172+ clusterNodeConfDto.setClusterNodeId(clusterNode.getClusterNodeId()).setNodeName(nodeName);
173+ List<AlertClusterNodeConf> clusterNodeConfs = alertClusterNodeConfs.stream().filter(
174+ item -> item.getClusterNodeId().equals(clusterNode.getClusterNodeId())).collect(
175+ Collectors.toList());
176+ if (CollectionUtil.isNotEmpty(clusterNodeConfs)) {
177+ AlertClusterNodeConf alertClusterNodeConf = clusterNodeConfs.get(0);
178+ AlertTemplate alertTemplate = templateMapper.selectById(alertClusterNodeConf.getTemplateId());
179+ clusterNodeConfDto.setTemplateId(alertClusterNodeConf.getTemplateId()).setTemplateName(
180+ alertTemplate.getTemplateName());
181+ }
182+ dtoList.add(clusterNodeConfDto);
183+ }
184+ return dtoList;
185+ }
186+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertConfigServiceImpl.java+40-0
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
9+import org.opengauss.plugin.alertcenter.entity.AlertConfig;
10+import org.opengauss.plugin.alertcenter.mapper.AlertConfigMapper;
11+import org.opengauss.plugin.alertcenter.service.AlertConfigService;
12+import org.opengauss.plugin.alertcenter.service.PrometheusService;
13+import org.springframework.beans.factory.annotation.Autowired;
14+import org.springframework.stereotype.Service;
15+import org.springframework.transaction.annotation.Transactional;
16+ 
17+import java.util.List;
18+import java.util.stream.Collectors;
19+ 
20+/**
21+ * @author wuyuebin
22+ * @date 2023/6/6 17:18
23+ * @description
24+ */
25+@Service
26+public class AlertConfigServiceImpl extends ServiceImpl<AlertConfigMapper, AlertConfig> implements AlertConfigService {
27+ @Autowired
28+ private PrometheusService prometheusService;
29+ 
30+ @Override
31+ @Transactional
32+ public void saveAlertConf(AlertConfig alertConfig) {
33+ List<AlertConfig> list = this.list();
34+ if (CollectionUtil.isNotEmpty(list)) {
35+ this.removeBatchByIds(list.stream().map(item -> item.getId()).collect(Collectors.toList()));
36+ }
37+ this.saveOrUpdate(alertConfig);
38+ prometheusService.updateAlertConfig(alertConfig, list);
39+ }
40+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertRecordServiceImpl.java+329-0
@@ -0,0 +1,329 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.bean.BeanUtil;
8+import cn.hutool.core.collection.CollectionUtil;
9+import cn.hutool.core.util.StrUtil;
10+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
11+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
12+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
13+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
14+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
15+import com.gitee.starblues.bootstrap.annotation.AutowiredType;
16+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity;
17+import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity;
18+import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity;
19+import org.opengauss.admin.system.plugin.facade.HostFacade;
20+import org.opengauss.admin.system.service.ops.IOpsClusterNodeService;
21+import org.opengauss.admin.system.service.ops.IOpsClusterService;
22+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
23+import org.opengauss.plugin.alertcenter.dto.AlertRecordDto;
24+import org.opengauss.plugin.alertcenter.dto.AlertRelationDto;
25+import org.opengauss.plugin.alertcenter.dto.AlertStatisticsDto;
26+import org.opengauss.plugin.alertcenter.entity.AlertRecord;
27+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
28+import org.opengauss.plugin.alertcenter.entity.NctigbaEnv;
29+import org.opengauss.plugin.alertcenter.mapper.AlertRecordMapper;
30+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemMapper;
31+import org.opengauss.plugin.alertcenter.mapper.NctigbaEnvMapper;
32+import org.opengauss.plugin.alertcenter.model.AlertRecordReq;
33+import org.opengauss.plugin.alertcenter.model.AlertStatisticsReq;
34+import org.opengauss.plugin.alertcenter.service.AlertRecordService;
35+import org.opengauss.plugin.alertcenter.service.PrometheusService;
36+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
37+import org.springframework.beans.factory.annotation.Autowired;
38+import org.springframework.stereotype.Service;
39+ 
40+import java.time.LocalDateTime;
41+import java.time.format.DateTimeFormatter;
42+import java.util.ArrayList;
43+import java.util.List;
44+import java.util.Map;
45+import java.util.stream.Collectors;
46+ 
47+import static org.opengauss.plugin.alertcenter.constant.CommonConstants.FIRING_STATUS;
48+import static org.opengauss.plugin.alertcenter.constant.CommonConstants.READ_STATUS;
49+import static org.opengauss.plugin.alertcenter.constant.CommonConstants.UNREAD_STATUS;
50+ 
51+/**
52+ * @author wuyuebin
53+ * @date 2023/5/17 10:35
54+ * @description
55+ */
56+@Service
57+public class AlertRecordServiceImpl extends ServiceImpl<AlertRecordMapper, AlertRecord> implements AlertRecordService {
58+ @Autowired
59+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
60+ private IOpsClusterService clusterService;
61+ 
62+ @Autowired
63+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
64+ private IOpsClusterNodeService clusterNodeService;
65+ 
66+ @Autowired
67+ @AutowiredType(AutowiredType.Type.PLUGIN_MAIN)
68+ private HostFacade hostFacade;
69+ 
70+ @Autowired
71+ private AlertTemplateRuleItemMapper templateRuleItemMapper;
72+ 
73+ @Autowired
74+ private PrometheusService prometheusService;
75+ 
76+ @Autowired
77+ private NctigbaEnvMapper envMapper;
78+ 
79+ @Override
80+ public Page<AlertRecordDto> getListPage(AlertRecordReq alertRecordReq, Page page) {
81+ LocalDateTime startTime = StrUtil.isNotBlank(alertRecordReq.getStartTime()) ? LocalDateTime.parse(
82+ alertRecordReq.getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
83+ LocalDateTime endTime = StrUtil.isNotBlank(alertRecordReq.getEndTime()) ? LocalDateTime.parse(
84+ alertRecordReq.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
85+ String clusterNodeId = alertRecordReq.getClusterNodeId();
86+ Page<AlertRecord> recordPage = this.baseMapper.selectPage(page, Wrappers.<AlertRecord>lambdaQuery()
87+ .eq(StrUtil.isNotBlank(clusterNodeId), AlertRecord::getClusterNodeId, clusterNodeId)
88+ .in(StrUtil.isNotBlank(alertRecordReq.getAlertStatus()), AlertRecord::getAlertStatus,
89+ alertRecordReq.getAlertStatus().split(CommonConstants.DELIMITER))
90+ .in(StrUtil.isNotBlank(alertRecordReq.getRecordStatus()), AlertRecord::getRecordStatus,
91+ alertRecordReq.getRecordStatus().split(CommonConstants.DELIMITER))
92+ .in(StrUtil.isNotBlank(alertRecordReq.getAlertLevel()), AlertRecord::getLevel,
93+ alertRecordReq.getAlertLevel().split(CommonConstants.DELIMITER))
94+ .ge(startTime != null, AlertRecord::getStartTime, startTime)
95+ .le(endTime != null, AlertRecord::getEndTime, endTime).orderByDesc(AlertRecord::getId));
96+ List<AlertRecord> records = recordPage.getRecords();
97+ Page<AlertRecordDto> recordDtoPage = new Page<>();
98+ recordDtoPage.setTotal(recordPage.getTotal()).setCurrent(recordPage.getCurrent()).setSize(recordPage.getSize());
99+ if (CollectionUtil.isEmpty(records)) {
100+ recordDtoPage.setRecords(new ArrayList<>());
101+ return recordDtoPage;
102+ }
103+ List<String> clusterNodeIdList = records.stream().map(item -> clusterNodeId).collect(Collectors.toList());
104+ List<OpsClusterNodeEntity> opsClusterNodeEntities = clusterNodeService.listByIds(clusterNodeIdList);
105+ List<String> hostIds = opsClusterNodeEntities.stream().map(item -> item.getHostId()).collect(
106+ Collectors.toList());
107+ List<String> clusterIds = opsClusterNodeEntities.stream().map(item -> item.getClusterId()).collect(
108+ Collectors.toList());
109+ List<OpsHostEntity> opsHostEntities = hostFacade.listByIds(hostIds);
110+ List<OpsClusterEntity> opsClusterEntities = clusterService.listByIds(clusterIds);
111+ 
112+ List<AlertRecordDto> list = new ArrayList<>();
113+ for (AlertRecord record : records) {
114+ list.add(alertRecordToDto(record, opsClusterNodeEntities, opsHostEntities, opsClusterEntities));
115+ }
116+ recordDtoPage.setRecords(list);
117+ return recordDtoPage;
118+ }
119+ 
120+ private AlertRecordDto alertRecordToDto(
121+ AlertRecord record, List<OpsClusterNodeEntity> opsClusterNodeEntities,
122+ List<OpsHostEntity> opsHostEntities, List<OpsClusterEntity> opsClusterEntities) {
123+ AlertRecordDto alertRecordDto = new AlertRecordDto();
124+ BeanUtil.copyProperties(record, alertRecordDto);
125+ String clusterNodeId0 = record.getClusterNodeId();
126+ OpsClusterNodeEntity opsClusterNodeEntity = opsClusterNodeEntities.stream().filter(
127+ item -> item.getClusterNodeId().equals(clusterNodeId0)).findFirst().get();
128+ OpsHostEntity opsHostEntity = opsHostEntities.stream().filter(
129+ item -> item.getHostId().equals(opsClusterNodeEntity.getHostId())).findFirst().get();
130+ OpsClusterEntity opsClusterEntity = opsClusterEntities.stream().filter(
131+ item -> item.getClusterId().equals(opsClusterNodeEntity.getClusterId())).findFirst().get();
132+ String nodeName = opsClusterEntity.getClusterId() + "/" + opsHostEntity.getPublicIp() + ":"
133+ + opsClusterEntity.getPort() + "(" + opsClusterNodeEntity.getClusterRole() + ")";
134+ alertRecordDto.setClusterNodeName(nodeName).setClusterId(opsClusterEntity.getClusterId()).setHostIpAndPort(
135+ opsHostEntity.getPublicIp() + ":" + opsClusterEntity.getPort()).setNodeRole(
136+ opsClusterNodeEntity.getClusterRole().name());
137+ return alertRecordDto;
138+ }
139+ 
140+ @Override
141+ public AlertStatisticsDto alertRecordStatistics(AlertStatisticsReq alertStatisticsReq) {
142+ LocalDateTime startTime = StrUtil.isNotBlank(alertStatisticsReq.getStartTime()) ? LocalDateTime.parse(
143+ alertStatisticsReq.getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
144+ LocalDateTime endTime = StrUtil.isNotBlank(alertStatisticsReq.getEndTime()) ? LocalDateTime.parse(
145+ alertStatisticsReq.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null;
146+ Long total = this.baseMapper.selectCount(
147+ Wrappers.<AlertRecord>lambdaQuery().eq(StrUtil.isNotBlank(alertStatisticsReq.getClusterNodeId()),
148+ AlertRecord::getClusterNodeId, alertStatisticsReq.getClusterNodeId())
149+ .ge(startTime != null, AlertRecord::getStartTime, startTime)
150+ .le(endTime != null, AlertRecord::getEndTime, endTime));
151+ 
152+ AlertStatisticsDto alertStatisticsDto = new AlertStatisticsDto();
153+ alertStatisticsDto.setTotalNum(total.intValue());
154+ AlertStatisticsDto statusCountStatistics = setAndGetAlertStatusCount(alertStatisticsReq.getClusterNodeId(),
155+ startTime, endTime);
156+ alertStatisticsDto.setFiringNum(statusCountStatistics.getFiringNum()).setRecoverNum(
157+ statusCountStatistics.getRecoverNum());
158+ 
159+ AlertStatisticsDto recordStatusCountStatistics =
160+ setAndGetRecordStatusCount(alertStatisticsReq.getClusterNodeId(), startTime, endTime);
161+ alertStatisticsDto.setUnReadNum(recordStatusCountStatistics.getUnReadNum()).setReadNum(
162+ recordStatusCountStatistics.getReadNum());
163+ 
164+ AlertStatisticsDto levelCountStatistics = setAndGetLevelCount(alertStatisticsReq.getClusterNodeId(),
165+ startTime, endTime);
166+ alertStatisticsDto.setSeriousNum(levelCountStatistics.getSeriousNum()).setWarnNum(
167+ levelCountStatistics.getWarnNum()).setInfoNum(levelCountStatistics.getInfoNum());
168+ 
169+ return alertStatisticsDto;
170+ }
171+ 
172+ private AlertStatisticsDto setAndGetAlertStatusCount(
173+ String clusterNodeId, LocalDateTime startTime,
174+ LocalDateTime endTime) {
175+ QueryWrapper<AlertRecord> queryWrapper = new QueryWrapper<>();
176+ queryWrapper.select("alert_status as alertStatus,count(1) count").eq(
177+ StrUtil.isNotBlank(clusterNodeId), "cluster_node_id", clusterNodeId)
178+ .ge(startTime != null, "start_time", startTime)
179+ .le(endTime != null, "end_time", endTime).groupBy("alert_status");
180+ List<Map<String, Object>> alertStatusMaps = this.listMaps(queryWrapper);
181+ AlertStatisticsDto alertStatisticsDto = new AlertStatisticsDto();
182+ for (Map<String, Object> alertStatusMap : alertStatusMaps) {
183+ int count = 0;
184+ if (alertStatusMap.get("count") instanceof Long) {
185+ count = ((Long) alertStatusMap.get("count")).intValue();
186+ }
187+ if (alertStatusMap.get("alertstatus").equals(FIRING_STATUS)) {
188+ alertStatisticsDto.setFiringNum(count);
189+ } else {
190+ alertStatisticsDto.setRecoverNum(count);
191+ }
192+ }
193+ return alertStatisticsDto;
194+ }
195+ 
196+ private AlertStatisticsDto setAndGetRecordStatusCount(
197+ String clusterNodeId, LocalDateTime startTime,
198+ LocalDateTime endTime) {
199+ QueryWrapper<AlertRecord> queryWrapper = new QueryWrapper<>();
200+ queryWrapper.select("record_status as recordStatus,count(1) count").eq(
201+ StrUtil.isNotBlank(clusterNodeId), "cluster_node_id",
202+ clusterNodeId)
203+ .ge(startTime != null, "start_time", startTime)
204+ .le(endTime != null, "end_time", endTime).groupBy("record_status");
205+ List<Map<String, Object>> recordStatusMaps = this.listMaps(queryWrapper);
206+ AlertStatisticsDto alertStatisticsDto = new AlertStatisticsDto();
207+ for (Map<String, Object> recordStatusMap : recordStatusMaps) {
208+ int count = 0;
209+ if (recordStatusMap.get("count") instanceof Long) {
210+ count = ((Long) recordStatusMap.get("count")).intValue();
211+ }
212+ if (recordStatusMap.get("recordstatus").equals(FIRING_STATUS)) {
213+ alertStatisticsDto.setUnReadNum(count);
214+ } else {
215+ alertStatisticsDto.setReadNum(count);
216+ }
217+ }
218+ return alertStatisticsDto;
219+ }
220+ 
221+ private AlertStatisticsDto setAndGetLevelCount(
222+ String clusterNodeId, LocalDateTime startTime,
223+ LocalDateTime endTime) {
224+ QueryWrapper<AlertRecord> queryWrapper = new QueryWrapper<>();
225+ queryWrapper.select("level ,count(1) count").eq(StrUtil.isNotBlank(clusterNodeId),
226+ "cluster_node_id", clusterNodeId)
227+ .ge(startTime != null, "start_time", startTime)
228+ .le(endTime != null, "end_time", endTime).groupBy("level");
229+ List<Map<String, Object>> levelMaps = this.listMaps(queryWrapper);
230+ AlertStatisticsDto alertStatisticsDto = new AlertStatisticsDto();
231+ for (Map<String, Object> levelMap : levelMaps) {
232+ int count = 0;
233+ if (levelMap.get("count") instanceof Long) {
234+ count = ((Long) levelMap.get("count")).intValue();
235+ }
236+ if (levelMap.get("level").equals(CommonConstants.SERIOUS)) {
237+ alertStatisticsDto.setSeriousNum(count);
238+ } else if (levelMap.get("level").equals(CommonConstants.WARN)) {
239+ alertStatisticsDto.setWarnNum(count);
240+ } else {
241+ alertStatisticsDto.setInfoNum(count);
242+ }
243+ }
244+ return alertStatisticsDto;
245+ }
246+ 
247+ @Override
248+ public String markAsRead(String ids) {
249+ String[] idArr = ids.split(CommonConstants.DELIMITER);
250+ LambdaUpdateWrapper<AlertRecord> updateWrapper = new LambdaUpdateWrapper<>();
251+ updateWrapper.in(AlertRecord::getId, idArr);
252+ updateWrapper.set(AlertRecord::getRecordStatus, READ_STATUS).set(AlertRecord::getUpdateTime,
253+ LocalDateTime.now());
254+ update(null, updateWrapper);
255+ return "success";
256+ }
257+ 
258+ @Override
259+ public AlertRecordDto getById(Long id) {
260+ AlertRecordDto alertRecordDto = new AlertRecordDto();
261+ AlertRecord alertRecord = this.baseMapper.selectById(id);
262+ if (alertRecord == null) {
263+ return alertRecordDto;
264+ }
265+ BeanUtil.copyProperties(alertRecord, alertRecordDto);
266+ String clusterNodeId = alertRecordDto.getClusterNodeId();
267+ OpsHostEntity opsHostEntity = new OpsHostEntity();
268+ OpsClusterEntity opsClusterEntity = new OpsClusterEntity();
269+ OpsClusterNodeEntity opsClusterNodeEntity = clusterNodeService.getById(clusterNodeId);
270+ if (opsClusterNodeEntity == null) {
271+ opsClusterNodeEntity = new OpsClusterNodeEntity();
272+ }
273+ if (StrUtil.isNotBlank(opsClusterNodeEntity.getHostId())) {
274+ opsHostEntity = hostFacade.getById(opsClusterNodeEntity.getHostId());
275+ }
276+ if (StrUtil.isNotBlank(opsClusterNodeEntity.getClusterId())) {
277+ opsClusterEntity = clusterService.getById(opsClusterNodeEntity.getClusterId());
278+ }
279+ String nodeName =
280+ opsClusterEntity.getClusterId() + "/" + opsHostEntity.getPublicIp() + ":" + opsClusterEntity.getPort()
281+ + "(" + opsClusterNodeEntity.getClusterRole() + ")";
282+ alertRecordDto.setClusterNodeName(nodeName).setHostIpAndPort(
283+ opsHostEntity.getPublicIp() + ":" + opsClusterEntity.getPort()).setClusterId(
284+ opsClusterEntity.getClusterId()).setNodeRole(opsClusterNodeEntity.getClusterRole().name());
285+ return alertRecordDto;
286+ }
287+ 
288+ @Override
289+ public List<AlertRelationDto> getRelationData(Long id) {
290+ MessageSourceUtil.reset();
291+ AlertRecord alertRecord = this.baseMapper.selectById(id);
292+ Long templateRuleId = alertRecord.getTemplateRuleId();
293+ List<AlertTemplateRuleItem> templateRuleItems = templateRuleItemMapper.selectList(
294+ Wrappers.<AlertTemplateRuleItem>lambdaQuery().eq(AlertTemplateRuleItem::getTemplateRuleId,
295+ templateRuleId));
296+ List<AlertRelationDto> relationDtoList = new ArrayList<>();
297+ NctigbaEnv promEnv = envMapper
298+ .selectOne(Wrappers.<NctigbaEnv>lambdaQuery().eq(NctigbaEnv::getType, NctigbaEnv.Type.PROMETHEUS));
299+ OpsHostEntity opsHostEntity = hostFacade.getById(promEnv.getHostid());
300+ for (AlertTemplateRuleItem templateRuleItem : templateRuleItems) {
301+ AlertRelationDto relationDto = new AlertRelationDto();
302+ relationDto.setName(MessageSourceUtil.get(templateRuleItem.getRuleExpName())).setUnit(
303+ templateRuleItem.getUnit()).setStartTime(alertRecord.getStartTime()).setEndTime(
304+ alertRecord.getEndTime());
305+ // 向前推30分钟,向后推30分钟
306+ LocalDateTime minTime = alertRecord.getStartTime().minusMinutes(30L);
307+ LocalDateTime maxTime = alertRecord.getEndTime().plusMinutes(30L);
308+ relationDto.setMinTime(minTime).setMaxTime(maxTime);
309+ String ruleExp = templateRuleItem.getRuleExp();
310+ ruleExp = ruleExp.replace("${instances}", alertRecord.getClusterNodeId());
311+ Number[][] datas = prometheusService.queryRange(opsHostEntity.getPublicIp(), promEnv.getPort().toString(),
312+ ruleExp, minTime, maxTime);
313+ relationDto.setDatas(datas).setLimitValue(templateRuleItem.getLimitValue());
314+ relationDtoList.add(relationDto);
315+ }
316+ return relationDtoList;
317+ }
318+ 
319+ @Override
320+ public String markAsUnread(String ids) {
321+ String[] idArr = ids.split(CommonConstants.DELIMITER);
322+ LambdaUpdateWrapper<AlertRecord> updateWrapper = new LambdaUpdateWrapper<>();
323+ updateWrapper.in(AlertRecord::getId, idArr);
324+ updateWrapper.set(AlertRecord::getRecordStatus, UNREAD_STATUS).set(AlertRecord::getUpdateTime,
325+ LocalDateTime.now());
326+ update(null, updateWrapper);
327+ return "success";
328+ }
329+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertRuleServiceImpl.java+177-0
@@ -0,0 +1,177 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.bean.BeanUtil;
8+import cn.hutool.core.collection.CollectionUtil;
9+import cn.hutool.core.util.StrUtil;
10+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
11+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
12+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
13+import org.opengauss.admin.common.exception.ServiceException;
14+import org.opengauss.plugin.alertcenter.config.properties.AlertProperty;
15+import org.opengauss.plugin.alertcenter.config.properties.RuleItemProperty;
16+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
17+import org.opengauss.plugin.alertcenter.dto.AlertRuleDto;
18+import org.opengauss.plugin.alertcenter.dto.RuleItemPropertyDto;
19+import org.opengauss.plugin.alertcenter.entity.AlertRule;
20+import org.opengauss.plugin.alertcenter.entity.AlertRuleItem;
21+import org.opengauss.plugin.alertcenter.entity.AlertRuleItemParam;
22+import org.opengauss.plugin.alertcenter.mapper.AlertRuleItemMapper;
23+import org.opengauss.plugin.alertcenter.mapper.AlertRuleItemParamMapper;
24+import org.opengauss.plugin.alertcenter.mapper.AlertRuleMapper;
25+import org.opengauss.plugin.alertcenter.model.RuleReq;
26+import org.opengauss.plugin.alertcenter.service.AlertRuleService;
27+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
28+import org.springframework.beans.factory.annotation.Autowired;
29+import org.springframework.stereotype.Service;
30+ 
31+import java.util.ArrayList;
32+import java.util.List;
33+import java.util.stream.Collectors;
34+ 
35+/**
36+ * @author wuyuebin
37+ * @date 2023/5/9 10:13
38+ * @description
39+ */
40+@Service
41+public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements AlertRuleService {
42+ @Autowired
43+ private AlertProperty alertProperty;
44+ @Autowired
45+ private AlertRuleItemMapper alertRuleItemMapper;
46+ @Autowired
47+ private AlertRuleItemParamMapper ruleItemParamMapper;
48+ 
49+ @Override
50+ public Page<AlertRuleDto> getRulePage(RuleReq ruleReq, Page page) {
51+ MessageSourceUtil.reset();
52+ Page<AlertRuleDto> ruleDtoPage = new Page<>();
53+ Page<AlertRule> alertRulePage = this.baseMapper.selectPage(page, Wrappers.<AlertRule>lambdaQuery()
54+ .like(StrUtil.isNotBlank(ruleReq.getRuleName()), AlertRule::getRuleName, ruleReq.getRuleName())
55+ .eq(StrUtil.isNotBlank(ruleReq.getRuleType()), AlertRule::getRuleType, ruleReq.getRuleType())
56+ .eq(StrUtil.isNotBlank(ruleReq.getLevel()), AlertRule::getLevel, ruleReq.getLevel()).orderByDesc(
57+ AlertRule::getId));
58+ ruleDtoPage.setSize(alertRulePage.getSize()).setTotal(alertRulePage.getTotal()).setCurrent(
59+ alertRulePage.getCurrent());
60+ List<AlertRule> records = alertRulePage.getRecords();
61+ if (CollectionUtil.isEmpty(records)) {
62+ ruleDtoPage.setRecords(new ArrayList<>());
63+ return ruleDtoPage;
64+ }
65+ List<Long> ruleIdList = records.stream().map(item -> item.getId()).collect(Collectors.toList());
66+ List<AlertRuleItem> alertRuleItems = alertRuleItemMapper.selectList(
67+ Wrappers.<AlertRuleItem>lambdaQuery().in(AlertRuleItem::getRuleId, ruleIdList).eq(
68+ AlertRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
69+ List<Long> itemIdList = alertRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
70+ List<AlertRuleItemParam> itemParamList = ruleItemParamMapper.selectList(
71+ Wrappers.<AlertRuleItemParam>lambdaQuery().in(AlertRuleItemParam::getItemId, itemIdList).eq(
72+ AlertRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
73+ List<AlertRuleDto> list = new ArrayList<>();
74+ for (AlertRule record : records) {
75+ AlertRuleDto ruleDto = new AlertRuleDto();
76+ BeanUtil.copyProperties(record, ruleDto);
77+ List<AlertRuleItem> ruleItems = alertRuleItems.stream().filter(
78+ item -> item.getRuleId().equals(record.getId())).collect(Collectors.toList());
79+ String ruleExpDesc = ruleItems.stream().map(item -> {
80+ List<String> paramVals = itemParamList.stream().filter(
81+ item0 -> item0.getItemId().equals(item.getId())).map(item0 -> item0.getParamValue()).collect(
82+ Collectors.toList());
83+ return "[" + item.getRuleMark() + "]:" + MessageSourceUtil.get(item.getRuleExpName())
84+ + (CollectionUtil.isNotEmpty(paramVals)
85+ ? "(" + paramVals.stream().collect(Collectors.joining(CommonConstants.DELIMITER)) + ")" : "")
86+ + " " + (item.getAction().equals("normal")
87+ ? MessageSourceUtil.get("rule.ruleItem.normalAction") + " " + item.getOperate()
88+ + item.getLimitValue() + (StrUtil.isNotBlank(
89+ item.getUnit()) ? item.getUnit() : "") : item.getAction().equals(
90+ "increase") ? MessageSourceUtil.get("rule.ruleItem.increaseAction") : MessageSourceUtil.get(
91+ "rule.ruleItem.decreaseAction"));
92+ }).collect(Collectors.joining("<br/>"));
93+ ruleDto.setRuleExpDesc(ruleExpDesc);
94+ list.add(ruleDto);
95+ }
96+ ruleDtoPage.setRecords(list);
97+ return ruleDtoPage;
98+ }
99+ 
100+ @Override
101+ public AlertRule getRuleById(Long id) {
102+ MessageSourceUtil.reset();
103+ AlertRule alertRule = this.baseMapper.selectById(id);
104+ if (alertRule == null) {
105+ throw new ServiceException("the rule is not found");
106+ }
107+ alertRule.setRuleName(MessageSourceUtil.get(alertRule.getRuleName()));
108+ List<AlertRuleItem> alertRuleItems = alertRuleItemMapper.selectList(
109+ Wrappers.<AlertRuleItem>lambdaQuery().eq(AlertRuleItem::getRuleId, id));
110+ List<Long> itemIdList = alertRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
111+ List<AlertRuleItemParam> itemParamList = ruleItemParamMapper.selectList(
112+ Wrappers.<AlertRuleItemParam>lambdaQuery().in(AlertRuleItemParam::getItemId, itemIdList).eq(
113+ AlertRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
114+ for (AlertRuleItem alertRuleItem : alertRuleItems) {
115+ List<AlertRuleItemParam> params = itemParamList.stream().filter(
116+ item -> item.getItemId().equals(alertRuleItem.getId())).collect(Collectors.toList());
117+ alertRuleItem.setItemParamList(params);
118+ }
119+ alertRule.setAlertRuleItemList(alertRuleItems);
120+ return alertRule;
121+ }
122+ 
123+ @Override
124+ public List<RuleItemPropertyDto> getRuleItemProperties() {
125+ MessageSourceUtil.reset();
126+ List<RuleItemPropertyDto> ruleItemDtoList = new ArrayList<>();
127+ List<RuleItemProperty> ruleItems = alertProperty.getRuleItems();
128+ for (RuleItemProperty ruleItem : ruleItems) {
129+ RuleItemPropertyDto ruleItemPropertyDto = new RuleItemPropertyDto();
130+ BeanUtil.copyProperties(ruleItem, ruleItemPropertyDto);
131+ String name = ruleItemPropertyDto.getName();
132+ ruleItemPropertyDto.setI18nName(MessageSourceUtil.get(name));
133+ ruleItemDtoList.add(ruleItemPropertyDto);
134+ }
135+ return ruleItemDtoList;
136+ }
137+ 
138+ @Override
139+ public List<AlertRuleDto> getRuleList() {
140+ MessageSourceUtil.reset();
141+ List<AlertRule> alertRules =
142+ this.baseMapper.selectList(Wrappers.<AlertRule>lambdaQuery().eq(AlertRule::getIsDeleted,
143+ CommonConstants.IS_NOT_DELETE).orderByDesc(AlertRule::getId));
144+ List<Long> ruleIdList = alertRules.stream().map(item -> item.getId()).collect(Collectors.toList());
145+ List<AlertRuleItem> alertRuleItems = alertRuleItemMapper.selectList(
146+ Wrappers.<AlertRuleItem>lambdaQuery().in(AlertRuleItem::getRuleId, ruleIdList).eq(
147+ AlertRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
148+ List<Long> itemIdList = alertRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
149+ List<AlertRuleItemParam> itemParamList = ruleItemParamMapper.selectList(
150+ Wrappers.<AlertRuleItemParam>lambdaQuery().in(AlertRuleItemParam::getItemId, itemIdList).eq(
151+ AlertRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
152+ List<AlertRuleDto> list = new ArrayList<>();
153+ for (AlertRule alertRule : alertRules) {
154+ AlertRuleDto ruleDto = new AlertRuleDto();
155+ BeanUtil.copyProperties(alertRule, ruleDto);
156+ List<AlertRuleItem> ruleItems = alertRuleItems.stream().filter(
157+ item -> item.getRuleId().equals(alertRule.getId())).collect(Collectors.toList());
158+ String ruleExpDesc = ruleItems.stream().map(item -> {
159+ List<String> paramVals = itemParamList.stream().filter(
160+ item0 -> item0.getItemId().equals(item.getId())).map(item0 -> item0.getParamValue()).collect(
161+ Collectors.toList());
162+ return "[" + item.getRuleMark() + "]:" + MessageSourceUtil.get(item.getRuleExpName())
163+ + (CollectionUtil.isNotEmpty(paramVals)
164+ ? "(" + paramVals.stream().collect(Collectors.joining(CommonConstants.DELIMITER)) + ")" : "")
165+ + " " + (item.getAction().equals("normal")
166+ ? MessageSourceUtil.get("rule.ruleItem.normalAction") + " " + item.getOperate()
167+ + item.getLimitValue() + (StrUtil.isNotBlank(
168+ item.getUnit()) ? item.getUnit() : "") : item.getAction().equals(
169+ "increase") ? MessageSourceUtil.get("rule.ruleItem.increaseAction") : MessageSourceUtil.get(
170+ "rule.ruleItem.decreaseAction"));
171+ }).collect(Collectors.joining("<br/>"));
172+ ruleDto.setRuleExpDesc(ruleExpDesc);
173+ list.add(ruleDto);
174+ }
175+ return list;
176+ }
177+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertTemplateRuleItemParamServiceImpl.java+22-0
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
8+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItemParam;
9+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemParamMapper;
10+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleItemParamService;
11+import org.springframework.stereotype.Service;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/6/5 19:25
16+ * @description
17+ */
18+@Service
19+public class AlertTemplateRuleItemParamServiceImpl
20+ extends ServiceImpl<AlertTemplateRuleItemParamMapper, AlertTemplateRuleItemParam>
21+ implements AlertTemplateRuleItemParamService {
22+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertTemplateRuleItemServiceImpl.java+27-0
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
8+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
9+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemMapper;
10+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleItemService;
11+import org.springframework.stereotype.Service;
12+ 
13+import java.util.List;
14+ 
15+/**
16+ * @author wuyuebin
17+ * @date 2023/5/24 10:40
18+ * @description
19+ */
20+@Service
21+public class AlertTemplateRuleItemServiceImpl extends ServiceImpl<AlertTemplateRuleItemMapper, AlertTemplateRuleItem>
22+ implements AlertTemplateRuleItemService {
23+ @Override
24+ public void saveOrUpdateList(List<AlertTemplateRuleItem> templateRuleItems) {
25+ this.saveOrUpdateBatch(templateRuleItems);
26+ }
27+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertTemplateRuleServiceImpl.java+155-0
@@ -0,0 +1,155 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.bean.BeanUtil;
8+import cn.hutool.core.collection.CollectionUtil;
9+import cn.hutool.core.util.StrUtil;
10+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
11+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
12+import org.opengauss.admin.common.exception.ServiceException;
13+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
14+import org.opengauss.plugin.alertcenter.dto.AlertTemplateRuleDto;
15+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
16+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
17+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItemParam;
18+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemMapper;
19+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemParamMapper;
20+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleMapper;
21+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleItemParamService;
22+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleItemService;
23+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleService;
24+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
25+import org.springframework.beans.factory.annotation.Autowired;
26+import org.springframework.stereotype.Service;
27+ 
28+import java.time.LocalDateTime;
29+import java.util.ArrayList;
30+import java.util.List;
31+import java.util.stream.Collectors;
32+ 
33+/**
34+ * @author wuyuebin
35+ * @date 2023/5/24 10:32
36+ * @description
37+ */
38+@Service
39+public class AlertTemplateRuleServiceImpl extends ServiceImpl<AlertTemplateRuleMapper, AlertTemplateRule>
40+ implements AlertTemplateRuleService {
41+ @Autowired
42+ private AlertTemplateRuleItemMapper alertTemplateRuleItemMapper;
43+ @Autowired
44+ private AlertTemplateRuleItemService templateRuleItemService;
45+ @Autowired
46+ private AlertTemplateRuleItemParamMapper ruleItemParamMapper;
47+ @Autowired
48+ private AlertTemplateRuleItemParamService ruleItemParamService;
49+ 
50+ @Override
51+ public AlertTemplateRule getTemplateRule(Long templateRuleId) {
52+ AlertTemplateRule alertTemplateRule = this.baseMapper.selectById(templateRuleId);
53+ if (alertTemplateRule == null) {
54+ throw new ServiceException("the rule is not found");
55+ }
56+ List<AlertTemplateRuleItem> alertTemplateRuleItems = alertTemplateRuleItemMapper.selectList(
57+ Wrappers.<AlertTemplateRuleItem>lambdaQuery().eq(AlertTemplateRuleItem::getTemplateRuleId,
58+ alertTemplateRule.getId()));
59+ List<Long> itemIdList = alertTemplateRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
60+ List<AlertTemplateRuleItemParam> itemParamList = ruleItemParamMapper.selectList(
61+ Wrappers.<AlertTemplateRuleItemParam>lambdaQuery().in(AlertTemplateRuleItemParam::getItemId,
62+ itemIdList).eq(AlertTemplateRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
63+ for (AlertTemplateRuleItem alertRuleItem : alertTemplateRuleItems) {
64+ List<AlertTemplateRuleItemParam> params = itemParamList.stream().filter(
65+ item -> item.getItemId().equals(alertRuleItem.getId())).collect(Collectors.toList());
66+ alertRuleItem.setItemParamList(params);
67+ }
68+ alertTemplateRule.setAlertRuleItemList(alertTemplateRuleItems);
69+ return alertTemplateRule;
70+ }
71+ 
72+ @Override
73+ public AlertTemplateRuleDto saveTemplateRule(AlertTemplateRule alertTemplateRule) {
74+ MessageSourceUtil.reset();
75+ AlertTemplateRuleDto templateRuleDto = new AlertTemplateRuleDto();
76+ if (alertTemplateRule.getId() == null) {
77+ alertTemplateRule.setCreateTime(LocalDateTime.now());
78+ } else {
79+ alertTemplateRule.setUpdateTime(LocalDateTime.now());
80+ }
81+ this.saveOrUpdate(alertTemplateRule);
82+ List<AlertTemplateRuleItem> templateRuleItemList = alertTemplateRule.getAlertRuleItemList();
83+ if (CollectionUtil.isEmpty(templateRuleItemList)) {
84+ BeanUtil.copyProperties(alertTemplateRule, templateRuleDto);
85+ return templateRuleDto;
86+ }
87+ for (AlertTemplateRuleItem templateRuleItem : templateRuleItemList) {
88+ templateRuleItem.setTemplateRuleId(alertTemplateRule.getId());
89+ if (templateRuleItem.getId() == null) {
90+ templateRuleItem.setCreateTime(LocalDateTime.now());
91+ } else {
92+ templateRuleItem.setUpdateTime(LocalDateTime.now());
93+ }
94+ templateRuleItemService.saveOrUpdate(templateRuleItem);
95+ List<AlertTemplateRuleItemParam> itemParamList = templateRuleItem.getItemParamList();
96+ if (CollectionUtil.isEmpty(itemParamList)) {
97+ continue;
98+ }
99+ for (AlertTemplateRuleItemParam itemParam : itemParamList) {
100+ itemParam.setItemId(templateRuleItem.getId());
101+ if (itemParam.getId() == null) {
102+ itemParam.setCreateTime(LocalDateTime.now());
103+ } else {
104+ itemParam.setUpdateTime(LocalDateTime.now());
105+ }
106+ }
107+ ruleItemParamService.saveOrUpdateBatch(itemParamList);
108+ }
109+ BeanUtil.copyProperties(alertTemplateRule, templateRuleDto);
110+ templateRuleDto.buildRuleExpDesc();
111+ return templateRuleDto;
112+ }
113+ 
114+ 
115+ public List<AlertTemplateRuleDto> getDtoListByTemplateId(Long templateId) {
116+ MessageSourceUtil.reset();
117+ List<AlertTemplateRule> alertRules =
118+ this.baseMapper.selectList(
119+ Wrappers.<AlertTemplateRule>lambdaQuery().eq(AlertTemplateRule::getTemplateId, templateId).eq(
120+ AlertTemplateRule::getIsDeleted, CommonConstants.IS_NOT_DELETE).orderByDesc(
121+ AlertTemplateRule::getId));
122+ List<Long> ruleIdList = alertRules.stream().map(item -> item.getId()).collect(Collectors.toList());
123+ List<AlertTemplateRuleItem> alertRuleItems = alertTemplateRuleItemMapper.selectList(
124+ Wrappers.<AlertTemplateRuleItem>lambdaQuery().in(AlertTemplateRuleItem::getTemplateRuleId,
125+ ruleIdList).eq(AlertTemplateRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
126+ List<Long> itemIdList = alertRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
127+ List<AlertTemplateRuleItemParam> itemParamList = ruleItemParamMapper.selectList(
128+ Wrappers.<AlertTemplateRuleItemParam>lambdaQuery().in(AlertTemplateRuleItemParam::getItemId,
129+ itemIdList).eq(AlertTemplateRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
130+ List<AlertTemplateRuleDto> list = new ArrayList<>();
131+ for (AlertTemplateRule alertRule : alertRules) {
132+ AlertTemplateRuleDto ruleDto = new AlertTemplateRuleDto();
133+ BeanUtil.copyProperties(alertRule, ruleDto);
134+ List<AlertTemplateRuleItem> ruleItems = alertRuleItems.stream().filter(
135+ item -> item.getTemplateRuleId().equals(alertRule.getId())).collect(Collectors.toList());
136+ String ruleExpDesc = ruleItems.stream().map(item -> {
137+ List<String> paramVals = itemParamList.stream().filter(
138+ item0 -> item0.getItemId().equals(item.getId())).map(item0 -> item0.getParamValue()).collect(
139+ Collectors.toList());
140+ return "[" + item.getRuleMark() + "]:" + MessageSourceUtil.get(item.getRuleExpName())
141+ + (CollectionUtil.isNotEmpty(paramVals)
142+ ? "(" + paramVals.stream().collect(Collectors.joining(CommonConstants.DELIMITER)) + ")" : "")
143+ + " " + (item.getAction().equals("normal")
144+ ? MessageSourceUtil.get("rule.ruleItem.normalAction") + " " + item.getOperate()
145+ + item.getLimitValue() + (StrUtil.isNotBlank(
146+ item.getUnit()) ? item.getUnit() : "") : item.getAction().equals(
147+ "increase") ? MessageSourceUtil.get("rule.ruleItem.increaseAction") : MessageSourceUtil.get(
148+ "rule.ruleItem.decreaseAction"));
149+ }).collect(Collectors.joining("<br/>"));
150+ ruleDto.setRuleExpDesc(ruleExpDesc);
151+ list.add(ruleDto);
152+ }
153+ return list;
154+ }
155+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/AlertTemplateServiceImpl.java+314-0
@@ -0,0 +1,314 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.bean.BeanUtil;
8+import cn.hutool.core.collection.CollectionUtil;
9+import cn.hutool.core.util.StrUtil;
10+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
12+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
13+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
14+import org.opengauss.admin.common.exception.ServiceException;
15+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
16+import org.opengauss.plugin.alertcenter.dto.AlertTemplateDto;
17+import org.opengauss.plugin.alertcenter.dto.AlertTemplateRuleDto;
18+import org.opengauss.plugin.alertcenter.entity.AlertClusterNodeConf;
19+import org.opengauss.plugin.alertcenter.entity.AlertRule;
20+import org.opengauss.plugin.alertcenter.entity.AlertRuleItem;
21+import org.opengauss.plugin.alertcenter.entity.AlertRuleItemParam;
22+import org.opengauss.plugin.alertcenter.entity.AlertTemplate;
23+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
24+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItem;
25+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRuleItemParam;
26+import org.opengauss.plugin.alertcenter.mapper.AlertRuleItemMapper;
27+import org.opengauss.plugin.alertcenter.mapper.AlertRuleItemParamMapper;
28+import org.opengauss.plugin.alertcenter.mapper.AlertRuleMapper;
29+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateMapper;
30+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemMapper;
31+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleItemParamMapper;
32+import org.opengauss.plugin.alertcenter.mapper.AlertTemplateRuleMapper;
33+import org.opengauss.plugin.alertcenter.model.AlertTemplateReq;
34+import org.opengauss.plugin.alertcenter.model.AlertTemplateRuleReq;
35+import org.opengauss.plugin.alertcenter.service.AlertClusterNodeConfService;
36+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleItemParamService;
37+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleService;
38+import org.opengauss.plugin.alertcenter.service.AlertTemplateService;
39+import org.opengauss.plugin.alertcenter.service.PrometheusService;
40+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
41+import org.springframework.beans.factory.annotation.Autowired;
42+import org.springframework.stereotype.Service;
43+import org.springframework.transaction.annotation.Transactional;
44+ 
45+import java.time.LocalDateTime;
46+import java.util.ArrayList;
47+import java.util.List;
48+import java.util.stream.Collectors;
49+ 
50+/**
51+ * @author wuyuebin
52+ * @date 2023/5/12 11:55
53+ * @description
54+ */
55+@Service
56+public class AlertTemplateServiceImpl extends ServiceImpl<AlertTemplateMapper, AlertTemplate>
57+ implements AlertTemplateService {
58+ @Autowired
59+ private AlertRuleMapper alertRuleMapper;
60+ @Autowired
61+ private AlertRuleItemMapper alertRuleItemMapper;
62+ @Autowired
63+ private AlertRuleItemParamMapper ruleItemParamMapper;
64+ @Autowired
65+ private AlertTemplateRuleMapper alertTemplateRuleMapper;
66+ @Autowired
67+ private AlertTemplateRuleItemMapper alertTemplateRuleItemMapper;
68+ @Autowired
69+ private PrometheusService prometheusService;
70+ @Autowired
71+ private AlertTemplateRuleService templateRuleService;
72+ @Autowired
73+ private AlertTemplateRuleItemParamMapper tRuleItemParamMapper;
74+ @Autowired
75+ private AlertTemplateRuleItemParamService templateRuleItemParamService;
76+ 
77+ @Autowired
78+ private AlertClusterNodeConfService nodeConfService;
79+ 
80+ @Override
81+ public Page<AlertTemplate> getTemplatePage(String templateName, Page page) {
82+ return this.baseMapper.selectPage(page,
83+ Wrappers.<AlertTemplate>lambdaQuery().like(StrUtil.isNotBlank(templateName),
84+ AlertTemplate::getTemplateName, templateName).eq(AlertTemplate::getIsDeleted,
85+ CommonConstants.IS_NOT_DELETE).orderByDesc(AlertTemplate::getId));
86+ }
87+ 
88+ @Override
89+ public Page<AlertTemplateRuleDto> getTemplateRulePage(Long templateId, String ruleName, Page page) {
90+ MessageSourceUtil.reset();
91+ Page<AlertTemplateRuleDto> ruleDtoPage = new Page<>();
92+ Page<AlertTemplateRule> rulePage = alertTemplateRuleMapper.selectPage(page,
93+ Wrappers.<AlertTemplateRule>lambdaQuery().eq(AlertTemplateRule::getTemplateId, templateId).like(
94+ StrUtil.isNotBlank(ruleName), AlertTemplateRule::getRuleName, ruleName).eq(
95+ AlertTemplateRule::getIsDeleted, CommonConstants.IS_NOT_DELETE).orderByDesc(
96+ AlertTemplateRule::getId));
97+ ruleDtoPage.setSize(rulePage.getSize()).setTotal(rulePage.getTotal()).setCurrent(rulePage.getCurrent());
98+ List<AlertTemplateRule> records = rulePage.getRecords();
99+ if (CollectionUtil.isEmpty(records)) {
100+ ruleDtoPage.setRecords(new ArrayList<>());
101+ return ruleDtoPage;
102+ }
103+ List<Long> ruleIdList = records.stream().map(item -> item.getId()).collect(Collectors.toList());
104+ List<AlertTemplateRuleItem> alertTemplateRuleItems = alertTemplateRuleItemMapper.selectList(
105+ Wrappers.<AlertTemplateRuleItem>lambdaQuery().in(AlertTemplateRuleItem::getTemplateRuleId,
106+ ruleIdList).eq(AlertTemplateRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
107+ List<Long> itemIdList = alertTemplateRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
108+ List<AlertTemplateRuleItemParam> itemParamList = tRuleItemParamMapper.selectList(
109+ Wrappers.<AlertTemplateRuleItemParam>lambdaQuery().in(AlertTemplateRuleItemParam::getItemId,
110+ itemIdList).eq(AlertTemplateRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
111+ List<AlertTemplateRuleDto> ruleDtoList = new ArrayList<>();
112+ for (AlertTemplateRule record : records) {
113+ AlertTemplateRuleDto ruleDto = new AlertTemplateRuleDto();
114+ BeanUtil.copyProperties(record, ruleDto);
115+ List<AlertTemplateRuleItem> templateRuleItems = alertTemplateRuleItems.stream().filter(
116+ item -> item.getTemplateRuleId().equals(record.getId())).collect(Collectors.toList());
117+ String ruleExpDesc = templateRuleItems.stream().map(item -> {
118+ List<String> paramVals = itemParamList.stream().filter(
119+ item0 -> item0.getItemId().equals(item.getId())).map(item0 -> item0.getParamValue()).collect(
120+ Collectors.toList());
121+ return "[" + item.getRuleMark() + "]:" + MessageSourceUtil.get(item.getRuleExpName())
122+ + (CollectionUtil.isNotEmpty(paramVals)
123+ ? "(" + paramVals.stream().collect(Collectors.joining(CommonConstants.DELIMITER)) + ")" : "")
124+ + " " + (item.getAction().equals("normal")
125+ ? MessageSourceUtil.get("rule.ruleItem.normalAction") + " " + item.getOperate()
126+ + item.getLimitValue() + (StrUtil.isNotBlank(
127+ item.getUnit()) ? item.getUnit() : "") : item.getAction().equals(
128+ "increase") ? MessageSourceUtil.get("rule.ruleItem.increaseAction") : MessageSourceUtil.get(
129+ "rule.ruleItem.decreaseAction"));
130+ }).collect(Collectors.joining("<br/>"));
131+ ruleDto.setRuleExpDesc(ruleExpDesc);
132+ ruleDtoList.add(ruleDto);
133+ }
134+ ruleDtoPage.setRecords(ruleDtoList);
135+ return ruleDtoPage;
136+ }
137+ 
138+ 
139+ @Override
140+ public AlertTemplateDto getTemplate(Long id) {
141+ AlertTemplate alertTemplate = this.baseMapper.selectById(id);
142+ AlertTemplateDto templateDto = new AlertTemplateDto();
143+ BeanUtil.copyProperties(alertTemplate, templateDto);
144+ List<AlertTemplateRuleDto> ruleDtoList = templateRuleService.getDtoListByTemplateId(id);
145+ templateDto.setTemplateRuleDtoList(ruleDtoList);
146+ return templateDto;
147+ }
148+ 
149+ @Override
150+ @Transactional
151+ public AlertTemplate saveTemplate(AlertTemplateReq templateReq) {
152+ AlertTemplate alertTemplate = null;
153+ if (templateReq.getId() != null) {
154+ alertTemplate = this.baseMapper.selectById(templateReq.getId());
155+ }
156+ if (alertTemplate == null) {
157+ alertTemplate = new AlertTemplate();
158+ BeanUtil.copyProperties(templateReq, alertTemplate);
159+ alertTemplate.setCreateTime(LocalDateTime.now()).setIsDeleted(0);
160+ } else {
161+ alertTemplate.setTemplateName(templateReq.getTemplateName()).setUpdateTime(
162+ LocalDateTime.now()).setIsDeleted(0);
163+ }
164+ saveOrUpdate(alertTemplate);
165+ 
166+ List<AlertTemplateRuleReq> templateRuleReqList = templateReq.getTemplateRuleReqList();
167+ List<Long> templateRuleIds = templateRuleReqList.stream().map(item -> item.getTemplateRuleId()).filter(
168+ item -> item != null).collect(Collectors.toList());
169+ // 删除不需要的规则
170+ List<AlertTemplateRule> alertTemplateRules = alertTemplateRuleMapper.selectList(
171+ Wrappers.<AlertTemplateRule>lambdaQuery().eq(AlertTemplateRule::getTemplateId,
172+ alertTemplate.getId()).notIn(CollectionUtil.isNotEmpty(templateRuleIds),
173+ AlertTemplateRule::getId, templateRuleIds));
174+ alertTemplateRules.forEach(item -> {
175+ item.setIsDeleted(CommonConstants.IS_DELETE);
176+ alertTemplateRuleMapper.updateById(item);
177+ alertTemplateRuleItemMapper.update(null,
178+ new LambdaUpdateWrapper<AlertTemplateRuleItem>().set(AlertTemplateRuleItem::getIsDeleted,
179+ CommonConstants.IS_DELETE).set(AlertTemplateRuleItem::getUpdateTime, LocalDateTime.now())
180+ .eq(AlertTemplateRuleItem::getTemplateRuleId, item.getId())
181+ .eq(AlertTemplateRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
182+ });
183+ for (AlertTemplateRuleReq alertTemplateRuleReq : templateRuleReqList) {
184+ updateByAlertTemplateRuleReq(alertTemplateRuleReq, alertTemplate.getId());
185+ }
186+ return alertTemplate;
187+ }
188+ 
189+ private void updateByAlertTemplateRuleReq(AlertTemplateRuleReq alertTemplateRuleReq, Long templateId) {
190+ if (alertTemplateRuleReq.getTemplateRuleId() == null) {
191+ AlertRule alertRule = alertRuleMapper.selectById(alertTemplateRuleReq.getRuleId());
192+ AlertTemplateRule alertTemplateRule = new AlertTemplateRule();
193+ BeanUtil.copyProperties(alertRule, alertTemplateRule);
194+ alertTemplateRule.setId(null).setTemplateId(templateId).setRuleId(
195+ alertRule.getId()).setIsDeleted(0).setCreateTime(LocalDateTime.now()).setUpdateTime(null);
196+ alertTemplateRuleMapper.insert(alertTemplateRule);
197+ List<AlertRuleItem> alertRuleItems = alertRuleItemMapper.selectList(
198+ Wrappers.<AlertRuleItem>lambdaQuery().eq(AlertRuleItem::getRuleId, alertRule.getId()).eq(
199+ AlertRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
200+ for (AlertRuleItem alertRuleItem : alertRuleItems) {
201+ AlertTemplateRuleItem templateRuleItem = new AlertTemplateRuleItem();
202+ BeanUtil.copyProperties(alertRuleItem, templateRuleItem);
203+ templateRuleItem.setId(null).setTemplateRuleId(alertTemplateRule.getId()).setRuleItemId(
204+ alertRuleItem.getId()).setIsDeleted(0).setCreateTime(LocalDateTime.now()).setUpdateTime(null);
205+ alertTemplateRuleItemMapper.insert(templateRuleItem);
206+ List<AlertRuleItemParam> itemParamList = ruleItemParamMapper.selectList(
207+ Wrappers.<AlertRuleItemParam>lambdaQuery().eq(AlertRuleItemParam::getItemId,
208+ alertRuleItem.getId()).eq(AlertRuleItemParam::getIsDeleted,
209+ CommonConstants.IS_NOT_DELETE));
210+ if (CollectionUtil.isEmpty(itemParamList)) {
211+ continue;
212+ }
213+ List<AlertTemplateRuleItemParam> tItemParamList = new ArrayList<>();
214+ for (AlertRuleItemParam alertRuleItemParam : itemParamList) {
215+ AlertTemplateRuleItemParam tRuleItemParam = new AlertTemplateRuleItemParam();
216+ BeanUtil.copyProperties(alertRuleItemParam, tRuleItemParam);
217+ tRuleItemParam.setId(null).setItemId(templateRuleItem.getId()).setCreateTime(
218+ LocalDateTime.now()).setIsDeleted(CommonConstants.IS_NOT_DELETE);
219+ tItemParamList.add(tRuleItemParam);
220+ }
221+ templateRuleItemParamService.saveBatch(tItemParamList);
222+ }
223+ } else {
224+ AlertTemplateRule alertTemplateRule = alertTemplateRuleMapper.selectById(
225+ alertTemplateRuleReq.getTemplateRuleId());
226+ alertTemplateRule.setTemplateId(templateId);
227+ alertTemplateRuleMapper.updateById(alertTemplateRule);
228+ }
229+ }
230+ 
231+ @Override
232+ public List<AlertTemplate> getTemplateList() {
233+ return this.baseMapper.selectList(Wrappers.<AlertTemplate>lambdaQuery().eq(AlertTemplate::getIsDeleted,
234+ CommonConstants.IS_NOT_DELETE).orderByDesc(AlertTemplate::getId));
235+ }
236+ 
237+ @Override
238+ public List<AlertTemplateRuleDto> getTemplateRuleListById(Long templateId) {
239+ MessageSourceUtil.reset();
240+ List<AlertTemplateRuleDto> ruleDtoList = new ArrayList<>();
241+ List<AlertTemplateRule> templateRuleList =
242+ alertTemplateRuleMapper.selectList(
243+ Wrappers.<AlertTemplateRule>lambdaQuery().eq(AlertTemplateRule::getTemplateId, templateId).eq(
244+ AlertTemplateRule::getIsDeleted, CommonConstants.IS_NOT_DELETE).orderByDesc(
245+ AlertTemplateRule::getId));
246+ if (CollectionUtil.isEmpty(templateRuleList)) {
247+ return ruleDtoList;
248+ }
249+ List<Long> ruleIdList = templateRuleList.stream().map(item -> item.getId()).collect(Collectors.toList());
250+ List<AlertTemplateRuleItem> alertTemplateRuleItems = alertTemplateRuleItemMapper.selectList(
251+ Wrappers.<AlertTemplateRuleItem>lambdaQuery().in(AlertTemplateRuleItem::getTemplateRuleId,
252+ ruleIdList).eq(AlertTemplateRuleItem::getIsDeleted, 0));
253+ List<Long> itemIdList = alertTemplateRuleItems.stream().map(item -> item.getId()).collect(Collectors.toList());
254+ List<AlertTemplateRuleItemParam> itemParamList = tRuleItemParamMapper.selectList(
255+ Wrappers.<AlertTemplateRuleItemParam>lambdaQuery().in(AlertTemplateRuleItemParam::getItemId,
256+ itemIdList).eq(AlertTemplateRuleItemParam::getIsDeleted, CommonConstants.IS_NOT_DELETE));
257+ for (AlertTemplateRule templateRule : templateRuleList) {
258+ AlertTemplateRuleDto ruleDto = new AlertTemplateRuleDto();
259+ BeanUtil.copyProperties(templateRule, ruleDto);
260+ List<AlertTemplateRuleItem> templateRuleItems = alertTemplateRuleItems.stream().filter(
261+ item -> item.getTemplateRuleId().equals(templateRule.getId())).collect(Collectors.toList());
262+ String ruleExpDesc = templateRuleItems.stream().map(item -> {
263+ List<String> paramVals = itemParamList.stream().filter(
264+ item0 -> item0.getItemId().equals(item.getId())).map(item0 -> item0.getParamValue()).collect(
265+ Collectors.toList());
266+ return "[" + item.getRuleMark() + "]:" + MessageSourceUtil.get(item.getRuleExpName())
267+ + (CollectionUtil.isNotEmpty(paramVals)
268+ ? "(" + paramVals.stream().collect(Collectors.joining(CommonConstants.DELIMITER)) + ")" : "")
269+ + " " + (item.getAction().equals("normal")
270+ ? MessageSourceUtil.get("rule.ruleItem.normalAction") + " " + item.getOperate()
271+ + item.getLimitValue() + (StrUtil.isNotBlank(
272+ item.getUnit()) ? item.getUnit() : "") : item.getAction().equals(
273+ "increase") ? MessageSourceUtil.get("rule.ruleItem.increaseAction") : MessageSourceUtil.get(
274+ "rule.ruleItem.decreaseAction"));
275+ }).collect(Collectors.joining("<br/>"));
276+ ruleDto.setRuleExpDesc(ruleExpDesc);
277+ ruleDtoList.add(ruleDto);
278+ }
279+ return ruleDtoList;
280+ }
281+ 
282+ @Override
283+ public void delTemplate(Long id) {
284+ MessageSourceUtil.reset();
285+ // 查看是否被引用
286+ List<AlertClusterNodeConf> nodeConfList = nodeConfService.list(
287+ Wrappers.<AlertClusterNodeConf>lambdaQuery().eq(AlertClusterNodeConf::getTemplateId, id).eq(
288+ AlertClusterNodeConf::getIsDeleted, CommonConstants.IS_NOT_DELETE));
289+ if (CollectionUtil.isNotEmpty(nodeConfList)) {
290+ throw new ServiceException(MessageSourceUtil.get("templateIsUsed"));
291+ }
292+ LambdaUpdateWrapper<AlertTemplate> updateWrapper = new LambdaUpdateWrapper<>();
293+ updateWrapper.set(AlertTemplate::getIsDeleted, CommonConstants.IS_DELETE).set(AlertTemplate::getUpdateTime,
294+ LocalDateTime.now()).eq(AlertTemplate::getId, id).eq(AlertTemplate::getIsDeleted,
295+ CommonConstants.IS_NOT_DELETE);
296+ this.baseMapper.update(null, updateWrapper);
297+ List<AlertTemplateRule> templateRuleList = alertTemplateRuleMapper.selectList(
298+ Wrappers.<AlertTemplateRule>lambdaQuery().eq(AlertTemplateRule::getTemplateId, id).eq(
299+ AlertTemplateRule::getIsDeleted, CommonConstants.IS_NOT_DELETE));
300+ alertTemplateRuleMapper.update(null,
301+ new LambdaUpdateWrapper<AlertTemplateRule>().set(AlertTemplateRule::getIsDeleted, 1).set(
302+ AlertTemplateRule::getUpdateTime, LocalDateTime.now()).eq(AlertTemplateRule::getTemplateId,
303+ id).eq(AlertTemplateRule::getIsDeleted, CommonConstants.IS_NOT_DELETE));
304+ List<Long> templateRuleIds = templateRuleList.stream().map(item -> item.getId()).collect(Collectors.toList());
305+ if (CollectionUtil.isEmpty(templateRuleIds)) {
306+ return;
307+ }
308+ alertTemplateRuleItemMapper.update(null,
309+ new LambdaUpdateWrapper<AlertTemplateRuleItem>().set(AlertTemplateRuleItem::getIsDeleted,
310+ CommonConstants.IS_DELETE).set(AlertTemplateRuleItem::getUpdateTime, LocalDateTime.now()).in(
311+ AlertTemplateRuleItem::getTemplateRuleId, templateRuleIds).eq(
312+ AlertTemplateRuleItem::getIsDeleted, CommonConstants.IS_NOT_DELETE));
313+ }
314+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/NotifyConfigServiceImpl.java+126-0
@@ -0,0 +1,126 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import cn.hutool.core.util.StrUtil;
9+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
10+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
11+import lombok.extern.slf4j.Slf4j;
12+import org.opengauss.admin.common.exception.ServiceException;
13+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
14+import org.opengauss.plugin.alertcenter.entity.NotifyConfig;
15+import org.opengauss.plugin.alertcenter.entity.NotifyTemplate;
16+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
17+import org.opengauss.plugin.alertcenter.mapper.NotifyConfigMapper;
18+import org.opengauss.plugin.alertcenter.mapper.NotifyTemplateMapper;
19+import org.opengauss.plugin.alertcenter.mapper.NotifyWayMapper;
20+import org.opengauss.plugin.alertcenter.model.NotifyConfigReq;
21+import org.opengauss.plugin.alertcenter.service.EmailService;
22+import org.opengauss.plugin.alertcenter.service.NotifyConfigService;
23+import org.springframework.beans.factory.annotation.Autowired;
24+import org.springframework.stereotype.Service;
25+import org.springframework.validation.BeanPropertyBindingResult;
26+import org.springframework.validation.Errors;
27+import org.springframework.validation.SmartValidator;
28+ 
29+import java.time.LocalDateTime;
30+import java.util.ArrayList;
31+import java.util.List;
32+import java.util.Map;
33+import java.util.Set;
34+import java.util.stream.Collectors;
35+ 
36+/**
37+ * @author wuyuebin
38+ * @date 2023/6/1 01:46
39+ * @description
40+ */
41+@Service
42+@Slf4j
43+public class NotifyConfigServiceImpl extends ServiceImpl<NotifyConfigMapper, NotifyConfig>
44+ implements NotifyConfigService {
45+ @Autowired
46+ private EmailService emailService;
47+ @Autowired
48+ private NotifyWayMapper notifyWayMapper;
49+ @Autowired
50+ private NotifyTemplateMapper notifyTemplateMapper;
51+ @Autowired
52+ private SmartValidator smartValidator;
53+ 
54+ 
55+ @Override
56+ public List<NotifyConfig> getAllList() {
57+ List<NotifyConfig> notifyConfigList = new ArrayList<>();
58+ List<NotifyConfig> list = this.list(
59+ Wrappers.<NotifyConfig>lambdaQuery().eq(NotifyConfig::getIsDeleted, CommonConstants.IS_NOT_DELETE));
60+ Map<String, List<NotifyConfig>> map = list.stream().collect(Collectors.groupingBy(NotifyConfig::getType));
61+ map.forEach((key, val) -> {
62+ if (CollectionUtil.isNotEmpty(val)) {
63+ notifyConfigList.add(val.get(0));
64+ }
65+ });
66+ return notifyConfigList;
67+ }
68+ 
69+ @Override
70+ public void saveList(List<NotifyConfig> list) {
71+ if (CollectionUtil.isEmpty(list)) {
72+ throw new ServiceException("the notify config list is empty");
73+ }
74+ // validate
75+ for (NotifyConfig notifyConfig : list) {
76+ Errors errors = new BeanPropertyBindingResult(notifyConfig, "notifyConfig");
77+ smartValidator.validate(notifyConfig, errors, NotifyConfig.DefaultGroup.class);
78+ if (errors.hasErrors()) {
79+ throw new ServiceException(StrUtil.join(",", errors.getAllErrors()));
80+ }
81+ if (notifyConfig.getType().equals(CommonConstants.EMAIL)
82+ && notifyConfig.getEnable() == CommonConstants.ENABLE) {
83+ smartValidator.validate(notifyConfig, errors, NotifyConfig.EmailGroup.class);
84+ }
85+ if (notifyConfig.getType().equals(CommonConstants.WE_COM)
86+ && notifyConfig.getEnable() == CommonConstants.ENABLE) {
87+ smartValidator.validate(notifyConfig, errors, NotifyConfig.WeComGroup.class);
88+ }
89+ if (notifyConfig.getType().equals(CommonConstants.DING_TALK)
90+ && notifyConfig.getEnable() == CommonConstants.ENABLE) {
91+ smartValidator.validate(notifyConfig, errors, NotifyConfig.DingTalkGroup.class);
92+ }
93+ if (errors.hasErrors()) {
94+ throw new ServiceException(StrUtil.join(",", errors.getAllErrors()));
95+ }
96+ }
97+ 
98+ Set<String> typeSet = list.stream().map(item -> item.getType()).collect(Collectors.toSet());
99+ List<NotifyConfig> oldList = this.list(
100+ Wrappers.<NotifyConfig>lambdaQuery().in(NotifyConfig::getType, typeSet).eq(NotifyConfig::getIsDeleted,
101+ CommonConstants.IS_NOT_DELETE));
102+ for (NotifyConfig notifyConfig : list) {
103+ String type = notifyConfig.getType();
104+ List<NotifyConfig> list0 = oldList.stream().filter(item -> item.getType().equals(type)).collect(
105+ Collectors.toList());
106+ if (CollectionUtil.isEmpty(list0)) {
107+ notifyConfig.setCreateTime(LocalDateTime.now());
108+ continue;
109+ }
110+ notifyConfig.setId(list0.get(0).getId()).setUpdateTime(LocalDateTime.now());
111+ }
112+ this.saveOrUpdateBatch(list);
113+ }
114+ 
115+ @Override
116+ public void testConfig(NotifyConfigReq notifyConfigReq) {
117+ NotifyWay notifyWay = notifyWayMapper.selectById(notifyConfigReq.getNotifyWayId());
118+ NotifyTemplate notifyTemplate = notifyTemplateMapper.selectById(notifyWay.getNotifyTemplateId());
119+ if (notifyConfigReq.getType().equals(CommonConstants.EMAIL)) {
120+ emailService.sendTest(notifyConfigReq, notifyWay.getEmail(), notifyTemplate.getNotifyTitle(),
121+ notifyTemplate.getNotifyContent());
122+ } else {
123+ throw new ServiceException("test fail");
124+ }
125+ }
126+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/NotifyTemplateServiceImpl.java+81-0
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import cn.hutool.core.util.StrUtil;
9+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
11+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
12+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
13+import lombok.extern.slf4j.Slf4j;
14+import org.opengauss.admin.common.exception.ServiceException;
15+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
16+import org.opengauss.plugin.alertcenter.entity.NotifyTemplate;
17+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
18+import org.opengauss.plugin.alertcenter.mapper.NotifyTemplateMapper;
19+import org.opengauss.plugin.alertcenter.service.NotifyTemplateService;
20+import org.opengauss.plugin.alertcenter.service.NotifyWayService;
21+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
22+import org.springframework.beans.factory.annotation.Autowired;
23+import org.springframework.stereotype.Service;
24+ 
25+import java.time.LocalDateTime;
26+import java.util.List;
27+ 
28+/**
29+ * @author wuyuebin
30+ * @date 2023/5/31 10:18
31+ * @description
32+ */
33+@Service
34+@Slf4j
35+public class NotifyTemplateServiceImpl extends ServiceImpl<NotifyTemplateMapper, NotifyTemplate>
36+ implements NotifyTemplateService {
37+ @Autowired
38+ private NotifyWayService notifyWayService;
39+ 
40+ @Override
41+ public Page getListPage(String notifyTemplateName, String notifyTemplateType, Page page) {
42+ return this.page(page,
43+ Wrappers.<NotifyTemplate>lambdaQuery().like(StrUtil.isNotBlank(notifyTemplateName),
44+ NotifyTemplate::getNotifyTemplateName, notifyTemplateName).eq(
45+ StrUtil.isNotBlank(notifyTemplateType), NotifyTemplate::getNotifyTemplateType,
46+ notifyTemplateType).eq(NotifyTemplate::getIsDeleted, CommonConstants.IS_NOT_DELETE).orderByDesc(
47+ NotifyTemplate::getId));
48+ }
49+ 
50+ @Override
51+ public void saveTemplate(NotifyTemplate notifyTemplate) {
52+ if (notifyTemplate.getId() == null) {
53+ notifyTemplate.setCreateTime(LocalDateTime.now());
54+ } else {
55+ notifyTemplate.setUpdateTime(LocalDateTime.now());
56+ }
57+ this.saveOrUpdate(notifyTemplate);
58+ }
59+ 
60+ @Override
61+ public void delById(Long id) {
62+ MessageSourceUtil.reset();
63+ List<NotifyWay> list = notifyWayService.list(
64+ Wrappers.<NotifyWay>lambdaQuery().eq(NotifyWay::getNotifyTemplateId, id).eq(NotifyWay::getIsDeleted,
65+ CommonConstants.IS_NOT_DELETE));
66+ if (CollectionUtil.isNotEmpty(list)) {
67+ throw new ServiceException(MessageSourceUtil.get("notifyTemplateIsUsed"));
68+ }
69+ LambdaUpdateWrapper<NotifyTemplate> updateWrapper = new LambdaUpdateWrapper<>();
70+ updateWrapper.set(NotifyTemplate::getIsDeleted, CommonConstants.IS_DELETE).set(NotifyTemplate::getUpdateTime,
71+ LocalDateTime.now()).eq(NotifyTemplate::getId, id);
72+ this.update(null, updateWrapper);
73+ }
74+ 
75+ @Override
76+ public List<NotifyTemplate> getList(String notifyTemplateType) {
77+ return this.list(Wrappers.<NotifyTemplate>lambdaQuery().eq(NotifyTemplate::getIsDeleted,
78+ CommonConstants.IS_NOT_DELETE).eq(StrUtil.isNotBlank(notifyTemplateType),
79+ NotifyTemplate::getNotifyTemplateType, notifyTemplateType).orderByDesc(NotifyTemplate::getId));
80+ }
81+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/service/impl/NotifyWayServiceImpl.java+91-0
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.service.impl;
6+ 
7+import cn.hutool.core.collection.CollectionUtil;
8+import cn.hutool.core.util.StrUtil;
9+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
10+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
12+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
13+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
14+import lombok.extern.slf4j.Slf4j;
15+import org.opengauss.admin.common.exception.ServiceException;
16+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
17+import org.opengauss.plugin.alertcenter.dto.NotifyWayDto;
18+import org.opengauss.plugin.alertcenter.entity.AlertRule;
19+import org.opengauss.plugin.alertcenter.entity.AlertTemplateRule;
20+import org.opengauss.plugin.alertcenter.entity.NotifyWay;
21+import org.opengauss.plugin.alertcenter.mapper.NotifyWayMapper;
22+import org.opengauss.plugin.alertcenter.service.AlertRuleService;
23+import org.opengauss.plugin.alertcenter.service.AlertTemplateRuleService;
24+import org.opengauss.plugin.alertcenter.service.NotifyWayService;
25+import org.opengauss.plugin.alertcenter.utils.MessageSourceUtil;
26+import org.springframework.beans.factory.annotation.Autowired;
27+import org.springframework.stereotype.Service;
28+ 
29+import java.time.LocalDateTime;
30+import java.util.List;
31+ 
32+/**
33+ * @author wuyuebin
34+ * @date 2023/5/25 15:54
35+ * @description
36+ */
37+@Service
38+@Slf4j
39+public class NotifyWayServiceImpl extends ServiceImpl<NotifyWayMapper, NotifyWay>
40+ implements NotifyWayService {
41+ @Autowired
42+ private AlertRuleService ruleService;
43+ @Autowired
44+ private AlertTemplateRuleService templateRuleService;
45+ 
46+ @Override
47+ public Page<NotifyWayDto> getListPage(String name, String notifyType, Page page) {
48+ QueryWrapper<NotifyWay> ew =
49+ Wrappers.<NotifyWay>query().like(StrUtil.isNotBlank(name), "t1.name", name).eq(
50+ StrUtil.isNotBlank(notifyType), "t1.notify_type", notifyType).eq("t1.is_deleted",
51+ CommonConstants.IS_NOT_DELETE).orderByDesc("t1.id");
52+ return this.baseMapper.selectDtoPage(page, ew);
53+ }
54+ 
55+ @Override
56+ public List<NotifyWay> getList(String notifyType) {
57+ return this.baseMapper.selectList(Wrappers.<NotifyWay>lambdaQuery().eq(NotifyWay::getIsDeleted,
58+ CommonConstants.IS_NOT_DELETE).eq(StrUtil.isNotBlank(notifyType), NotifyWay::getNotifyType,
59+ notifyType).orderByDesc(NotifyWay::getId));
60+ }
61+ 
62+ @Override
63+ public void saveNotifyWay(NotifyWay notifyWay) {
64+ if (notifyWay.getId() == null) {
65+ notifyWay.setCreateTime(LocalDateTime.now());
66+ } else {
67+ notifyWay.setUpdateTime(LocalDateTime.now());
68+ }
69+ this.saveOrUpdate(notifyWay);
70+ }
71+ 
72+ @Override
73+ public void delById(Long id) {
74+ MessageSourceUtil.reset();
75+ List<AlertRule> ruleList = ruleService.list(
76+ Wrappers.<AlertRule>query().eq("is_deleted", CommonConstants.IS_NOT_DELETE).and(
77+ wrapper -> wrapper.gt("position('" + id + ",' in notify_way_ids)", 0).or().gt(
78+ "position('," + id + "' in notify_way_ids)", 0)));
79+ List<AlertTemplateRule> templateRuleList = templateRuleService.list(
80+ Wrappers.<AlertTemplateRule>query().eq("is_deleted", CommonConstants.IS_NOT_DELETE).and(
81+ wrapper -> wrapper.gt("position('" + id + ",' in notify_way_ids)", 0).or().gt(
82+ "position('," + id + "' in notify_way_ids)", 0)));
83+ if (CollectionUtil.isNotEmpty(ruleList) || CollectionUtil.isNotEmpty(templateRuleList)) {
84+ throw new ServiceException(MessageSourceUtil.get("notifyWayIsUsed"));
85+ }
86+ LambdaUpdateWrapper<NotifyWay> queryWrapper = new LambdaUpdateWrapper<>();
87+ queryWrapper.set(NotifyWay::getIsDeleted, CommonConstants.IS_DELETE).eq(NotifyWay::getId, id).eq(
88+ NotifyWay::getIsDeleted, CommonConstants.IS_NOT_DELETE);
89+ this.update(queryWrapper);
90+ }
91+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/utils/HttpUtils.java+75-0
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.utils;
6+ 
7+import org.apache.commons.lang3.StringUtils;
8+import org.slf4j.Logger;
9+import org.slf4j.LoggerFactory;
10+ 
11+import java.io.BufferedReader;
12+import java.io.IOException;
13+import java.io.InputStreamReader;
14+import java.net.URL;
15+import java.net.URLConnection;
16+ 
17+/**
18+ * General http sending method
19+ *
20+ * @author xielibo
21+ */
22+public class HttpUtils {
23+ private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
24+ 
25+ /**
26+ * Send a request for the GET method to the specified URL
27+ *
28+ * @param url URL
29+ * @param param Request parameters, which should be in the form of name1=value1&name2=value2.
30+ * @return Response result of the remote resource represented
31+ */
32+ public static String sendGet(String url, String param) {
33+ return sendGet(url, param, "UTF-8");
34+ }
35+ 
36+ /**
37+ * Send a request for the GET method to the specified URL
38+ *
39+ * @param url URL
40+ * @param param Request parameters, which should be in the form of name1=value1&name2=value2.
41+ * @param contentType Encoding type
42+ * @return Response result of the remote resource represented
43+ */
44+ public static String sendGet(String url, String param, String contentType) {
45+ StringBuilder result = new StringBuilder();
46+ BufferedReader in = null;
47+ try {
48+ String urlNameString = StringUtils.isEmpty(param) ? url : url + "?" + param;
49+ log.info("sendGet - {}", urlNameString);
50+ URL realUrl = new URL(urlNameString);
51+ URLConnection connection = realUrl.openConnection();
52+ connection.setRequestProperty("accept", "*/*");
53+ connection.setRequestProperty("connection", "Keep-Alive");
54+ connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
55+ connection.connect();
56+ in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
57+ String line;
58+ while ((line = in.readLine()) != null) {
59+ result.append(line);
60+ }
61+ log.info("recv - {}", result);
62+ } catch (IOException e) {
63+ log.error("use HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
64+ } finally {
65+ try {
66+ if (in != null) {
67+ in.close();
68+ }
69+ } catch (IOException e) {
70+ log.error("use in.close Exception, url=" + url + ",param=" + param, e);
71+ }
72+ }
73+ return result.toString();
74+ }
75+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/utils/MessageSourceUtil.java+71-0
@@ -0,0 +1,71 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.utils;
6+ 
7+import cn.hutool.core.util.StrUtil;
8+import org.springframework.beans.factory.annotation.Autowired;
9+import org.springframework.context.MessageSource;
10+import org.springframework.http.HttpHeaders;
11+import org.springframework.stereotype.Component;
12+import org.springframework.web.context.request.RequestContextHolder;
13+import org.springframework.web.context.request.ServletRequestAttributes;
14+ 
15+import javax.servlet.http.HttpServletRequest;
16+import java.util.Locale;
17+ 
18+@Component
19+public class MessageSourceUtil {
20+ private static MessageSource messageSource;
21+ private static final ThreadLocal<Locale> LOC = new ThreadLocal<>();
22+ 
23+ @Autowired
24+ public void setMessageSource(MessageSource messageSource) {
25+ MessageSourceUtil.messageSource = messageSource;
26+ }
27+ 
28+ public static void set(String language) {
29+ Locale locale;
30+ if (StrUtil.isBlank(language)) {
31+ locale = Locale.CHINA;
32+ } else {
33+ String[] str = language.split("_");
34+ locale = new Locale(str[0], str[1]);
35+ }
36+ LOC.set(locale);
37+ }
38+ 
39+ public static void set(Locale locale) {
40+ LOC.set(locale);
41+ }
42+ 
43+ public static String get(String key) {
44+ return messageSource.getMessage(key, null, key, getLocale());
45+ }
46+ 
47+ public static void reset() {
48+ LOC.remove();
49+ }
50+ 
51+ private static Locale getLocale() {
52+ if (LOC.get() != null) {
53+ return LOC.get();
54+ }
55+ String language = null;
56+ if (RequestContextHolder.currentRequestAttributes() instanceof ServletRequestAttributes) {
57+ final HttpServletRequest request =
58+ ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
59+ language = request.getHeader(HttpHeaders.CONTENT_LANGUAGE);
60+ }
61+ Locale locale;
62+ if (language == null || language.isBlank()) {
63+ locale = Locale.CHINA;
64+ } else {
65+ String[] str = language.split("_");
66+ locale = new Locale(str[0], str[1]);
67+ }
68+ LOC.set(locale);
69+ return locale;
70+ }
71+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/utils/SshSession.java+142-0
@@ -0,0 +1,142 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.utils;
6+ 
7+import cn.hutool.core.thread.ThreadUtil;
8+import lombok.extern.slf4j.Slf4j;
9+import org.apache.sshd.client.SshClient;
10+import org.apache.sshd.client.channel.ChannelExec;
11+import org.apache.sshd.client.channel.ChannelShell;
12+import org.apache.sshd.client.channel.ClientChannelEvent;
13+import org.apache.sshd.client.future.ConnectFuture;
14+import org.apache.sshd.client.session.ClientSession;
15+import org.apache.sshd.sftp.client.SftpClientFactory;
16+import org.apache.sshd.sftp.client.fs.SftpFileSystem;
17+import org.apache.sshd.sftp.client.fs.SftpPath;
18+import org.opengauss.admin.common.exception.base.BaseException;
19+import org.opengauss.plugin.alertcenter.constant.CommonConstants;
20+ 
21+import java.io.ByteArrayInputStream;
22+import java.io.ByteArrayOutputStream;
23+import java.io.IOException;
24+import java.io.InputStream;
25+import java.io.OutputStream;
26+import java.nio.file.Files;
27+import java.nio.file.Paths;
28+import java.nio.file.StandardCopyOption;
29+import java.text.MessageFormat;
30+import java.util.EnumSet;
31+ 
32+@Slf4j
33+public class SshSession implements AutoCloseable {
34+ private static final int SESSION_TIMEOUT = 10000;
35+ 
36+ private static final int CHANNEL_TIMEOUT = 1000 * 60 * 5;
37+ 
38+ private static SshClient cli;
39+ 
40+ private ClientSession session;
41+ 
42+ public enum Command {
43+ ARCH("arch"), CD("cd {0}"), LS("ls {0}"), STAT("stat {0}"), WGET("wget {0}"), TAR("tar zxf {0}"),
44+ UNZIP("unzip {0}"), APPEND_FILE(""),
45+ CHECK_USER("cat /etc/passwd | awk -F \":\" \"'{print $1}\"|grep {0} | wc -l"),
46+ CREATE_USER("useradd omm && echo ''{0} ALL=(ALL) ALL'' >> /etc/sudoers"), CHANGE_PASSWORD("passwd {1}"),
47+ PS("ps -ef|grep {0} |grep -v grep |grep {1,number,#}|awk '''{print $2}''"), KILL("kill -9 {0}");
48+ 
49+ private String cmd;
50+ 
51+ Command(String cmd) {
52+ this.cmd = cmd;
53+ }
54+ 
55+ public String parse(String... args) {
56+ return MessageFormat.format(cmd, args);
57+ }
58+ }
59+ 
60+ private SshSession(String host, Integer port, String username, String password) throws IOException {
61+ ConnectFuture cf = getClient().connect(username, host, port);
62+ session = cf.verify().getSession();
63+ 
64+ try {
65+ session.addPasswordIdentity(password);
66+ session.auth().verify(SESSION_TIMEOUT);
67+ } catch (IOException e) {
68+ throw new BaseException(username + " password error");
69+ }
70+ }
71+ 
72+ 
73+ public String execute(Command command) throws IOException {
74+ return execute(command.cmd, null);
75+ }
76+ 
77+ public String execute(String command) throws IOException {
78+ return execute(command, null);
79+ }
80+ 
81+ public String execute(String command, OutputStream os) throws IOException {
82+ log.info("exec:" + command);
83+ ChannelExec ec = session.createExecChannel(command);
84+ if (os == null) {
85+ os = new ByteArrayOutputStream();
86+ }
87+ ec.setOut(os);
88+ ec.setErr(os);
89+ ec.open();
90+ ec.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), CHANNEL_TIMEOUT);
91+ for (int i = 0; i < 100 && ec.getExitStatus() == null; i++) {
92+ ThreadUtil.sleep(100L);
93+ }
94+ if (ec.getExitStatus() != null && ec.getExitStatus() != 0) {
95+ throw new BaseException(command + " " + CommonConstants.LINE_FEED + " " + os.toString().trim());
96+ }
97+ return os.toString().trim();
98+ }
99+ 
100+ public void executeNoWait(String command) throws IOException {
101+ log.info("execC:" + command);
102+ ChannelShell channelShell = session.createShellChannel();
103+ String command0 = command + CommonConstants.LINE_FEED + "exit" + CommonConstants.LINE_FEED;
104+ channelShell.setIn(new ByteArrayInputStream(command0.getBytes()));
105+ ByteArrayOutputStream os = new ByteArrayOutputStream();
106+ channelShell.setOut(os);
107+ channelShell.setErr(os);
108+ channelShell.open();
109+ channelShell.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), CHANNEL_TIMEOUT);
110+ log.info(os.toString());
111+ }
112+ 
113+ private static final synchronized SshClient getClient() {
114+ if (cli == null) {
115+ cli = SshClient.setUpDefaultClient();
116+ cli.start();
117+ }
118+ return cli;
119+ }
120+ 
121+ public synchronized void upload(InputStream in, String target) throws IOException {
122+ SftpFileSystem sf = SftpClientFactory.instance().createSftpFileSystem(session);
123+ SftpPath path = sf.getDefaultDir().resolve(target);
124+ Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
125+ }
126+ 
127+ public synchronized void upload(String source, String target) throws IOException {
128+ log.info("from:{},to:{}", source, target);
129+ SftpFileSystem sf = SftpClientFactory.instance().createSftpFileSystem(session);
130+ SftpPath path = sf.getDefaultDir().resolve(target);
131+ Files.copy(Paths.get(source), path, StandardCopyOption.REPLACE_EXISTING);
132+ }
133+ 
134+ public static SshSession connect(String host, Integer port, String username, String password) throws IOException {
135+ return new SshSession(host, port, username, password);
136+ }
137+ 
138+ @Override
139+ public void close() throws IOException {
140+ session.close();
141+ }
142+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/utils/TextParser.java+68-0
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.utils;
6+ 
7+import cn.hutool.core.bean.BeanUtil;
8+import cn.hutool.core.util.StrUtil;
9+import org.opengauss.admin.common.exception.ServiceException;
10+ 
11+import java.util.Map;
12+ 
13+/**
14+ * @author wuyuebin
15+ * @date 2023/5/2 22:25
16+ * @description
17+ */
18+public class TextParser {
19+ private String paramOpen = "${";
20+ private String paramClose = "}";
21+ 
22+ public TextParser() {
23+ }
24+ 
25+ public TextParser(String paramOpen, String paramClose) {
26+ this.paramOpen = paramOpen;
27+ this.paramClose = paramClose;
28+ }
29+ 
30+ public String parse(String text, Map<String, Object> paramMap) {
31+ if (StrUtil.isBlank(text)) {
32+ return "";
33+ }
34+ int start = text.indexOf(this.paramOpen);
35+ if (start == -1) {
36+ return text;
37+ } else {
38+ char[] src = text.toCharArray();
39+ int offset = 0;
40+ StringBuilder builder = new StringBuilder();
41+ 
42+ do {
43+ builder.append(src, offset, start - offset);
44+ offset = start + this.paramOpen.length();
45+ 
46+ int end = text.indexOf(this.paramClose, offset);
47+ if (end == -1) {
48+ throw new ServiceException("text parser error");
49+ }
50+ String paramName = text.substring(offset, end);
51+ Object paramVal = paramMap.getOrDefault(paramName, "");
52+ builder.append(paramVal);
53+ offset = end + this.paramClose.length();
54+ start = text.indexOf(this.paramOpen, offset);
55+ } while (start > -1);
56+ 
57+ if (offset < src.length) {
58+ builder.append(src, offset, src.length - offset);
59+ }
60+ return builder.toString();
61+ }
62+ }
63+ 
64+ public String parse(String text, Object obj) {
65+ Map<String, Object> paramMap = BeanUtil.beanToMap(obj);
66+ return parse(text, paramMap);
67+ }
68+}
Aplugins/alert-center/src/main/java/org/opengauss/plugin/alertcenter/utils/YamlUtil.java+33-0
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.alertcenter.utils;
6+ 
7+import org.yaml.snakeyaml.Yaml;
8+import org.yaml.snakeyaml.introspector.Property;
9+import org.yaml.snakeyaml.nodes.NodeTuple;
10+import org.yaml.snakeyaml.nodes.Tag;
11+import org.yaml.snakeyaml.representer.Representer;
12+ 
13+public class YamlUtil {
14+ public static Yaml get() {
15+ return new Yaml(new Representer() {
16+ @Override
17+ protected NodeTuple representJavaBeanProperty(
18+ Object javaBean, Property property, Object propertyValue,
19+ Tag customTag) {
20+ return propertyValue == null ? null
21+ : super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
22+ }
23+ });
24+ }
25+ 
26+ public static <T> T loadAs(String yaml, Class<T> type) {
27+ return get().loadAs(yaml, type);
28+ }
29+ 
30+ public static String dump(Object obj) {
31+ return get().dumpAsMap(obj);
32+ }
33+}
Aplugins/alert-center/src/main/resources/application-dev.yml+30-0
@@ -0,0 +1,30 @@
1+spring:
2+ autoconfigure:
3+ exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
4+ datasource:
5+ dynamic:
6+ primary: master
7+ datasource:
8+ master:
9+ url: jdbc:opengauss://192.168.110.32:7001/postgres?currentSchema=public
10+# url: jdbc:opengauss://192.168.56.51:26000/postgres?currentSchema=public
11+ driverClassName: org.opengauss.Driver
12+ username: scott
13+ password: Test@1234
14+ type: com.alibaba.druid.pool.DruidDataSource
15+# driver-class-name: org.opengauss.Driver
16+# url: jdbc:opengauss://192.168.110.31:5432/postgres?currentSchema=public
17+# username: scott
18+# password: Test@1234
19+ druid:
20+ test-while-idle: false
21+ test-on-borrow: true
22+ validation-query: "select 1"
23+ validation-query-timeout: 10000
24+ connection-error-retry-attempts: 0
25+ break-after-acquire-failure: true
26+ max-wait: 3000
27+ 
28+mybatis-plus:
29+ # 搜索指定包别名
30+ typeAliasesPackage: org.opengauss.plugin.alertcenter.entity,org.opengauss.admin.common.core.domain.entity
Aplugins/alert-center/src/main/resources/application-prod.yml+0-0
The file is empty
Aplugins/alert-center/src/main/resources/application.yml+94-0
@@ -0,0 +1,94 @@
1+## Spring配置
2+context:
3+ listener:
4+ classes: org.opengauss.plugin.alertcenter.listener.PluginListener
5+#server:
6+# servlet:
7+# context-path: /alertCenter/api/v1
8+spring:
9+ servlet:
10+ multipart:
11+ max-file-size: 20MB #单个文件最大为20M
12+ max-request-size: 20MB #单次请求文件总数大小为20M
13+ sql:
14+ init:
15+ mode: always
16+ data-locations: classpath:db/observability-alert.sql
17+ continue-on-error: true
18+ profiles:
19+ active: '@profiles.active@'
20+ resources:
21+ static-locations: classpath:resources,file:D://static
22+ datasource:
23+ dynamic:
24+ strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
25+ jackson:
26+ time-zone: GMT+8
27+ date-format: yyyy-MM-dd HH:mm:ss
28+ serialization:
29+ indent_output: true
30+ fail_on_empty_beans: false
31+ deserialization:
32+ fail_on_unknown_properties: true
33+ parser:
34+ allow_unquoted_control_chars: true
35+ allow_single_quotes: true
36+server.compression:
37+ enabled: true
38+ mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
39+mybatis-plus:
40+ # 搜索指定包别名
41+ typeAliasesPackage: org.opengauss.plugin.alertcenter.entity
42+ # 配置mapper的扫描,找到所有的mapper.xml映射文件
43+ mapperLocations: classpath*:mapper/*.xml
44+ # 加载全局的配置文件
45+ configuration:
46+ mapUnderscoreToCamelCase: true
47+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
48+ 
49+alert:
50+ rule-file-prefix: rules/
51+ rule-file-suffix: .yml
52+ alertmanager: #prometheus推送的完整路径是 /plugins/alert-center/alertCenter/api/v1/alerts
53+ path-prefix: /plugins/alert-center/alertCenter
54+ api-version: v1
55+ ruleItems:
56+ - name: cpuUsage
57+ unit: "%"
58+ ruleExps:
59+ - type: normal
60+ exp: (1- avg(rate(node_cpu_seconds_total{mode=\"idle\",instance=~\"${instances}\"}[5m])) by(instance)) * 100
61+ - type: trend
62+ exp: avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\",instance=~\"${instances}\"}[5m] offset 15s)) - avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\",instance=~\"${instances}\"}[5m]))
63+ - name: memoryUsage
64+ unit: "%"
65+ ruleExps:
66+ - type: normal
67+ exp: 100 * (1 - (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / node_memory_MemTotal_bytes{instance=~\"${instances}\"})
68+ - type: trend
69+ exp: (node_memory_MemFree_bytes offset 15s + node_memory_Cached_bytes offset 15s + node_memory_Buffers_bytes offset 15s) / node_memory_MemTotal_bytes{instance=~\"${instances}\"} - (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / node_memory_MemTotal_bytes{instance=~\"${instances}\"}
70+ - name: diskUsage
71+ unit: MB
72+ ruleExps:
73+ - type: normal
74+ exp: (node_filesystem_size_bytes{device=~\"${filesystemPath}\",instance=~\"${instances}\"} - node_filesystem_free_bytes) /1024/1024
75+ - type: trend
76+ exp: node_filesystem_free_bytes{device=~\"${filesystemPath}\",instance=~\"${instances}\"} offset 15s - node_filesystem_free_bytes{device=~\"${filesystemPath}\",instance=~\"${instances}\"}
77+ params:
78+ paramName: filesystemPath
79+ paramValue:
80+ paramOrder: 1
81+ - name: diskWriteRate
82+ unit: MB/s
83+ ruleExps:
84+ - type: normal
85+ exp: sum(rate(node_disk_written_bytes_total{instance=~\"${instances}\"}[2m])) by (instance) /1024/1024
86+ - type: trend
87+ exp: sum(rate(node_disk_written_bytes_total{instance=~\"${instances}\"}[2m])) by (instance) - sum(rate(node_disk_written_bytes_total{instance=~\"${instances}\"}[5m] offset 15s)) by (instance)
88+ - name: diskReadRate
89+ unit: MB/s
90+ ruleExps:
91+ - type: normal
92+ exp: sum(rate(node_disk_read_bytes_total{instance=~\"${instances}\"}[2m])) by (instance) /1024/1024
93+ - type: trend
94+ exp: sum(rate(node_disk_read_bytes_total{instance=~\"${instances}\"}[2m])) by (instance) - sum(rate(node_disk_read_bytes_total{instance=~\"${instances}\"}[5m] offset 15s)) by (instance)
Aplugins/alert-center/src/main/resources/db/observability-alert.sql+547-0
@@ -0,0 +1,547 @@
1+CREATE OR REPLACE FUNCTION create_sequences_not_exists() RETURNS integer AS 'BEGIN
2+ 
3+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_rule_id'' )
4+THEN
5+CREATE SEQUENCE "public"."sq_alert_rule_id"
6+INCREMENT 1
7+MINVALUE 1
8+MAXVALUE 9223372036854775807
9+START 20
10+CACHE 1;
11+END IF;
12+ 
13+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_rule_item_id'' )
14+THEN
15+CREATE SEQUENCE "public"."sq_alert_rule_item_id"
16+INCREMENT 1
17+MINVALUE 1
18+MAXVALUE 9223372036854775807
19+START 20
20+CACHE 1;
21+END IF;
22+ 
23+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_rule_item_param_id'' )
24+THEN
25+CREATE SEQUENCE "public"."sq_alert_rule_item_param_id"
26+INCREMENT 1
27+MINVALUE 1
28+MAXVALUE 9223372036854775807
29+START 20
30+CACHE 1;
31+END IF;
32+ 
33+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_notify_way_id'' )
34+THEN
35+CREATE SEQUENCE "public"."sq_notify_way_id"
36+INCREMENT 1
37+MINVALUE 1
38+MAXVALUE 9223372036854775807
39+START 20
40+CACHE 1;
41+END IF;
42+ 
43+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_template_id'' )
44+THEN
45+CREATE SEQUENCE "public"."sq_alert_template_id"
46+INCREMENT 1
47+MINVALUE 1
48+MAXVALUE 9223372036854775807
49+START 20
50+CACHE 1;
51+END IF;
52+ 
53+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_template_rule_id'' )
54+THEN
55+CREATE SEQUENCE "public"."sq_alert_template_rule_id"
56+INCREMENT 1
57+MINVALUE 2
58+MAXVALUE 9223372036854775807
59+START 20
60+CACHE 1;
61+END IF;
62+ 
63+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_template_rule_item_id'' )
64+THEN
65+CREATE SEQUENCE "public"."sq_alert_template_rule_item_id"
66+INCREMENT 1
67+MINVALUE 2
68+MAXVALUE 9223372036854775807
69+START 20
70+CACHE 1;
71+END IF;
72+ 
73+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_template_rule_item_param_id'' )
74+THEN
75+CREATE SEQUENCE "public"."sq_alert_template_rule_item_param_id"
76+INCREMENT 1
77+MINVALUE 2
78+MAXVALUE 9223372036854775807
79+START 20
80+CACHE 1;
81+END IF;
82+ 
83+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_host_template_id'' )
84+THEN
85+CREATE SEQUENCE "public"."sq_alert_host_template_id"
86+INCREMENT 1
87+MINVALUE 2
88+MAXVALUE 9223372036854775807
89+START 20
90+CACHE 1;
91+END IF;
92+ 
93+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_record_id'' )
94+THEN
95+CREATE SEQUENCE "public"."sq_alert_record_id"
96+INCREMENT 1
97+MINVALUE 1
98+MAXVALUE 9223372036854775807
99+START 1
100+CACHE 1;
101+END IF;
102+ 
103+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_notify_template_id'' )
104+THEN
105+CREATE SEQUENCE "public"."sq_notify_template_id"
106+INCREMENT 1
107+MINVALUE 1
108+MAXVALUE 9223372036854775807
109+START 20
110+CACHE 1;
111+END IF;
112+ 
113+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_notify_message_id'' )
114+THEN
115+CREATE SEQUENCE "public"."sq_notify_message_id"
116+INCREMENT 1
117+MINVALUE 1
118+MAXVALUE 9223372036854775807
119+START 1
120+CACHE 1;
121+END IF;
122+ 
123+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_cluster_node_conf_id'' )
124+THEN
125+CREATE SEQUENCE "public"."sq_alert_cluster_node_conf_id"
126+INCREMENT 1
127+MINVALUE 1
128+MAXVALUE 9223372036854775807
129+START 1
130+CACHE 1;
131+END IF;
132+ 
133+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_template_notify_conf_id'' )
134+THEN
135+CREATE SEQUENCE "public"."sq_alert_template_notify_conf_id"
136+INCREMENT 1
137+MINVALUE 1
138+MAXVALUE 9223372036854775807
139+START 20
140+CACHE 1;
141+END IF;
142+ 
143+IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_alert_config_id'' )
144+THEN
145+CREATE SEQUENCE "public"."sq_alert_config_id"
146+INCREMENT 1
147+MINVALUE 1
148+MAXVALUE 9223372036854775807
149+START 20
150+CACHE 1;
151+END IF;
152+ 
153+RETURN 0;
154+END;'
155+LANGUAGE plpgsql;
156+ 
157+SELECT create_sequences_not_exists();
158+DROP FUNCTION create_sequences_not_exists;
159+ 
160+CREATE TABLE IF NOT EXISTS "public"."alert_rule" (
161+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_rule_id'::regclass),
162+ "rule_name" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
163+ "level" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,
164+ "rule_type" varchar(20) COLLATE "pg_catalog"."default",
165+ "rule_exp_comb" varchar(100) COLLATE "pg_catalog"."default",
166+ "rule_content" text COLLATE "pg_catalog"."default",
167+ "notify_duration" int,
168+ "notify_duration_unit" char(1) COLLATE "pg_catalog"."default",
169+ "is_repeat" int1,
170+ "is_silence" int1,
171+ "silence_start_time" timestamp(6),
172+ "silence_end_time" timestamp(6),
173+ "alert_notify" varchar(20) COLLATE "pg_catalog"."default",
174+ "notify_way_ids" text COLLATE "pg_catalog"."default",
175+ "alert_desc" text COLLATE "pg_catalog"."default",
176+ "is_deleted" int1 DEFAULT 0,
177+ "create_time" timestamp(6),
178+ "update_time" timestamp(6)
179+ );
180+COMMENT ON COLUMN "public"."alert_rule"."id" IS '规则ID';
181+COMMENT ON COLUMN "public"."alert_rule"."rule_name" IS '规则名称';
182+COMMENT ON COLUMN "public"."alert_rule"."level" IS '告警等级,serous:严重,warn:警告,info:提示';
183+COMMENT ON COLUMN "public"."alert_rule"."rule_type" IS '规则类型,index为指标,log为日志';
184+COMMENT ON COLUMN "public"."alert_rule"."rule_exp_comb" IS '组合表达式';
185+COMMENT ON COLUMN "public"."alert_rule"."rule_content" IS '规则内容';
186+COMMENT ON COLUMN "public"."alert_rule"."notify_duration" IS '通知评估时长,表示告警持续多久通知';
187+COMMENT ON COLUMN "public"."alert_rule"."notify_duration_unit" IS '通知评估时长单位,s表示秒,m表示分,h表示小时,d表示天';
188+COMMENT ON COLUMN "public"."alert_rule"."is_repeat" IS '是否重复通知,0否,1是';
189+COMMENT ON COLUMN "public"."alert_rule"."is_silence" IS '是否静默,0为否,1为是';
190+COMMENT ON COLUMN "public"."alert_rule"."silence_start_time" IS '静默开始时间';
191+COMMENT ON COLUMN "public"."alert_rule"."silence_end_time" IS '静默结束时间';
192+COMMENT ON COLUMN "public"."alert_rule"."alert_notify" IS '告警通知,firing为告警,recover为告警恢复,两个可以同时存在,用逗号分隔';
193+COMMENT ON COLUMN "public"."alert_rule"."notify_way" IS '通知方式,message、email、WeCom、DingTalk';
194+COMMENT ON COLUMN "public"."alert_rule"."alert_desc" IS '告警描述';
195+COMMENT ON COLUMN "public"."alert_rule"."is_deleted" IS '是否删除,0未删除,1删除';
196+COMMENT ON COLUMN "public"."alert_rule"."create_time" IS '创建时间';
197+COMMENT ON COLUMN "public"."alert_rule"."update_time" IS '更新时间';
198+COMMENT ON TABLE "public"."alert_rule" IS '告警规则表';
199+ 
200+ 
201+CREATE TABLE IF NOT EXISTS "public"."alert_rule_item" (
202+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_rule_item_id'::regclass),
203+ "rule_id" int8 NOT NULL,
204+ "rule_mark" char(1) COLLATE "pg_catalog"."default" NOT NULL,
205+ "rule_exp_name" varchar(100) COLLATE "pg_catalog"."default" NOT NULL,
206+ "action" varchar(50) COLLATE "pg_catalog"."default",
207+ "operate" varchar(20) COLLATE "pg_catalog"."default",
208+ "limit_value" REAL ,
209+ "unit" varchar(20) COLLATE "pg_catalog"."default",
210+ "rule_exp" varchar(200) COLLATE "pg_catalog"."default" NOT NULL,
211+ "rule_exp_param" varchar(500) COLLATE "pg_catalog"."default",
212+ "rule_item_desc" text COLLATE "pg_catalog"."default",
213+ "is_deleted" int1 DEFAULT 0,
214+ "create_time" timestamp(6),
215+ "update_time" timestamp(6)
216+ );
217+COMMENT ON COLUMN "public"."alert_rule_item"."id" IS '规则项ID';
218+COMMENT ON COLUMN "public"."alert_rule_item"."rule_id" IS '规则ID';
219+COMMENT ON COLUMN "public"."alert_rule_item"."rule_mark" IS '规则标记,A B C ……';
220+COMMENT ON COLUMN "public"."alert_rule_item"."rule_exp_name" IS '规则表达式名称';
221+COMMENT ON COLUMN "public"."alert_rule_item"."action" IS 'normal:连续发生,increase:持续增长,decrease:持续减少';
222+COMMENT ON COLUMN "public"."alert_rule_item"."operate" IS '操作符,<、 <=、 = 、>、 >=';
223+COMMENT ON COLUMN "public"."alert_rule_item"."limit_value" IS '阈值';
224+COMMENT ON COLUMN "public"."alert_rule_item"."unit" IS '单位';
225+COMMENT ON COLUMN "public"."alert_rule_item"."rule_exp" IS '规则表达式';
226+COMMENT ON COLUMN "public"."alert_rule_item"."rule_exp_param" IS '规则表达式参数,json';
227+COMMENT ON COLUMN "public"."alert_rule_item"."rule_item_desc" IS '描述';
228+COMMENT ON COLUMN "public"."alert_rule_item"."is_deleted" IS '是否删除,0未删除,1删除';
229+COMMENT ON COLUMN "public"."alert_rule_item"."create_time" IS '创建时间';
230+COMMENT ON COLUMN "public"."alert_rule_item"."update_time" IS '更新时间';
231+COMMENT ON TABLE "public"."alert_rule_item" IS '告警规则项表';
232+ 
233+CREATE TABLE IF NOT EXISTS "public"."alert_rule_item_param" (
234+ id int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_rule_item_param_id'::regclass),
235+ item_id bigint NOT NULL,
236+ param_name varchar(20) COLLATE "pg_catalog"."default",
237+ param_value varchar(100) COLLATE "pg_catalog"."default",
238+ "param_order" int default 0,
239+ is_deleted tinyint DEFAULT 0,
240+ create_time timestamp(6),
241+ update_time timestamp(6)
242+);
243+ 
244+CREATE TABLE IF NOT EXISTS "public"."notify_way" (
245+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_notify_way_id'::regclass),
246+ "name" varchar(50) COLLATE "pg_catalog"."default",
247+ "notify_type" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,
248+ "phone" text COLLATE "pg_catalog"."default",
249+ "email" text COLLATE "pg_catalog"."default",
250+ "person_id" text COLLATE "pg_catalog"."default",
251+ "dept_id" text COLLATE "pg_catalog"."default",
252+ "notify_template_id" int8 NOT NULL,
253+ "is_deleted" int1 DEFAULT 0,
254+ "create_time" timestamp(6),
255+ "update_time" timestamp(6)
256+ );
257+COMMENT ON COLUMN "public"."notify_way"."id" IS '通知方式ID';
258+COMMENT ON COLUMN "public"."notify_way"."name" IS '通知方式名称';
259+COMMENT ON COLUMN "public"."notify_way"."notify_type" IS '通知方式,message、email、WeCom、DingTalk';
260+COMMENT ON COLUMN "public"."notify_way"."phone" IS '手机号';
261+COMMENT ON COLUMN "public"."notify_way"."email" IS '邮箱';
262+COMMENT ON COLUMN "public"."notify_way"."person_id" IS '企业微信或者钉钉用户ID';
263+COMMENT ON COLUMN "public"."notify_way"."dept_id" IS '企业微信或者钉钉部门ID';
264+COMMENT ON COLUMN "public"."notify_way"."notify_template_id" IS '通知模板ID';
265+COMMENT ON COLUMN "public"."notify_way"."is_deleted" IS '是否删除,0未删除,1删除';
266+COMMENT ON COLUMN "public"."notify_way"."create_time" IS '创建时间';
267+COMMENT ON COLUMN "public"."notify_way"."update_time" IS '更新时间';
268+COMMENT ON TABLE "public"."notify_way" IS '通知方式';
269+ 
270+ 
271+CREATE TABLE IF NOT EXISTS "public"."alert_template" (
272+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_template_id'::regclass), --
273+ "template_name" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
274+ "is_deleted" int1 DEFAULT 0,
275+ "create_time" timestamp(6),
276+ "update_time" timestamp(6)
277+ );
278+COMMENT ON COLUMN "public"."alert_template"."id" IS '告警模板ID';
279+COMMENT ON COLUMN "public"."alert_template"."template_name" IS 'template_name';
280+COMMENT ON COLUMN "public"."alert_template"."is_deleted" IS '是否删除,0未删除,1删除';
281+COMMENT ON COLUMN "public"."alert_template"."create_time" IS '创建时间';
282+COMMENT ON COLUMN "public"."alert_template"."update_time" IS '更新时间';
283+COMMENT ON TABLE "public"."alert_template" IS '告警模板';
284+ 
285+ 
286+CREATE TABLE IF NOT EXISTS "public"."alert_template_rule" (
287+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_template_rule_id'::regclass),
288+ "template_id" int8,
289+ "rule_id" int8 NOT NULL,
290+ "rule_name" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
291+ "level" varchar(20) COLLATE "pg_catalog"."default" NOT NULL,
292+ "rule_type" varchar(20) COLLATE "pg_catalog"."default",
293+ "rule_exp_comb" varchar(100) COLLATE "pg_catalog"."default",
294+ "rule_content" text COLLATE "pg_catalog"."default",
295+ "notify_duration" int4,
296+ "notify_duration_unit" char(1) COLLATE "pg_catalog"."default",
297+ "is_repeat" int1,
298+ "is_silence" int1,
299+ "silence_start_time" timestamp(6),
300+ "silence_end_time" timestamp(6),
301+ "alert_notify" varchar(20) COLLATE "pg_catalog"."default",
302+ "notify_way_ids" text COLLATE "pg_catalog"."default",
303+ "alert_desc" text COLLATE "pg_catalog"."default",
304+ "is_deleted" int1 DEFAULT 0,
305+ "create_time" timestamp(6),
306+ "update_time" timestamp(6)
307+ );
308+COMMENT ON COLUMN "public"."alert_template_rule"."id" IS '模板规则ID';
309+COMMENT ON COLUMN "public"."alert_template_rule"."template_id" IS '模板ID';
310+COMMENT ON COLUMN "public"."alert_template_rule"."rule_id" IS '规则ID';
311+COMMENT ON COLUMN "public"."alert_template_rule"."rule_name" IS '规则名称';
312+COMMENT ON COLUMN "public"."alert_template_rule"."level" IS '告警等级,serous:严重,warn:警告,info:提示';
313+COMMENT ON COLUMN "public"."alert_template_rule"."rule_type" IS '规则类型,index为指标,log为日志';
314+COMMENT ON COLUMN "public"."alert_template_rule"."rule_exp_comb" IS '组合表达式';
315+COMMENT ON COLUMN "public"."alert_template_rule"."rule_content" IS '规则内容';
316+COMMENT ON COLUMN "public"."alert_template_rule"."notify_duration" IS '通知评估时长,表示告警持续多久通知';
317+COMMENT ON COLUMN "public"."alert_template_rule"."notify_duration_unit" IS '通知评估时长单位,s表示秒,m表示分,h表示小时,d表示天';
318+COMMENT ON COLUMN "public"."alert_template_rule"."is_repeat" IS '是否重复通知,0否,1是';
319+COMMENT ON COLUMN "public"."alert_template_rule"."is_silence" IS '是否静默,0为否,1为是';
320+COMMENT ON COLUMN "public"."alert_template_rule"."silence_start_time" IS '静默开始时间';
321+COMMENT ON COLUMN "public"."alert_template_rule"."silence_end_time" IS '静默结束时间';
322+COMMENT ON COLUMN "public"."alert_template_rule"."alert_notify" IS '告警通知,firing为告警,recover为告警恢复,两个可以同时存在,用逗号分隔';
323+COMMENT ON COLUMN "public"."alert_template_rule"."notify_way" IS '通知方式,message、email、WeCom、DingTalk';
324+COMMENT ON COLUMN "public"."alert_template_rule"."alert_desc" IS '告警描述';
325+COMMENT ON COLUMN "public"."alert_template_rule"."is_deleted" IS '是否删除,0未删除,1删除';
326+COMMENT ON COLUMN "public"."alert_template_rule"."create_time" IS '创建时间';
327+COMMENT ON COLUMN "public"."alert_template_rule"."update_time" IS '更新时间';
328+COMMENT ON TABLE "public"."alert_template_rule" IS '告警模板规则表';
329+ 
330+CREATE TABLE IF NOT EXISTS "public"."alert_template_rule_item" (
331+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_template_rule_item_id'::regclass),
332+ "template_rule_id" int8 NOT NULL,
333+ "rule_item_id" int8 NOT NULL,
334+ "rule_mark" char(1) COLLATE "pg_catalog"."default" NOT NULL,
335+ "rule_exp_name" varchar(100) COLLATE "pg_catalog"."default" NOT NULL,
336+ "action" varchar(50) COLLATE "pg_catalog"."default",
337+ "operate" varchar(20) COLLATE "pg_catalog"."default",
338+ "limit_value" REAL ,
339+ "unit" varchar(20) COLLATE "pg_catalog"."default",
340+ "rule_exp" varchar(200) COLLATE "pg_catalog"."default" NOT NULL,
341+ "rule_exp_param" varchar(500) COLLATE "pg_catalog"."default",
342+ "rule_item_desc" text COLLATE "pg_catalog"."default",
343+ "is_deleted" int1 DEFAULT 0,
344+ "create_time" timestamp(6),
345+ "update_time" timestamp(6)
346+ );
347+COMMENT ON COLUMN "public"."alert_template_rule_item"."id" IS '告警模板规则项ID';
348+COMMENT ON COLUMN "public"."alert_template_rule_item"."template_rule_id" IS '告警模板规则ID';
349+COMMENT ON COLUMN "public"."alert_template_rule_item"."rule_item_id" IS '规则项ID';
350+COMMENT ON COLUMN "public"."alert_template_rule_item"."rule_mark" IS '规则标记,A B C ……';
351+COMMENT ON COLUMN "public"."alert_template_rule_item"."rule_exp_name" IS '规则表达式名称';
352+COMMENT ON COLUMN "public"."alert_rule_item"."action" IS 'normal:连续发生,increase:持续增长,decrease:持续减少';
353+COMMENT ON COLUMN "public"."alert_template_rule_item"."operate" IS '操作符,<、 <=、 = 、>、 >=';
354+COMMENT ON COLUMN "public"."alert_template_rule_item"."limit_value" IS '阈值';
355+COMMENT ON COLUMN "public"."alert_template_rule_item"."unit" IS '单位';
356+COMMENT ON COLUMN "public"."alert_template_rule_item"."rule_exp" IS '规则表达式';
357+COMMENT ON COLUMN "public"."alert_template_rule_item"."rule_exp_param" IS '规则表达式参数,json';
358+COMMENT ON COLUMN "public"."alert_template_rule_item"."rule_item_desc" IS '描述';
359+COMMENT ON COLUMN "public"."alert_template_rule_item"."is_deleted" IS '是否删除,0未删除,1删除';
360+COMMENT ON COLUMN "public"."alert_template_rule_item"."create_time" IS '创建时间';
361+COMMENT ON COLUMN "public"."alert_template_rule_item"."update_time" IS '更新时间';
362+COMMENT ON TABLE "public"."alert_template_rule_item" IS '告警模板规则项表';
363+ 
364+CREATE TABLE alert_template_rule_item_param (
365+ id int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_template_rule_item_param_id'::regclass),
366+ param_name varchar(20) COLLATE "pg_catalog"."default",
367+ param_value varchar(100) COLLATE "pg_catalog"."default",
368+ "param_order" int default 0,
369+ is_deleted tinyint DEFAULT 0,
370+ create_time timestamp(6),
371+ update_time timestamp(6)
372+);
373+ 
374+CREATE TABLE IF NOT EXISTS "public"."alert_cluster_node_conf" (
375+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_cluster_node_conf_id'::regclass),
376+ "cluster_node_id" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
377+-- "host_id" int8 NOT NULL,
378+ "template_id" int8 NOT NULL,
379+ "is_deleted" int1 DEFAULT 0,
380+ "create_time" timestamp(6),
381+ "update_time" timestamp(6)
382+ );
383+COMMENT ON COLUMN "public"."alert_cluster_node_conf"."id" IS '告警配置ID';
384+COMMENT ON COLUMN "public"."alert_cluster_node_conf"."cluster_node_id" IS '实例ID';
385+COMMENT ON COLUMN "public"."alert_cluster_node_conf"."template_id" IS '告警模板ID';
386+COMMENT ON COLUMN "public"."alert_cluster_node_conf"."is_deleted" IS '是否删除,0未删除,1删除';
387+COMMENT ON COLUMN "public"."alert_cluster_node_conf"."create_time" IS '创建时间';
388+COMMENT ON COLUMN "public"."alert_cluster_node_conf"."update_time" IS '更新时间';
389+COMMENT ON TABLE "public"."alert_cluster_node_conf" IS '实例告警配置';
390+ 
391+CREATE TABLE IF NOT EXISTS "public"."alert_record" (
392+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_record_id'::regclass),
393+ "cluster_node_id" varchar(50) COLLATE "pg_catalog"."default" NOT NULL,
394+-- "host_id" int8 NOT NULL,
395+ "template_id" int8 NOT NULL,
396+ "template_name" varchar(100) COLLATE "pg_catalog"."default",
397+ "template_rule_id" int8 NOT NULL,
398+ "template_rule_name" varchar(100) COLLATE "pg_catalog"."default",
399+ "template_rule_type" varchar(50) COLLATE "pg_catalog"."default",
400+ "level" varchar(50) COLLATE "pg_catalog"."default",
401+ "alert_content" text COLLATE "pg_catalog"."default" NOT NULL,
402+ "notify_way_ids" text COLLATE "pg_catalog"."default",
403+ "notify_way_names" text COLLATE "pg_catalog"."default",
404+ "start_time" timestamp(6),
405+ "end_time" timestamp(6),
406+ "duration" int8,
407+ "alert_status" int1 DEFAULT 0,
408+ "record_status" int1 DEFAULT 0,
409+ "is_deleted" int1 DEFAULT 0,
410+ "create_time" timestamp(6),
411+ "update_time" timestamp(6)
412+ );
413+COMMENT ON COLUMN "public"."alert_record"."id" IS '告警记录ID';
414+COMMENT ON COLUMN "public"."alert_record"."cluster_node_id" IS '实例ID';
415+COMMENT ON COLUMN "public"."alert_record"."template_id" IS '告警模板ID';
416+COMMENT ON COLUMN "public"."alert_record"."template_rule_id" IS '告警模板规则ID';
417+COMMENT ON COLUMN "public"."alert_record"."alert_content" IS '告警内容';
418+COMMENT ON COLUMN "public"."alert_record"."start_time" IS '告警开始时间';
419+COMMENT ON COLUMN "public"."alert_record"."end_time" IS '告警结束时间';
420+COMMENT ON COLUMN "public"."alert_record"."duration" IS '持续时间';
421+COMMENT ON COLUMN "public"."alert_record"."alert_status" IS '告警状态,告警中:0,已结束:1';
422+COMMENT ON COLUMN "public"."alert_record"."record_status" IS '信息状态,未读:0,已读:1';
423+COMMENT ON COLUMN "public"."alert_record"."is_deleted" IS '是否删除,0未删除,1删除';
424+COMMENT ON COLUMN "public"."alert_record"."create_time" IS '创建时间';
425+COMMENT ON COLUMN "public"."alert_record"."update_time" IS '更新时间';
426+COMMENT ON TABLE "public"."alert_record" IS '告警记录';
427+ 
428+CREATE TABLE IF NOT EXISTS "public"."notify_template" (
429+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_notify_template_id'::regclass),
430+ "notify_template_name" varchar(100) COLLATE "pg_catalog"."default",
431+ "notify_template_desc" varchar(200) COLLATE "pg_catalog"."default",
432+ "notify_title" varchar(100) COLLATE "pg_catalog"."default",
433+ "notify_content" text COLLATE "pg_catalog"."default" NOT NULL,
434+ "notify_template_type" varchar(50) COLLATE "pg_catalog"."default",
435+ "is_deleted" int1 DEFAULT 0,
436+ "create_time" timestamp(6),
437+ "update_time" timestamp(6)
438+ );
439+COMMENT ON COLUMN "public"."notify_template"."id" IS '通知模板ID';
440+COMMENT ON COLUMN "public"."notify_template"."notify_template_name" IS '模板名称';
441+COMMENT ON COLUMN "public"."notify_template"."notify_template_desc" IS '模板描述';
442+COMMENT ON COLUMN "public"."notify_template"."notify_title" IS '消息主题';
443+COMMENT ON COLUMN "public"."notify_template"."notify_content" IS '消息内容';
444+COMMENT ON COLUMN "public"."notify_template"."notify_template_type" IS 'email、WeCom、DingTalk';
445+COMMENT ON COLUMN "public"."notify_template"."is_deleted" IS '是否删除,0未删除,1删除';
446+COMMENT ON COLUMN "public"."notify_template"."create_time" IS '创建时间';
447+COMMENT ON COLUMN "public"."notify_template"."update_time" IS '更新时间';
448+COMMENT ON TABLE "public"."notify_template" IS '通知模板';
449+ 
450+ 
451+CREATE TABLE IF NOT EXISTS "public"."notify_config" (
452+ "id" int8 NOT NULL PRIMARY KEY,
453+ "type" varchar(20) COLLATE "pg_catalog"."default",
454+ "email" varchar(100) COLLATE "pg_catalog"."default",
455+ "sender" varchar(50) COLLATE "pg_catalog"."default",
456+ "sever" varchar(100) COLLATE "pg_catalog"."default",
457+ "port" int4,
458+ "account" varchar(50) COLLATE "pg_catalog"."default",
459+ "passwd" varchar(100) COLLATE "pg_catalog"."default",
460+ "agent_id" varchar(100) COLLATE "pg_catalog"."default",
461+ "app_key" varchar(100) COLLATE "pg_catalog"."default",
462+ "secret" text COLLATE "pg_catalog"."default",
463+ "enable" int1 DEFAULT 0,
464+ "is_deleted" int1 DEFAULT 0,
465+ "create_time" timestamp(6),
466+ "update_time" timestamp(6)
467+ );
468+COMMENT ON COLUMN "public"."notify_config"."type" IS '发送方式';
469+COMMENT ON COLUMN "public"."notify_config"."email" IS '发信邮箱';
470+COMMENT ON COLUMN "public"."notify_config"."sender" IS '发信人';
471+COMMENT ON COLUMN "public"."notify_config"."sever" IS '服务器';
472+COMMENT ON COLUMN "public"."notify_config"."port" IS 'SMTP端口号';
473+COMMENT ON COLUMN "public"."notify_config"."account" IS 'SMTP账号';
474+COMMENT ON COLUMN "public"."notify_config"."passwd" IS 'SMTP密码';
475+COMMENT ON COLUMN "public"."notify_config"."agent_id" IS '应用ID';
476+COMMENT ON COLUMN "public"."notify_config"."app_key" IS '企业微信的企业ID或者钉钉的appKey';
477+COMMENT ON COLUMN "public"."notify_config"."enable" IS '是否启动,0未启动,1启动';
478+COMMENT ON COLUMN "public"."notify_config"."secret" IS '密钥';
479+COMMENT ON TABLE "public"."notify_config" IS '通知渠道';
480+ 
481+ 
482+CREATE TABLE IF NOT EXISTS "public"."notify_message" (
483+ "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_notify_message_id'::regclass),
484+ "record_id" int8 NOT NULL,
485+ "message_type" varchar(50) COLLATE "pg_catalog"."default",
486+ "title" varchar(100) COLLATE "pg_catalog"."default",
487+ "content" text COLLATE "pg_catalog"."default" NOT NULL,
488+ "email" varchar(100) COLLATE "pg_catalog"."default",
489+ "person_id" varchar(100) COLLATE "pg_catalog"."default",
490+ "dept_id" varchar(100) COLLATE "pg_catalog"."default",
491+ "webhook" text COLLATE "pg_catalog"."default",
492+ "status" int1 DEFAULT 0,
493+ "is_deleted" int1 DEFAULT 0,
494+ "create_time" timestamp(6),
495+ "update_time" timestamp(6)
496+ );
497+COMMENT ON COLUMN "public"."notify_message"."id" IS '消息ID';
498+COMMENT ON COLUMN "public"."notify_message"."record_id" IS '告警记录ID';
499+COMMENT ON COLUMN "public"."notify_message"."message_type" IS 'email WeCom DingTalk';
500+COMMENT ON COLUMN "public"."notify_message"."title" IS '消息主题';
501+COMMENT ON COLUMN "public"."notify_message"."content" IS '消息内容';
502+COMMENT ON COLUMN "public"."notify_message"."email" IS '收件邮箱,当message_type为email时,不为空';
503+COMMENT ON COLUMN "public"."notify_message"."person_id" IS '用户ID,当message_type为WeCom或DingTalk时,有用';
504+COMMENT ON COLUMN "public"."notify_message"."dept_id" IS '部门ID,当message_type为WeCom或DingTalk时,有用';
505+COMMENT ON COLUMN "public"."notify_message"."webhook" IS 'WeCom或者DingTalk提供webhook';
506+COMMENT ON COLUMN "public"."notify_message"."status" IS '消息发送状态,0为未发送,1为发送成功,2为发送失败';
507+COMMENT ON COLUMN "public"."notify_message"."is_deleted" IS '是否删除,0未删除,1删除';
508+COMMENT ON COLUMN "public"."notify_message"."create_time" IS '创建时间';
509+COMMENT ON COLUMN "public"."notify_message"."update_time" IS '更新时间';
510+COMMENT ON TABLE "public"."notify_message" IS '通知消息';
511+ 
512+CREATE TABLE IF NOT EXISTS "public"."alert_config" (
513+"id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_alert_config_id'::regclass),
514+"alert_ip" varchar(20) COLLATE "pg_catalog"."default",
515+"alert_port" varchar(10) COLLATE "pg_catalog"."default"
516+);
517+ 
518+INSERT INTO public.alert_rule (id,rule_name,level,rule_type,rule_exp_comb,rule_content,notify_duration,notify_duration_unit,is_repeat,is_silence,silence_start_time,silence_end_time,alert_notify,notify_way_ids,alert_desc,is_deleted,create_time,update_time)
519+ VALUES (1,'CPU使用率过高','warn','index','A','$'||'{nodeName}的CPU使用率超过90%',2,'m',1,0,null,null,'firing,recover','1',null,0,'2023-04-26 08:30:22.02',null) ON DUPLICATE KEY UPDATE NOTHING;
520+INSERT INTO public.alert_rule_item (id,rule_id,rule_mark,rule_exp_name,operate,limit_value,unit,rule_exp,rule_item_desc,is_deleted,create_time,update_time,action)
521+ VALUES (1,1,'A','cpuUsage','>=',90,'%','100 - avg(rate(node_cpu_seconds_total{mode="idle",instance=~"$'||'{instances}"}[5m])) by(instance) * 100','CPU使用率大于等于90%',0,'2023-06-05 15:45:20.02',null,'normal') ON DUPLICATE KEY UPDATE NOTHING;
522+ 
523+INSERT INTO public.alert_rule (id,rule_name,level,rule_type,rule_exp_comb,rule_content,notify_duration,notify_duration_unit,is_repeat,is_silence,silence_start_time,silence_end_time,alert_notify,notify_way_ids,alert_desc,is_deleted,create_time,update_time)
524+ VALUES (2,'内存使用率过高','serious','index','A','$'||'{nodeName}的内存使用率超过90%',2,'m',1,0,null,null,'firing,recover','1','内存使用率过高',0,'2023-06-05 15:45:20.02',null) ON DUPLICATE KEY UPDATE NOTHING;
525+INSERT INTO public.alert_rule_item (id,rule_id,rule_mark,rule_exp_name,operate,limit_value,unit,rule_exp,rule_item_desc,is_deleted,create_time,update_time,action)
526+ VALUES (2,2,'A','memoryUsage','>=',90,'%','100 * (1 - (node_memory_MemFree_bytes + node_memory_Cached_bytes + node_memory_Buffers_bytes) / node_memory_MemTotal_bytes{instance=~"$'||'{instances}"})','内存使用率大于等于90%',0,'2023-06-05 15:45:20.02',null,'normal') ON DUPLICATE KEY UPDATE NOTHING;
527+ 
528+INSERT INTO public.alert_rule (id,rule_name,level,rule_type,rule_exp_comb,rule_content,notify_duration,notify_duration_unit,is_repeat,is_silence,silence_start_time,silence_end_time,alert_notify,notify_way_ids,alert_desc,is_deleted,create_time,update_time)
529+ VALUES (3,'磁盘使用量过高','serious','index','A','$'||'{nodeName}的磁盘(/dev/vda1)使用量超过15GB',2,'m',1,0,null,null,'firing,recover','1','磁盘使用量过高',0,'2023-06-05 15:45:20.02',null) ON DUPLICATE KEY UPDATE NOTHING;
530+INSERT INTO public.alert_rule_item (id,rule_id,rule_mark,rule_exp_name,operate,limit_value,unit,rule_exp,rule_item_desc,is_deleted,create_time,update_time,action)
531+ VALUES (3,3,'A','diskUsage','>=',15360,'MB','(node_filesystem_size_bytes{device=~"/dev/vda1",instance="$' || '{instances}"} - node_filesystem_free_bytes) /1024/1024','内存使用率大于等于90%',0,'2023-06-05 15:45:20.02',null,'normal') ON DUPLICATE KEY UPDATE NOTHING;
532+insert into public.alert_rule_item_param(id,item_id,param_name,param_value,param_order,is_deleted,create_time) values(1,3,'filesystemPath','/dev/vda1',1,0,'2023-06-05 15:45:20.02');
533+ 
534+INSERT INTO public.alert_rule (id,rule_name,level,rule_type,rule_exp_comb,rule_content,notify_duration,notify_duration_unit,is_repeat,is_silence,silence_start_time,silence_end_time,alert_notify,notify_way_ids,alert_desc,is_deleted,create_time,update_time)
535+ VALUES (4,'磁盘写速率过高','serious','index','A','$'||'{nodeName}的磁盘写速率大于等于100MB/s',2,'m',1,0,null,null,'firing','1','磁盘写速率过高',0,'2023-06-05 15:45:20.02',null) ON DUPLICATE KEY UPDATE NOTHING;
536+INSERT INTO public.alert_rule_item (id,rule_id,rule_mark,rule_exp_name,operate,limit_value,unit,rule_exp,rule_item_desc,is_deleted,create_time,update_time,action)
537+ VALUES (4,4,'A','diskWriteRate','>=',100,'MB/s','sum(rate(node_disk_written_bytes_total{instance=~"$' || '{instances}"}[2m])) by (instance) /1024/1024','磁盘写速率大于等于100MB/s',0,'2023-06-05 15:45:20.02',null,'normal') ON DUPLICATE KEY UPDATE NOTHING;
538+ 
539+INSERT INTO public.alert_rule (id,rule_name,level,rule_type,rule_exp_comb,rule_content,notify_duration,notify_duration_unit,is_repeat,is_silence,silence_start_time,silence_end_time,alert_notify,notify_way_ids,alert_desc,is_deleted,create_time,update_time)
540+ VALUES (5,'磁盘读速率过高','serious','index','A','$'||'{nodeName}的磁盘读速率大于等于100MB/s',2,'m',1,0,null,null,'firing','1','磁盘读速率过高',0,'2023-06-05 15:45:20.02',null) ON DUPLICATE KEY UPDATE NOTHING;
541+INSERT INTO public.alert_rule_item (id,rule_id,rule_mark,rule_exp_name,operate,limit_value,unit,rule_exp,rule_item_desc,is_deleted,create_time,update_time,action)
542+ VALUES (5,5,'A','diskReadRate','>=',100,'MB/s','sum(rate(node_disk_read_bytes_total{instance=~"$'||'{instances}"}[2m])) by (instance) /1024/1024','磁盘读速率大于等于100MB/s',0,'2023-06-05 15:45:20.02',null,'normal') ON DUPLICATE KEY UPDATE NOTHING;
543+ 
544+INSERT INTO public.notify_template (id,notify_template_name,notify_template_desc,notify_title,notify_content,notify_template_type,is_deleted,create_time,update_time)
545+VALUES (1,'通用告警模板',null,'告警信息','告警时间:$'||'{alertTime}'||chr(10)||'告警等级:$'||'{level}'||chr(10)||'告警实例:$'||'{nodeName}'||chr(10)||'主机IP:$'||'{hostIp}'||chr(10)||'告警内容:$'||'{content}'||chr(10),'email',0,'2023-04-26 08:30:22.02',null) ON DUPLICATE KEY UPDATE NOTHING;
546+INSERT INTO public.notify_way (id,name,notify_type,phone,email,person_id,dept_id,message_template_id,is_deleted,create_time,update_time)
547+VALUES (1,'通用告警方式','email',null,'xxxx@xxx.com',null,null,1,0,'2023-05-24 11:25:38.073319',null) ON DUPLICATE KEY UPDATE NOTHING;
Aplugins/alert-center/src/main/resources/messages.properties+22-0
@@ -0,0 +1,22 @@
1+cpuUsage=CPU Usage
2+memoryUsage=Memory Usage
3+diskUsage=Disk Usage
4+diskWriteRate=Disk Write Rate
5+diskReadRate=Disk Read Rate
6+rule.ruleItem.normalAction=keep happening
7+rule.ruleItem.increaseAction=keep increasing
8+rule.ruleItem.decreaseAction=keep decreasing
9+templateIsUsed=The rule template is used,it can't be deleted.Please go to the Alert Config to remove the template before deleting it.
10+notifyTemplateIsUsed=The notify template is used,it can't be deleted.Please go to the notify way module to remove the template before deleting it.
11+notifyWayIsUsed=The notify template is used,it can't be deleted.it can't be deleted.Please go to the alert rule module or \
12+ the alert template module to remove the notify way before deleting it.
13+alertContentParam.nodeName=cluster node name
14+alertContentParam.hostname=hostname
15+alertContentParam.hostIp=host IP
16+alertContentParam.port=database port
17+alertContentParam.alertTime=alert time
18+alertContentParam.alertStatus=alert status
19+alertContentParam.level=alert level
20+alertContentParam.content=alert content
21+alerting=firing
22+alerted=recover
Aplugins/alert-center/src/main/resources/messages_en_US.properties+22-0
@@ -0,0 +1,22 @@
1+cpuUsage=Usage of CPU
2+memoryUsage=Memory Usage
3+diskUsage=Disk Usage
4+diskWriteRate=Disk Write Rate
5+diskReadRate=Disk Read Rate
6+rule.ruleItem.normalAction=keep happening
7+rule.ruleItem.increaseAction=keep increasing
8+rule.ruleItem.decreaseAction=keep decreasing
9+templateIsUsed=The rule template is used,it can't be deleted.Please go to the Alert Config to remove the template before deleting it.
10+notifyTemplateIsUsed=The notify template is used,it can't be deleted.Please go to the notify way module to remove the template before deleting it.
11+notifyWayIsUsed=The notify template is used,it can't be deleted.it can't be deleted.Please go to the alert rule module or \
12+ the alert template module to remove the notify way before deleting it.
13+alertContentParam.nodeName=cluster node name
14+alertContentParam.hostname=hostname
15+alertContentParam.hostIp=host IP
16+alertContentParam.port=database port
17+alertContentParam.alertTime=alert time
18+alertContentParam.alertStatus=alert status
19+alertContentParam.level=alert level
20+alertContentParam.content=alert content
21+alerting=firing
22+alerted=recover
Aplugins/alert-center/src/main/resources/messages_zh_CN.properties+21-0
@@ -0,0 +1,21 @@
1+cpuUsage=CPU使用率
2+memoryUsage=内存使用率
3+diskUsage=磁盘使用量
4+diskWriteRate=磁盘的写速率
5+diskReadRate=磁盘的读速率
6+rule.ruleItem.normalAction=持续发生
7+rule.ruleItem.increaseAction=持续增长
8+rule.ruleItem.decreaseAction=持续下降
9+templateIsUsed=模板正被使用,无法删除,需要到告警配置中解除该模板,才能删除
10+notifyTemplateIsUsed=通知模板正被使用,无法删除,需要到通知方式模块中解除该模板,才能删除
11+notifyWayIsUsed=该通知方式正在被使用,无法删除,需要到告警规则模块或者告警模板模块解除该通知方式,才能删除
12+alertContentParam.nodeName=节点名称
13+alertContentParam.hostname=主机名称
14+alertContentParam.hostIp=主机IP
15+alertContentParam.port=数据库端口号
16+alertContentParam.alertTime=告警时间
17+alertContentParam.alertStatus=告警状态
18+alertContentParam.level=告警级别
19+alertContentParam.content=告警内容
20+alerting=告警中
21+alerted=告警结束
Aplugins/alert-center/web-ui/.env.development+5-0
@@ -0,0 +1,5 @@
1+# 开发环境配置
2+ENV = 'development'
3+ 
4+# 开发环境
5+VITE_BASE_URL = 'http://localhost:8080'
Aplugins/alert-center/web-ui/.env.production+5-0
@@ -0,0 +1,5 @@
1+# 生产环境配置
2+ENV = 'production'
3+ 
4+# 生产环境
5+VITE_BASE_URL = 'http://10.10.9.221'
Aplugins/alert-center/web-ui/.eslintrc-auto-import.json+57-0
@@ -0,0 +1,57 @@
1+{
2+ "globals": {
3+ "EffectScope": true,
4+ "computed": true,
5+ "createApp": true,
6+ "customRef": true,
7+ "defineAsyncComponent": true,
8+ "defineComponent": true,
9+ "effectScope": true,
10+ "getCurrentInstance": true,
11+ "getCurrentScope": true,
12+ "h": true,
13+ "inject": true,
14+ "isProxy": true,
15+ "isReactive": true,
16+ "isReadonly": true,
17+ "isRef": true,
18+ "markRaw": true,
19+ "nextTick": true,
20+ "onActivated": true,
21+ "onBeforeMount": true,
22+ "onBeforeUnmount": true,
23+ "onBeforeUpdate": true,
24+ "onDeactivated": true,
25+ "onErrorCaptured": true,
26+ "onMounted": true,
27+ "onRenderTracked": true,
28+ "onRenderTriggered": true,
29+ "onScopeDispose": true,
30+ "onServerPrefetch": true,
31+ "onUnmounted": true,
32+ "onUpdated": true,
33+ "provide": true,
34+ "reactive": true,
35+ "readonly": true,
36+ "ref": true,
37+ "resolveComponent": true,
38+ "shallowReactive": true,
39+ "shallowReadonly": true,
40+ "shallowRef": true,
41+ "toRaw": true,
42+ "toRef": true,
43+ "toRefs": true,
44+ "triggerRef": true,
45+ "unref": true,
46+ "useAttrs": true,
47+ "useCssModule": true,
48+ "useCssVars": true,
49+ "useRoute": true,
50+ "useRouter": true,
51+ "useSlots": true,
52+ "watch": true,
53+ "watchEffect": true,
54+ "watchPostEffect": true,
55+ "watchSyncEffect": true
56+ }
57+}
Aplugins/alert-center/web-ui/.eslintrc.json+35-0
@@ -0,0 +1,35 @@
1+{
2+ "env": {
3+ "browser": true,
4+ "es2021": true
5+ },
6+ "extends": [
7+ "plugin:vue/vue3-essential",
8+ "standard",
9+ "./.eslintrc-auto-import.json"
10+ ],
11+ "parserOptions": {
12+ "ecmaVersion": "latest",
13+ "parser": "@typescript-eslint/parser",
14+ "sourceType": "module"
15+ },
16+ "plugins": [
17+ "vue",
18+ "@typescript-eslint"
19+ ],
20+ "rules": {
21+ "prefer-regex-literals": 0,
22+ "vue/multi-word-component-names": 0,
23+ "no-control-regex": 0,
24+ "indent": ["error", 4],
25+ "quotes": 0,
26+ "comma-dangle": 0,
27+ "semi": 0,
28+ "dot-notation": 0,
29+ "space-before-function-paren": 0,
30+ "prefer-const": 0,
31+ "no-trailing-spaces": 0,
32+ "func-call-spacing": 0
33+ },
34+ "ignorePatterns": ["src/**/*.d.ts"]
35+}
Aplugins/alert-center/web-ui/.gitignore+24-0
@@ -0,0 +1,24 @@
1+# Logs
2+logs
3+*.log
4+npm-debug.log*
5+yarn-debug.log*
6+yarn-error.log*
7+pnpm-debug.log*
8+lerna-debug.log*
9+ 
10+node_modules
11+dist
12+dist-ssr
13+*.local
14+ 
15+# Editor directories and files
16+.vscode/*
17+!.vscode/extensions.json
18+.idea
19+.DS_Store
20+*.suo
21+*.ntvs*
22+*.njsproj
23+*.sln
24+*.sw?
Aplugins/alert-center/web-ui/.prettierrc.json+6-0
@@ -0,0 +1,6 @@
1+{
2+ "printWidth": 120,
3+ "singleQuote": true,
4+ "semi": false,
5+ "tabWidth": 4
6+}
Aplugins/alert-center/web-ui/README.md+16-0
@@ -0,0 +1,16 @@
1+# Vue 3 + TypeScript + Vite
2+ 
3+This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4+ 
5+## Recommended IDE Setup
6+ 
7+- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
8+ 
9+## Type Support For `.vue` Imports in TS
10+ 
11+Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
12+ 
13+1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
14+2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
15+ 
16+You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
Aplugins/alert-center/web-ui/index.html+17-0
@@ -0,0 +1,17 @@
1+<!DOCTYPE html>
2+<html lang="en" class="dark">
3+ <head>
4+ <meta charset="UTF-8" />
5+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+ <title>一体化平台</title>
8+ </head>
9+ <body>
10+ <div id="app"></div>
11+ <script type="module" src="/src/main.ts"></script>
12+ <script type="module">
13+ document.getElementById('app').style.height =
14+ import.meta.env.MODE === 'production' ? 'calc(100vh - 170px)' : '100%';
15+ </script>
16+ </body>
17+</html>
Aplugins/alert-center/web-ui/package.json+67-0
@@ -0,0 +1,67 @@
1+{
2+ "name": "plugin-vem",
3+ "private": true,
4+ "version": "0.0.0",
5+ "type": "module",
6+ "scripts": {
7+ "dev": "vite --host 0.0.0.0",
8+ "build": "vue-tsc --noEmit && vite build",
9+ "buildNoTest": "vite build",
10+ "buildDev": "vite build --mode development",
11+ "preview": "vite preview",
12+ "lint": "eslint --ext .ts,.vue --fix src && vue-tsc --noEmit"
13+ },
14+ "dependencies": {
15+ "@element-plus/icons-vue": "^2.0.9",
16+ "@vueuse/core": "^9.0.0",
17+ "axios": "^0.27.2",
18+ "cron-parser": "^4.6.0",
19+ "dayjs": "^1.11.5",
20+ "echarts": "^5.4.0",
21+ "element-plus": "^2.2.15",
22+ "jsencrypt": "^3.0.0-rc.1",
23+ "lodash-es": "^4.17.21",
24+ "md5": "^2.3.0",
25+ "moment": "^2.29.4",
26+ "monaco-editor": "^0.34.0",
27+ "p-limit": "^4.0.0",
28+ "pinia": "^2.0.16",
29+ "pinia-plugin-persistedstate": "^1.6.3",
30+ "sql-formatter": "^8.2.0",
31+ "uuid": "^8.3.2",
32+ "vis": "^4.21.0-EOL",
33+ "vue": "^3.2.37",
34+ "vue-grid-layout": "^3.0.0-beta1",
35+ "vue-i18n": "9",
36+ "vue-request": "2.0.0-rc.3",
37+ "vue-router": "^4.1.2"
38+ },
39+ "devDependencies": {
40+ "@types/lodash-es": "^4.17.6",
41+ "@types/md5": "^2.3.2",
42+ "@types/node": "^18.6.1",
43+ "@types/uuid": "^8.3.4",
44+ "@types/vis": "^4.21.24",
45+ "@typescript-eslint/eslint-plugin": "^5.31.0",
46+ "@typescript-eslint/parser": "^5.31.0",
47+ "@vitejs/plugin-vue": "^3.0.0",
48+ "css-loader": "^6.7.1",
49+ "eslint": "^8.0.1",
50+ "eslint-config-prettier": "^8.5.0",
51+ "eslint-config-standard": "^17.0.0",
52+ "eslint-plugin-import": "^2.25.2",
53+ "eslint-plugin-n": "^15.0.0",
54+ "eslint-plugin-prettier": "^4.2.1",
55+ "eslint-plugin-promise": "^6.0.0",
56+ "eslint-plugin-vue": "^9.3.0",
57+ "prettier": "^2.7.1",
58+ "sass": "^1.54.0",
59+ "style-loader": "^3.3.1",
60+ "typescript": "^4.6.4",
61+ "unplugin-auto-import": "^0.10.1",
62+ "unplugin-vue-components": "^0.21.1",
63+ "vite": "^3.0.0",
64+ "vite-plugin-svg-icons": "^2.0.1",
65+ "vue-tsc": "^0.38.4"
66+ }
67+}
Aplugins/alert-center/web-ui/src/App.vue+183-0
@@ -0,0 +1,183 @@
1+<script setup lang="ts">
2+import zhCn from "element-plus/lib/locale/lang/zh-cn";
3+import en from "element-plus/lib/locale/lang/en";
4+import { useDark, useToggle } from "@vueuse/core";
5+import { useWindowStore } from "./store/window";
6+import router from './router';
7+import { i18n } from './i18n'
8+import { useMonitorStore } from "./store/monitor";
9+import { useAlertConfigStore } from "./store/alertConfig";
10+import AlertConfig from "@/components/AlertConfig.vue";
11+import { checkPrometheus } from "./api/environment";
12+import { getAlertConf } from "./api/alertConfig"
13+import { fa } from "element-plus/es/locale";
14+ 
15+const observabilitySign = 'alert-center';
16+ 
17+const themeBool = ref<boolean>(false)
18+ 
19+const isDark = useDark({
20+ // 存储到localStorage/sessionStorage中的Key 根据自己的需求更改
21+ storageKey: 'theme',
22+ // 暗黑class名字
23+ valueDark: 'dark',
24+ // 高亮class名字
25+ valueLight: 'light',
26+})
27+ 
28+useToggle(isDark);
29+ 
30+const collapse = ref(false)
31+const isFromObservability = ref<boolean>(false);
32+const mainDiv = ref<HTMLDivElement | null>();
33+const windowStore = useWindowStore();
34+const monitorStore = useMonitorStore();
35+const alertConfigEnv = useAlertConfigStore();
36+ 
37+// wujie frame special params
38+const getWujieParam = () => {
39+ // @ts-ignore
40+ if (window.$wujie) {
41+ // @ts-ignore
42+ const { id } = window.$wujie.props;
43+ if (typeof id === 'string' && id !== '') {
44+ monitorStore.instanceId = id;
45+ }
46+ }
47+}
48+ 
49+const dealUrlTo = () => {
50+ let urlTo = '/vem';
51+ const curUrl = window.location.href;
52+ if (curUrl.indexOf(observabilitySign) > -1) {
53+ isFromObservability.value = true;
54+ if (curUrl.indexOf('/vem') > -1) {
55+ urlTo = curUrl.slice(curUrl.indexOf('/vem'));
56+ }
57+ }
58+ router.replace(urlTo);
59+}
60+ 
61+const onThemeChange = (bool: boolean, themeType: string) => {
62+ themeBool.value = bool;
63+ localStorage.setItem("theme", themeType)
64+ windowStore.setTheme(themeType);
65+}
66+ 
67+const wujieInit = () => {
68+ if (localStorage.getItem("locale") === "en-US") {
69+ i18n.global.locale.value = 'en';
70+ } else {
71+ i18n.global.locale.value = 'zhCn';
72+ }
73+ if (localStorage.getItem("opengauss-theme") === "dark") {
74+ onThemeChange(false, 'dark');
75+ } else {
76+ onThemeChange(true, 'auto');
77+ }
78+}
79+ 
80+onMounted(async () => {
81+ new ResizeObserver(() => {
82+ windowStore.setMainHeight(mainDiv.value!.clientHeight);
83+ windowStore.setMainWidth(mainDiv.value!.clientWidth);
84+ }).observe(mainDiv.value!);
85+ dealUrlTo();
86+ getWujieParam();
87+ 
88+ // @ts-ignore
89+ const wujie = window.$wujie;
90+ // Judge whether it is a plug-in environment or a local environment through wujie
91+ if (wujie) {
92+ localStorage.setItem("INSTANCE_CURRENT_MODE", "wujie")
93+ 
94+ // init theme and language
95+ wujieInit();
96+ 
97+ // Monitoring platform theme changes
98+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
99+ if (val === 'dark') {
100+ onThemeChange(false, 'dark');
101+ } else {
102+ onThemeChange(true, 'auto');
103+ }
104+ })
105+ 
106+ // Monitoring platform language change
107+ wujie?.bus.$on('opengauss-locale-change', (val: string) => {
108+ if (val === "en-US") {
109+ i18n.global.locale.value = 'en';
110+ } else {
111+ i18n.global.locale.value = 'zhCn';
112+ }
113+ })
114+ } else {
115+ // local default
116+ localStorage.setItem("INSTANCE_CURRENT_MODE", "local")
117+ }
118+ if (!alertConfigEnv.isInstall || !alertConfigEnv.isConfig) {
119+ if (!alertConfigEnv.isInstall) {
120+ await checkPrometheus().then(async (res: any) => {
121+ if (res && res.code === 200) {
122+ alertConfigEnv.isInstall = true
123+ } else {
124+ alertConfigEnv.isInstall = false
125+ }
126+ }).catch(() => {
127+ alertConfigEnv.isInstall = false
128+ })
129+ }
130+ if (!alertConfigEnv.isConfig) {
131+ await getAlertConf().then(async (res: any) => {
132+ if (res && res.code === 200 && res.data && res.data.alertIp && res.data.alertPort) {
133+ alertConfigEnv.isConfig = true
134+ } else {
135+ alertConfigEnv.isConfig = false
136+ }
137+ }).catch(() => {
138+ alertConfigEnv.isConfig = false
139+ })
140+ }
141+ alertConfigEnv.showMain = alertConfigEnv.isInstall && alertConfigEnv.isConfig
142+ }
143+})
144+</script>
145+
146+<template>
147+ <el-config-provider size="small" :locale="$i18n.locale === 'zhCn' ? zhCn : en">
148+ <div class="container">
149+ <aside v-if="!isFromObservability">
150+ <my-menu :collapse="collapse" />
151+ </aside>
152+ <div class="main" ref="mainDiv">
153+ <div class="page" v-if="alertConfigEnv.showMain"><router-view />
154+ </div>
155+ <AlertConfig class="page" v-else />
156+ </div>
157+ </div>
158+ </el-config-provider>
159+</template>
160+ 
161+<style scoped lang="scss">
162+.container {
163+ /* height: calc(100% - var(--vem-header-height)); */
164+ height: 100%;
165+ overflow: hidden;
166+ display: flex;
167+ flex-flow: row nowrap;
168+}
169+ 
170+.main {
171+ flex: 1;
172+ min-width: 0;
173+ background-color: var(--el-bg-color);
174+ display: flex;
175+ flex-direction: column;
176+}
177+ 
178+.page {
179+ padding: 16px;
180+ height: calc(100% - var(--vem-header-height));
181+ overflow: auto;
182+}
183+</style>
Aplugins/alert-center/web-ui/src/api/alertConfig.ts+5-0
@@ -0,0 +1,5 @@
1+import request from '@/request';
2+ 
3+export async function getAlertConf(): Promise<any> {
4+ return request.get("/alertCenter/api/v1/alertConf")
5+}
Aplugins/alert-center/web-ui/src/api/alertRecord.ts+2-0
@@ -0,0 +1,2 @@
1+import request from "@/request";
2+ 
Aplugins/alert-center/web-ui/src/api/environment.ts+5-0
@@ -0,0 +1,5 @@
1+import request from '@/request';
2+ 
3+export async function checkPrometheus(): Promise<any> {
4+ return request.get("/alertCenter/api/v1/environment/checkPrometheus")
5+}
Aplugins/alert-center/web-ui/src/assets/img/VastEM-login.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/avail.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/close.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/error.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/large.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/login-bg.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/noresult.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/normal.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/small.png+0-0
Aplugins/alert-center/web-ui/src/assets/img/working.png+0-0
Aplugins/alert-center/web-ui/src/assets/style/color.module.scss+33-0
@@ -0,0 +1,33 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+$--charts-1: #246cff;
5+$--charts-2: #37c461;
6+$--charts-3: #ffbe00;
7+$--charts-4: #ff7200;
8+$--charts-5: #3dc7ff;
9+$--charts-6: #d13fd9;
10+$--charts-7: #975fe4;
11+$--charts-8: #465d86;
12+$--charts-9: #00abaa;
13+$--charts-10: #9ae75d;
14+$--charts-11: #f08bb4;
15+ 
16+$--charts-extend-1: #cdddfd;
17+$--charts-extend-2: #cdf3e4;
18+$--charts-extend-3: #fcebb9;
19+$--charts-extend-4: #ffd4c7;
20+$--charts-extend-5: #d3eef9;
21+$--charts-extend-6: #edb2f0;
22+$--charts-extend-7: #decfea;
23+$--charts-extend-8: #ced4de;
24+$--charts-extend-9: #bbdede;
25+$--charts-extend-10: #d7f5be;
26+$--charts-extend-11: #ffe0ed;
27+ 
28+:export {
29+ chartColors: $--charts-1, $--charts-2, $--charts-3, $--charts-4, $--charts-5, $--charts-7, $--charts-8, $--charts-9,
30+ $--charts-6, $--charts-10, $--charts-11, $--charts-extend-1, $--charts-extend-2, $--charts-extend-3,
31+ $--charts-extend-4, $--charts-extend-5, $--charts-extend-6, $--charts-extend-7, $--charts-extend-8,
32+ $--charts-extend-9, $--charts-extend-10, $--charts-extend-11;
33+}
Aplugins/alert-center/web-ui/src/assets/style/color.scss+29-0
@@ -0,0 +1,29 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+$og-primary-color: #5c5c5c;
5+$og-sub-primary-color: #0093ff;
6+$og-success-color: #52c41a;
7+$og-error-color: #ff4d4f;
8+$og-border-color: #4a4a4a;
9+$og-sub-border-color: #bcbcbc;
10+$og-button-border-color: #353535;
11+$og-text-color: #d4d4d4;
12+$og-disabled-text-color: #b3b3b3;
13+$og-background-color: #212121;
14+$og-sub-background-color: #424242;
15+$og-hover-background-color: #323e43;
16+$og-menu-color: #cfcfcf;
17+$og-button-active-color: #7d7d7d;
18+$og-content-color: #303030;
19+ 
20+$og-sub-text-color: #00bcd4;
21+$og-svg-fill-color: #00bcd4;
22+ 
23+$--primary-6: #e41d1d;
24+$--primary-5: #e94842;
25+$--primary-7: #be1118;
26+$--primary-4: #fac4bc;
27+$--primary-3: #f49c92;
28+$--primary-2: #f49c92;
29+$--primary-1: #ffece8;
Aplugins/alert-center/web-ui/src/assets/style/common.scss+240-0
@@ -0,0 +1,240 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+@use 'sass:math';
5+@use './color.scss' as *;
6+@use 'sass:map';
7+ 
8+// page-header
9+.page-header {
10+ display: flex;
11+ flex-direction: row;
12+ align-items: center;
13+ padding-bottom: 7px;
14+ border-bottom: 1px solid var(--border-1);
15+ 
16+ .icon {
17+ width: 4px;
18+ height: 14px;
19+ background: var(--primary-6);
20+ border-radius: 1px;
21+ margin-right: 4px;
22+ }
23+ .title {
24+ font-family: 'Source Han Sans CN';
25+ font-style: normal;
26+ font-weight: 700;
27+ font-size: 16px;
28+ line-height: 22px;
29+ height: 22px;
30+ /* identical to box height, or 138% */
31+ letter-spacing: -0.01px;
32+ color: var(--text-1);
33+ }
34+ .seperator {
35+ margin: 0px 16px;
36+ width: 18px;
37+ height: 0px;
38+ border: 1px solid var(--border-1);
39+ transform: rotate(90deg);
40+ }
41+ .cluster-title {
42+ height: 22px;
43+ font-family: 'Source Han Sans CN';
44+ font-style: normal;
45+ font-weight: 400;
46+ font-size: 14px;
47+ line-height: 22px;
48+ color: var(--text-1);
49+ }
50+ .instance-info {
51+ margin-left: 4px;
52+ }
53+}
54+ 
55+// card margin bottom
56+.card-margin-bottom {
57+ margin-bottom: 16px;
58+}
59+.card-links {
60+ .el-link {
61+ margin-right: 10px;
62+ }
63+}
64+ 
65+// tabs in card
66+.card-tabs {
67+ &.el-tabs {
68+ --el-tabs-header-height: 32px;
69+ > .el-tabs__header {
70+ .el-tabs__item {
71+ padding: 0px 16px !important;
72+ }
73+ }
74+ }
75+}
76+ 
77+// table in card
78+.table-in-card {
79+ padding: 0px 16px;
80+ .el-table--border {
81+ border: 1px solid rgba(22, 93, 255, 0.4);
82+ }
83+}
84+ 
85+// text in card
86+.text-in-card {
87+ padding: 8px 16px;
88+ .text-row {
89+ display: flex;
90+ flex-direction: row;
91+ .label {
92+ width: 115px;
93+ text-align: right;
94+ }
95+ }
96+}
97+ 
98+// normal grid header
99+.grid-header {
100+ background: var(--background-color-2) !important;
101+ .cell {
102+ line-height: 22px !important;
103+ }
104+}
105+ 
106+// grid header when grid is in card
107+.grid-header-in-card {
108+ background: var(--background-color-3) !important;
109+ .cell {
110+ line-height: 22px !important;
111+ }
112+}
113+ 
114+.table-link {
115+ color: var(--link-6);
116+ cursor: pointer;
117+}
118+ 
119+.el-tabs__item.is-disabled {
120+ cursor: auto !important;
121+}
122+ 
123+// table
124+.normal-table {
125+ .operate-btns {
126+ display: flex;
127+ justify-content: space-around;
128+ }
129+}
130+.search-form {
131+ display: flex;
132+ justify-content: flex-end;
133+ align-items: center;
134+ margin-bottom: 20px;
135+ .search-time-range {
136+ width: 300px;
137+ }
138+ .filter {
139+ display: flex;
140+ align-items: center;
141+ }
142+ .filter:not(:last-child) {
143+ margin-right: 15px;
144+ }
145+ .seperator {
146+ margin: auto;
147+ }
148+}
149+.search-form-multirow {
150+ .row {
151+ display: flex;
152+ justify-content: flex-end;
153+ align-items: center;
154+ .search-time-range {
155+ width: 300px;
156+ }
157+ .filter {
158+ display: flex;
159+ align-items: center;
160+ }
161+ .filter:not(:last-child) {
162+ margin-right: 15px;
163+ }
164+ .seperator {
165+ margin: auto;
166+ }
167+ margin-bottom: 10px;
168+ }
169+ margin-bottom: 10px;
170+}
171+ 
172+.refresh-bar {
173+ &-container {
174+ position: absolute;
175+ right: 0px;
176+ top: 0px;
177+ display: flex;
178+ margin-bottom: 10px;
179+ justify-content: end;
180+ overflow: hidden;
181+ font-size: 12px;
182+ }
183+ 
184+ &-filter {
185+ font-size: 12px;
186+ width: 600px;
187+ z-index: 10;
188+ padding-right: 16px;
189+ display: flex;
190+ align-items: center;
191+ padding: 0 10px;
192+ height: 30px;
193+ > div:not(:last-of-type),
194+ > span,
195+ > button {
196+ margin-right: 4px;
197+ }
198+ 
199+ :deep(.el-button .el-icon svg) {
200+ color: var(--el-color-icon-refresh-color);
201+ }
202+ 
203+ :deep(.el-select .el-input.is-focus .el-input__wrapper) {
204+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
205+ }
206+ :deep(.el-select-dropdown__item.selected) {
207+ color: var(--el-color-tabbar-active) !important;
208+ }
209+ :deep(.el-range-editor.is-active) {
210+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
211+ }
212+ }
213+ 
214+ :deep(.el-range-input) {
215+ background-color: var(--el-bg-color);
216+ }
217+ 
218+ :deep(.el-date-editor--datetimerange) {
219+ width: 100px;
220+ background-color: var(--el-bg-color);
221+ }
222+}
223+ 
224+.refresh-button.el-button.el-button--primary {
225+ background-color: var(--button-1-background) !important;
226+ border: 1px solid var(--button-1-border) !important;
227+ color: var(--button-1-color) !important;
228+ padding: 5px !important;
229+}
230+ 
231+.divider {
232+ height: 24px;
233+ width: 1px;
234+ margin: 0 8px !important;
235+ background-color: var(--el-color-divider-border-color);
236+}
237+ 
238+.gap-row {
239+ margin-bottom: 16px;
240+}
Aplugins/alert-center/web-ui/src/assets/style/dark.scss+121-0
@@ -0,0 +1,121 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+@use 'sass:math';
5+@use './color.scss' as *;
6+@use 'sass:map';
7+ 
8+html.dark {
9+ --hw-primary-1: #424242;
10+ --el-color-tabbar-active: #d4d4d4;
11+ --el-color-button-small-bg: #232324;
12+ --el-color-button-small-border: #5c5c5c;
13+ --el-color-card-header-color: #424242;
14+ --el-color--col-border-color: #4a4a4a;
15+ --el-color-line-text-color: #d4d4d4;
16+ --el-color-icon-refresh-color: #fff;
17+ --el-color-table-row-bg-color: #4e575a;
18+ --el-color-divider-border-color: #807f80;
19+ 
20+ --el-color-primary: #409eff;
21+ --el-color-success: #67c23a;
22+ --el-bg-color: #232324;
23+ --el-bg-color-og: #212121;
24+ --el-bg-color-og-hover: #ffffff;
25+ --el-bg-color-sub: #424242;
26+ --el-bg-color-sub-white: #232324;
27+ --el-bg-color-page: #303030;
28+ --el-bg-color-overlay: #303030;
29+ --el-color-primary: #909399;
30+ --el-color-success: #67c23a;
31+ --el-color-warning: #e6a23c;
32+ --el-color-danger: #f56c6c;
33+ --el-color-error: #f56c6c;
34+ --el-color-info: #909399;
35+ --el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.36), 0px 8px 20px rgba(0, 0, 0, 0.72);
36+ --el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.72);
37+ --el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.72);
38+ --el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, 0px 8px 16px -8px #000000;
39+ --el-text-color-og: #d4d4d4;
40+ --el-text-color-primary: #e5eaf3;
41+ --el-text-color-regular: #cfd3dc;
42+ --el-text-color-secondary: #a3a6ad;
43+ --el-text-color-placeholder: #8d9095;
44+ --el-text-color-disabled: #6c6e72;
45+ --el-border-color-darker: #636466;
46+ --el-border-color-dark: #58585b;
47+ --el-border-color: #4c4d4f;
48+ --el-border-color-light: #414243;
49+ --el-border-color-lighter: #363637;
50+ --el-border-color-extra-light: #2b2b2c;
51+ --el-fill-color-darker: #424243;
52+ --el-fill-color-dark: #39393a;
53+ --el-fill-color: #303030;
54+ --el-fill-color-light: #262727;
55+ --el-fill-color-lighter: #1d1d1d;
56+ --el-fill-color-extra-light: #191919;
57+ --el-fill-color-blank: transparent;
58+ --el-mask-color: rgba(0, 0, 0, 0.8);
59+ --el-mask-color-extra-light: rgba(0, 0, 0, 0.3);
60+ --el-button-color-small: #5c5c5c;
61+ --el-table-cell: #909399;
62+ --el-button-text-color: #ffffff;
63+ --el-og-border-color: #4a4a4a;
64+ 
65+ --color-text-1: #ffffff;
66+ --color-text-2: rgb(79, 91, 107);
67+ --color-text-3: rgb(134, 143, 156);
68+ --el-dialog-background-color: rgb(42, 42, 43);
69+ --primary-6: rgb(252, 239, 146);
70+ --main-background-color: rgb(35, 35, 36);
71+ --color-neutral-2: rgb(52, 52, 53);
72+ --color-fill-2: var(--color-neutral-2);
73+ --color-text-2: hsla(0, 0%, 100%, 0.7);
74+ --gray-9: rgb(223, 223, 223);
75+ --color-secondary: rgba(223, 223, 223, 0.08);
76+ --color-primary-light-1: rgba(252, 239, 146, 0.2);
77+ --el-color-primary: var(--primary-6);
78+ --el-color-primary-light-3: var(--primary-6);
79+ --el-menu-text-color: #cfcfcf;
80+ --el-menu-hover-text-color: #cfcfcf;
81+ --el-menu-bg-color: #212121;
82+ --el-menu-hover-bg-color: #323e43;
83+ --el-table-cell-center-background: #424242;
84+ --el-color-primary-light-3: var(--primary-6);
85+ --color-bg-2: #232324;
86+}
87+ 
88+:root.dark {
89+ --el-menu-active-color: #cfcfcf;
90+ --el-menu-text-color: #cfcfcf;
91+ --el-menu-hover-text-color: #cfcfcf;
92+ --el-menu-bg-color: #212121;
93+ --el-menu-hover-bg-color: #323e43;
94+ 
95+ --el-dialog-background-color: rgb(42, 42, 43);
96+ --primary-6: rgb(252, 239, 146);
97+ --main-background-color: rgb(35, 35, 36);
98+ --color-neutral-2: rgb(52, 52, 53);
99+ --color-fill-2: var(--color-neutral-2);
100+ --color-text-2: hsla(0, 0%, 100%, 0.7);
101+ --gray-9: rgb(223, 223, 223);
102+ --color-secondary: rgba(223, 223, 223, 0.08);
103+ --color-primary-light-1: rgba(252, 239, 146, 0.2);
104+ --el-color-primary: var(--primary-6);
105+ --el-color-primary-light-3: var(--primary-6);
106+ --el-table-cell-center-background: #424242;
107+ --el-color-primary-light-3: var(--primary-6);
108+ --color-bg-2: #232324;
109+ 
110+ // dashboardv2
111+ --color-1: #FFFFFF;
112+ --border-1: #484849;
113+ --border-2: #2ba1fa;
114+ --background-color-1: #484949;
115+ --background-color-2: #2e2d2f;
116+ --background-color-3: #484949;
117+ --text-1: #f5f6f6;
118+ --button-1-color: #ffffff;
119+ --button-1-background: #232324;
120+ --button-1-border: #484949;
121+}
Aplugins/alert-center/web-ui/src/assets/style/reset.scss+255-0
@@ -0,0 +1,255 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+@use 'sass:math';
5+@use './color.scss' as *;
6+@use 'sass:map';
7+ 
8+html,
9+body {
10+ margin: 0;
11+ padding: 0;
12+ height: 100%;
13+ font-size: 14px;
14+ position: relative;
15+}
16+::-webkit-scrollbar {
17+ width: 0;
18+ height: 20px;
19+}
20+ 
21+.el-pagination.is-background {
22+ .el-pager li:not(.is-disabled) {
23+ border: 1px solid $og-sub-border-color !important;
24+ &.is-active {
25+ color: $og-sub-primary-color !important;
26+ border-color: $og-sub-primary-color !important;
27+ }
28+ &:hover {
29+ border-color: $og-sub-primary-color !important;
30+ }
31+ }
32+ &.el-pagination--small .el-pager li {
33+ min-width: 32px !important;
34+ }
35+}
36+.el-overlay,
37+.el-message,
38+.el-popper {
39+ z-index: 10000000 !important;
40+}
41+.el-dialog {
42+ .el-dialog__header {
43+ border-bottom: 1px solid $og-sub-border-color !important;
44+ margin-right: 0px !important;
45+ padding-top: 10px !important;
46+ font-size: 14px !important;
47+ }
48+ .el-dialog__title {
49+ font-size: 14px !important;
50+ }
51+ .el-dialog__headerbtn {
52+ top: -2px !important;
53+ }
54+ .el-dialog__footer {
55+ border-top: 1px solid $og-sub-border-color !important;
56+ padding-bottom: 10px !important;
57+ }
58+}
59+.el-menu-item.is-active {
60+ background-color: var(--el-bg-color-og-hover) !important;
61+ border-right: 2px solid $og-primary-color !important;
62+ color: var(--el-text-og-regular);
63+}
64+.el-menu--collapse .el-sub-menu__title {
65+ span,
66+ .el-sub-menu__icon-arrow {
67+ display: none !important;
68+ }
69+}
70+.el-popper:focus {
71+ outline: none !important;
72+}
73+#app {
74+ height: 100%;
75+ --vem-header-height: 30px;
76+ --og-sub-text-color: #{$og-sub-text-color};
77+ color: var(--el-text-color-regular);
78+}
79+:root {
80+ --el-bg-color-overlay: $og-background-color;
81+ --el-color-primary: $og-primary-color;
82+ --el-text-color-primary: var(--el-text-color-og);
83+ @for $i from 1 through 9 {
84+ --el-color-primary-light-#{$i}: #{mix(#fff, #732fcb, math.percentage(math.div($i, 10)))};
85+ }
86+}
87+.el-picker-panel .el-button {
88+ background-color: var(--el-bg-color-sub) !important;
89+ border-color: $og-button-active-color !important;
90+ color: var(--el-table-cell) !important;
91+}
92+.el-button {
93+ height: auto !important;
94+ &:active {
95+ background-color: $og-button-active-color !important;
96+ border-color: $og-button-active-color !important;
97+ }
98+ &.is-text:not(.is-disabled).is-has-bg {
99+ background-color: transparent !important;
100+ padding: 0 !important;
101+ }
102+}
103+.el-picker-panel__link-btn {
104+ color: var(--el-button-text-color) !important;
105+}
106+.el-button.is-text {
107+ color: var(--el-button-text-color) !important;
108+}
109+.el-tabs__header {
110+ background-color: var(--el-bg-color-sub-white) !important;
111+}
112+.el-tabs__item {
113+ padding: 0 16px !important;
114+ font-size: 14px !important;
115+ color: var(--el-text-color-og) !important;
116+}
117+.el-tabs__active-bar {
118+ background-color: var(--primary-6) !important;
119+ height: 3px !important;
120+}
121+.is-active {
122+ color: var(--primary-6) !important;
123+}
124+.el-input__inner {
125+ color: var(--el-text-color-og) !important;
126+}
127+.el-progress {
128+ &-bar__inner {
129+ background-color: $og-sub-primary-color !important;
130+ }
131+}
132+.el-range-editor.is-disabled {
133+ border-color: $og-border-color !important;
134+}
135+// 弹窗loading
136+.el-loading-spinner .el-loading-text {
137+ color: $og-sub-primary-color !important;
138+}
139+.el-loading-spinner .path {
140+ stroke: $og-sub-primary-color !important;
141+}
142+.el-button--small {
143+// background-color: var(--el-button-color-small) !important;
144+ border: none !important;
145+ color: var(--el-text-color-og);
146+}
147+.sql-popover-tip {
148+ max-width: 300px !important;
149+ color: var(--el-text-color-og) !important;
150+}
151+.el-picker-panel__icon-btn .el-icon {
152+ color: var(--el-text-color-og);
153+}
154+.in-active-path .el-cascader-node__label {
155+ color: var(--el-color-tabbar-active) !important;
156+}
157+.el-cascader .el-input.is-focus {
158+ border: none !important;
159+}
160+.el-cascader .el-input.is-focus .el-input__wrapper {
161+ border: none !important;
162+ box-shadow: 0 0 0 1px var(--hw-primary-1, var(--hw-primary-1)) inset !important;
163+}
164+ 
165+.el-select-dropdown__item.selected {
166+ color: var(--el-color-tabbar-active) !important;
167+}
168+.el-cascader-panel .in-active-path .el-icon svg {
169+ color: var(--hw-primary-1);
170+}
171+ 
172+// textarea
173+.el-textarea__inner {
174+ background-color: var(--color-fill-2) !important;
175+}
176+.el-textarea__inner:focus {
177+ background-color: var(--main-background-color) !important;
178+}
179+ 
180+.el-table .cell {
181+ color: var(--color-1) !important;
182+}
183+.el-input__wrapper.is-active {
184+ background-color: var(--main-background-color) !important;
185+}
186+.el-input__wrapper.is-active .el-range-input {
187+ background-color: var(--main-background-color) !important;
188+}
189+ 
190+// pagination
191+.el-pagination {
192+ display: flex;
193+ justify-content: flex-end;
194+}
195+.el-pagination.is-background .el-pager li:not(.is-disabled) {
196+ border: 0 !important;
197+ background-color: rgb(255, 255, 255, 0);
198+}
199+.el-pagination.is-background .el-pager li:not(.is-disabled).is-active {
200+ color: var(--primary-6) !important;
201+ background-color: var(--color-primary-light-1) !important;
202+}
203+.btn-prev {
204+ background-color: rgb(255, 255, 255, 0) !important;
205+}
206+.btn-next {
207+ background-color: rgb(255, 255, 255, 0) !important;
208+}
209+ 
210+// el-link
211+.el-link {
212+ font-size: 12px !important;
213+}
214+// table
215+.table-wrapper-table-id {
216+ color: var(--primary-6) !important;
217+ cursor: pointer;
218+}
219+ 
220+// check-box
221+// .el-checkbox__inner::after {
222+// border-color: var(--color-bg-2) !important;
223+// }
224+ 
225+// button for message box
226+.el-message-box__btns .el-button.el-button--primary {
227+ color: var(--color-bg-2) !important;
228+ background-color: var(--primary-6) !important;
229+}
230+.el-message-box__btns .el-button.el-button--primary.search-button {
231+ color: var(--primary-6) !important;
232+ background-color: rgb(255, 255, 255, 0) !important;
233+ border: 1px solid var(--primary-6) !important;
234+}
235+ 
236+// Element Button Reset
237+// .el-button {
238+// font-size: 14px !important;
239+// padding: 8px 16px !important;
240+// border: 1px solid var(--button-1-border) !important;
241+// border-radius: 2px;
242+// font-weight: 400 !important;
243+// color: var(--button-1-color) !important;
244+// background-color: var(--button-1-background) !important;
245+// }
246+// .el-button.el-button--primary {
247+// color: var(--color-bg-2) !important;
248+// background-color: var(--primary-6) !important;
249+// }
250+// .el-button.el-button--primary.search-button {
251+// color: var(--primary-6) !important;
252+// background-color: rgb(255, 255, 255, 0) !important;
253+// border: 1px solid var(--primary-6) !important;
254+// }
255+ 
Aplugins/alert-center/web-ui/src/assets/style/style1.scss+46-0
@@ -0,0 +1,46 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+@use 'sass:math';
5+@use './color.scss' as *;
6+@use 'sass:map';
7+ 
8+.el-link.el-link--primary {
9+ color: var(--primary-6) !important;
10+}
11+:deep(.el-pagination) {
12+ display: flex;
13+ justify-content: flex-end;
14+}
15+.deleteBtn {
16+ color: #d4d4d4;
17+}
18+.deleteBtn-icon {
19+ position: relative;
20+ top: 2px;
21+ right: 5px;
22+}
23+:deep(.el-range-editor--small.el-input__wrapper) {
24+ width: 240px;
25+}
26+ 
27+.dialog {
28+ &:deep(.el-dialog .el-dialog__header) {
29+ text-align: center;
30+ }
31+ &:deep(.el-form-item--small .el-form-item__label) {
32+ width: 110px;
33+ }
34+ &:deep(.el-dialog .el-dialog__footer) {
35+ border: none !important;
36+ }
37+ .form-textarea {
38+ width: 100%;
39+ &:deep(.el-textarea__inner) {
40+ height: 100px;
41+ }
42+ }
43+ .option-wrap {
44+ margin-right: 20px;
45+ }
46+}
Aplugins/alert-center/web-ui/src/assets/style/theme.scss+111-0
@@ -0,0 +1,111 @@
1+/**
2+ * Copyright (c) 2023 Huawei Technologies Co.,Ltd.
3+ */
4+@use 'sass:math';
5+@use './color.scss' as *;
6+@use 'sass:map';
7+ 
8+@forward 'element-plus/theme-chalk/src/common/var.scss' with (
9+ $bg-color: (
10+ '': #fefefe,
11+ 'page': #fefefe,
12+ 'overlay': #fefefe,
13+ )
14+);
15+ 
16+html {
17+ --hw-primary-1: #e41d1d;
18+ --el-color-tabbar-active: #e41d1d;
19+ --el-color-button-small-bg: #fff;
20+ --el-color-button-small-border: #e41d1d;
21+ --el-color-card-header-color: #f7f8fa;
22+ --el-color--col-border-color: #ddd;
23+ --el-color-line-text-color: #868f9c;
24+ --el-color-icon-refresh-color: #868f9c;
25+ --el-color-table-row-bg-color: #f7f8fa;
26+ --el-color-divider-border-color: #ddd;
27+ 
28+ --el-bg-color-sub: #ddd;
29+ --el-bg-color-sub-white: #fff;
30+ --el-bg-color-og: #fefefe;
31+ --el-bg-color-og-hover: #ccc;
32+ --el-text-color-og: #333;
33+ --el-button-color-small: #ddd;
34+ --el-table-cell: #333;
35+ --el-color-primary: var(--primary-6);
36+ --el-button-text-color: #333;
37+ --el-og-border-color: #d9d9d9;
38+ 
39+ --primary-5: #e94842;
40+ --primary-7: #be1118;
41+ --primary-4: #fac4bc;
42+ --primary-3: #f49c92;
43+ --primary-2: #f49c92;
44+ --primary-1: #ffece8;
45+ --color-hw-text-1: #ffffff;
46+ --color-hw-text-2: #1d212a;
47+ --color-hw-text-3: #4e5a6a;
48+ --color-hw-text-4: #4e5a6a;
49+ --color-hw-text-5: #868f9c;
50+ --color-hw-text-6: #f2f3f5;
51+ --warning-6: #ff7d01;
52+ --success-6: #00b429;
53+ --link-6: #e41d1d;
54+ --color-text-1: rgb(28, 32, 41);
55+ --color-text-3: rgb(134, 143, 156);
56+ --dark-primary-6: #e41d1d;
57+ --dark-link-6: #e41d1d;
58+ --el-bg-color-sub: rgb(247, 248, 250);
59+ --el-bg-color-og: rgb(254, 254, 254);
60+ 
61+ --color-1: #4E5969;
62+ --border-1: #eaebee;
63+ --border-2: #165dff;
64+ --background-color-1: #ffffff;
65+ --background-color-2: #F7F7F7;
66+ --background-color-3: #f4faff;
67+ --text-1: #1d2129;
68+ --button-1-color: #4e5969;
69+ --button-1-background: #ffffff;
70+ --button-1-border: #d9d9d9;
71+}
72+ 
73+:root {
74+ --el-menu-active-color: #333;
75+ --el-menu-text-color: #333;
76+ --el-menu-bg-color: #eee;
77+ --el-menu-item-height: 40px;
78+ --el-menu-sub-item-height: 40px;
79+ --el-menu-horizontal-sub-item-height: 36px;
80+ --el-menu-item-font-size: var(--el-font-size-base);
81+ --el-menu-item-hover-fill: var(--el-color-primary-light-9);
82+ --el-menu-border-color: var(--el-border-color);
83+ --el-menu-base-level-padding: 20px;
84+ --el-menu-level-padding: 20px;
85+ --el-menu-icon-width: 24px;
86+ 
87+ --primary-6: #e41d1d;
88+ --el-dialog-background-color: rgb(255, 255, 255);
89+ --main-background-color: #ffffff;
90+ --color-neutral-2: rgb(242, 243, 245);
91+ --color-fill-2: var(--color-neutral-2);
92+ --color-text-2: rgb(79, 91, 107);
93+ --color-secondary: var(--color-neutral-2);
94+ --color-primary-light-1: #ffece8;
95+ --el-color-primary: var(--primary-6);
96+ --el-color-primary-light-3: var(--primary-6);
97+ --el-table-cell-center-background: #ffffff;
98+ --color-bg-2: #fff;
99+ 
100+ // dashboardv2
101+ --color-1: #4E5969;
102+ --border-1: #eaebee;
103+ --border-2: #165dff;
104+ --background-color-1: #ffffff;
105+ --background-color-2: #F7F7F7;
106+ --background-color-3: #f4faff;
107+ --text-1: #1d2129;
108+ --button-1-color: #4e5969;
109+ --button-1-background: #ffffff;
110+ --button-1-border: #d9d9d9;
111+}
Aplugins/alert-center/web-ui/src/assets/svg/arrow.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="8" height="6" viewBox="0 0 8 6" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.59649 5.33556C4.1891 5.61949 3.6246 5.57975 3.26119 5.21634L0.306018 2.26117C-0.102006 1.85315 -0.102006 1.19161 0.306018 0.783586C0.714042 0.375561 1.37558 0.375561 1.7836 0.783586L3.99999 2.99997L6.2164 0.783557C6.62442 0.375533 7.28596 0.375533 7.69398 0.783557C8.10201 1.19158 8.10201 1.85312 7.69398 2.26114L4.73881 5.21632C4.73389 5.22123 4.72894 5.22609 4.72395 5.23089C4.68365 5.26964 4.64101 5.30453 4.59649 5.33556Z" fill="#BCBCBC"/>
3+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/bulb-on.svg+4-0
@@ -0,0 +1,4 @@
1+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<rect width="32" height="32" rx="4" fill="#424242"/>
3+<path d="M12.1375 19.0591C9.955 17.8764 8.5 15.7229 8.5 13.2631C8.5 9.528 11.8563 6.5 16 6.5C20.1438 6.5 23.5 9.528 23.5 13.2631C23.5 15.7229 22.0431 17.8764 19.8625 19.0591C19.7406 19.6647 19.4481 20.2133 19.03 20.6693H12.97C12.5473 20.2118 12.2594 19.6563 12.1356 19.0591H12.1375ZM21.3569 13.9061C21.3649 12.5484 20.8051 11.2433 19.8007 10.2774C18.7962 9.31149 17.4292 8.76392 16 8.75497C15.5256 8.75497 15.2856 8.97049 15.2856 9.39798C15.2856 9.50485 15.5256 9.71859 16 9.71859C18.2875 9.71859 20.2863 11.5229 20.2863 13.9061C20.2863 14.013 20.5263 14.2285 20.9988 14.2285C21.1169 14.2285 21.3569 14.013 21.3569 13.9079V13.9061ZM12.7862 22.2777C12.7862 21.9232 13.105 21.6364 13.4988 21.6364H18.5013C18.895 21.6364 19.2137 21.9214 19.2137 22.2777C19.2137 22.6339 18.895 22.9225 18.5013 22.9225H13.4988C13.4075 22.9248 13.3166 22.9101 13.2314 22.879C13.1461 22.8479 13.0682 22.8012 13.002 22.7415C12.9358 22.6817 12.8827 22.6102 12.8456 22.5309C12.8086 22.4516 12.7884 22.3662 12.7862 22.2794V22.2777ZM18.88 23.8896C18.355 24.8426 17.2619 25.4998 16 25.4998C15.4111 25.5059 14.8317 25.3592 14.3241 25.0754C13.8166 24.7917 13.4003 24.3817 13.12 23.8896H18.88Z" fill="#177DDC"/>
4+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/bulb.svg+4-0
@@ -0,0 +1,4 @@
1+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<rect width="32" height="32" rx="4" fill="#424242"/>
3+<path d="M12.1375 19.0591C9.955 17.8764 8.5 15.7229 8.5 13.2631C8.5 9.528 11.8563 6.5 16 6.5C20.1438 6.5 23.5 9.528 23.5 13.2631C23.5 15.7229 22.0431 17.8764 19.8625 19.0591C19.7406 19.6647 19.4481 20.2133 19.03 20.6693H12.97C12.5473 20.2118 12.2594 19.6563 12.1356 19.0591H12.1375ZM21.3569 13.9061C21.3649 12.5484 20.8051 11.2433 19.8007 10.2774C18.7962 9.31149 17.4292 8.76392 16 8.75497C15.5256 8.75497 15.2856 8.97049 15.2856 9.39798C15.2856 9.50485 15.5256 9.71859 16 9.71859C18.2875 9.71859 20.2863 11.5229 20.2863 13.9061C20.2863 14.013 20.5263 14.2285 20.9988 14.2285C21.1169 14.2285 21.3569 14.013 21.3569 13.9079V13.9061ZM12.7862 22.2777C12.7862 21.9232 13.105 21.6364 13.4988 21.6364H18.5013C18.895 21.6364 19.2137 21.9214 19.2137 22.2777C19.2137 22.6339 18.895 22.9225 18.5013 22.9225H13.4988C13.4075 22.9248 13.3166 22.9101 13.2314 22.879C13.1461 22.8479 13.0682 22.8012 13.002 22.7415C12.9358 22.6817 12.8827 22.6102 12.8456 22.5309C12.8086 22.4516 12.7884 22.3662 12.7862 22.2794V22.2777ZM18.88 23.8896C18.355 24.8426 17.2619 25.4998 16 25.4998C15.4111 25.5059 14.8317 25.3592 14.3241 25.0754C13.8166 24.7917 13.4003 24.3817 13.12 23.8896H18.88Z" fill="#BCBCBC"/>
4+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/circle1.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<circle cx="4" cy="4" r="4" fill="#B8B8B8"/>
3+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/circle2.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<circle cx="4" cy="4" r="4" fill="#FF7D00"/>
3+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/circle3.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<circle cx="4" cy="4" r="4" fill="#F53F3F"/>
3+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/cpu.svg+6-0
@@ -0,0 +1,6 @@
1+<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
2+<path d="M33.75 32.5H6.25C5.91848 32.5 5.60054 32.3683 5.36612 32.1339C5.1317 31.8995 5 31.5815 5 31.25V8.75C5 8.41848 5.1317 8.10054 5.36612 7.86612C5.60054 7.6317 5.91848 7.5 6.25 7.5H33.75C34.0815 7.5 34.3995 7.6317 34.6339 7.86612C34.8683 8.10054 35 8.41848 35 8.75V31.25C35 31.5815 34.8683 31.8995 34.6339 32.1339C34.3995 32.3683 34.0815 32.5 33.75 32.5ZM7.5 30H32.5V10H7.5V30Z"/>
3+<path d="M11.875 5H13.125V8.75H11.875V5ZM16.875 5H18.125V8.75H16.875V5ZM21.875 5H23.125V8.75H21.875V5ZM26.875 5H28.125V8.75H26.875V5ZM11.875 31.25H13.125V35H11.875V31.25ZM16.875 31.25H18.125V35H16.875V31.25ZM21.875 31.25H23.125V35H21.875V31.25ZM26.875 31.25H28.125V35H26.875V31.25ZM33.75 26.875H37.5V28.125H33.75V26.875ZM33.75 21.875H37.5V23.125H33.75V21.875ZM33.75 16.875H37.5V18.125H33.75V16.875ZM33.75 11.875H37.5V13.125H33.75V11.875Z"/>
4+<path d="M2.5 26.875H6.25V28.125H2.5V26.875ZM2.5 21.875H6.25V23.125H2.5V21.875ZM2.5 16.875H6.25V18.125H2.5V16.875ZM2.5 11.875H6.25V13.125H2.5V11.875Z"/>
5+<path d="M13.8323 20.2387L15.7711 20.8249C15.6411 21.3699 15.4348 21.8224 15.1561 22.1874C14.8773 22.5524 14.5286 22.8274 14.1136 23.0124C13.6986 23.1974 13.1698 23.2912 12.5286 23.2912C11.7511 23.2912 11.1148 23.1787 10.6223 22.9524C10.1286 22.7274 9.70359 22.3299 9.34609 21.7612C8.98859 21.1924 8.80859 20.4624 8.80859 19.5749C8.80859 18.3912 9.12359 17.4812 9.75359 16.8449C10.3836 16.2087 11.2748 15.8887 12.4261 15.8887C13.3286 15.8887 14.0361 16.0712 14.5523 16.4362C15.0686 16.8012 15.4523 17.3612 15.7023 18.1162L13.7498 18.5487C13.7035 18.3789 13.6309 18.2174 13.5348 18.0699C13.4166 17.9055 13.2611 17.7714 13.0811 17.6787C12.8948 17.586 12.6891 17.5393 12.4811 17.5424C11.9798 17.5424 11.5961 17.7437 11.3286 18.1474C11.1273 18.4462 11.0261 18.9162 11.0261 19.5562C11.0261 20.3499 11.1473 20.8937 11.3873 21.1874C11.6273 21.4812 11.9673 21.6287 12.4036 21.6287C12.8273 21.6287 13.1473 21.5099 13.3636 21.2724C13.5798 21.0349 13.7348 20.6912 13.8323 20.2387ZM16.8361 16.0099H20.5123C21.3136 16.0099 21.9111 16.1999 22.3111 16.5812C22.7086 16.9624 22.9098 17.5037 22.9098 18.2074C22.9098 18.9299 22.6923 19.4949 22.2586 19.9012C21.8248 20.3074 21.1623 20.5112 20.2686 20.5112H19.0573V23.1674H16.8361V16.0099ZM19.0573 19.0624H19.5998C20.0261 19.0624 20.3261 18.9887 20.4986 18.8399C20.5819 18.7708 20.6484 18.6838 20.6933 18.5852C20.7381 18.4867 20.76 18.3794 20.7573 18.2712C20.7573 18.0462 20.6823 17.8562 20.5323 17.6999C20.3823 17.5437 20.1011 17.4649 19.6873 17.4649H19.0573V19.0624ZM28.7248 16.0099H30.9323V20.2749C30.9323 20.6974 30.8661 21.0974 30.7348 21.4724C30.6058 21.844 30.3937 22.1813 30.1148 22.4587C29.8664 22.719 29.5638 22.9216 29.2286 23.0524C28.7986 23.2112 28.2823 23.2899 27.6811 23.2899C27.3 23.2869 26.9194 23.2623 26.5411 23.2162C26.1311 23.1674 25.7861 23.0712 25.5098 22.9262C25.2336 22.7812 24.9786 22.5762 24.7511 22.3099C24.538 22.0709 24.3775 21.7899 24.2798 21.4849C24.1544 21.0929 24.0854 20.6851 24.0748 20.2737V16.0087H26.2836V20.3749C26.2836 20.7649 26.3911 21.0699 26.6086 21.2899C26.8248 21.5087 27.1248 21.6187 27.5098 21.6187C27.8911 21.6187 28.1898 21.5112 28.4061 21.2949C28.6236 21.0787 28.7311 20.7724 28.7311 20.3749V16.0099H28.7248Z"/>
6+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/disk.svg+6-0
@@ -0,0 +1,6 @@
1+<svg width="25" height="24" viewBox="0 0 25 24" xmlns="http://www.w3.org/2000/svg">
2+<path d="M20.75 22.5H4.25C4.05109 22.5 3.86032 22.421 3.71967 22.2803C3.57902 22.1397 3.5 21.9489 3.5 21.75V2.25C3.5 2.05109 3.57902 1.86032 3.71967 1.71967C3.86032 1.57902 4.05109 1.5 4.25 1.5H20.75C20.9489 1.5 21.1397 1.57902 21.2803 1.71967C21.421 1.86032 21.5 2.05109 21.5 2.25V21.75C21.5 21.9489 21.421 22.1397 21.2803 22.2803C21.1397 22.421 20.9489 22.5 20.75 22.5ZM5 21H20V3H5V21Z"/>
3+<path d="M16.25 4.5H17.75V7.5H16.25V4.5ZM13.25 4.5H14.75V7.5H13.25V4.5ZM10.25 4.5H11.75V7.5H10.25V4.5ZM7.25 4.5H8.75V7.5H7.25V4.5Z"/>
4+<path d="M17.75 19.5H7.25C7.05109 19.5 6.86032 19.421 6.71967 19.2803C6.57902 19.1397 6.5 18.9489 6.5 18.75V9.75C6.5 9.55109 6.57902 9.36032 6.71967 9.21967C6.86032 9.07902 7.05109 9 7.25 9H17.75C17.9489 9 18.1397 9.07902 18.2803 9.21967C18.421 9.36032 18.5 9.55109 18.5 9.75V18.75C18.5 18.9489 18.421 19.1397 18.2803 19.2803C18.1397 19.421 17.9489 19.5 17.75 19.5ZM8 18H17V10.5H8V18Z"/>
5+<path d="M11.7267 11.7891C11.8362 11.8049 11.8909 11.8829 11.8909 12.0239C11.8752 12.1649 11.8129 12.2346 11.7034 12.2346H10.9302C10.8364 12.7349 10.7194 13.1564 10.5784 13.5006H11.3284C11.5782 13.5321 11.7109 13.6491 11.7267 13.8531V16.6424C11.7267 16.8456 11.6097 16.9469 11.3749 16.9469H10.4374C10.1869 16.9469 10.0624 16.8209 10.0624 16.5719V14.6031C10.0309 14.6504 10.0227 14.6646 10.0392 14.6504C10.0128 14.7066 9.97256 14.7551 9.92217 14.7914C9.82842 14.8694 9.73467 14.8619 9.64092 14.7681C9.53142 14.6586 9.50742 14.5581 9.57042 14.4644C10.0392 13.9641 10.3204 13.2224 10.4142 12.2376H9.89817C9.75792 12.2189 9.68742 12.1409 9.68742 11.9999C9.71817 11.8589 9.80442 11.7891 9.94542 11.7891H11.7267ZM10.6722 13.9454C10.5627 13.9296 10.5079 13.9926 10.5079 14.1329V16.3364C10.4922 16.4616 10.5552 16.5156 10.6954 16.5006H11.1642C11.2579 16.5006 11.2969 16.4534 11.2812 16.3596V14.1329C11.2812 13.9919 11.2264 13.9296 11.1169 13.9454H10.6722ZM15.0312 11.7659C15.1557 11.7659 15.2262 11.8364 15.2419 11.9766C15.2262 12.1326 15.1557 12.2114 15.0312 12.2114H13.7419V12.6569H14.6562C14.9059 12.6726 15.0387 12.7979 15.0544 13.0319V14.6031C15.0387 14.8521 14.9134 14.9856 14.6794 15.0006H13.7652C13.7022 15.5009 13.5852 15.8759 13.4127 16.1256C13.7719 16.3604 14.3277 16.5239 15.0777 16.6176C15.2187 16.6491 15.2877 16.7339 15.2877 16.8756C15.2719 17.0166 15.1857 17.0864 15.0297 17.0864C14.3613 17.0351 13.7129 16.8347 13.1322 16.4999C12.8472 16.7363 12.5217 16.919 12.1714 17.0391C11.9997 17.1014 11.8744 17.0624 11.7964 16.9221C11.7334 16.7811 11.7807 16.6731 11.9374 16.5936C12.2809 16.4841 12.5472 16.3514 12.7347 16.1954C12.5779 16.0859 12.3904 15.8751 12.1722 15.5624C12.0942 15.4371 12.1017 15.3276 12.1954 15.2339C12.3364 15.1559 12.4534 15.1709 12.5472 15.2811C12.7654 15.5624 12.9297 15.7499 13.0399 15.8436C13.1809 15.5939 13.2657 15.3126 13.2979 14.9999H12.4999C12.1714 14.9999 12.0079 14.8439 12.0079 14.5311V13.0544C12.0079 12.7889 12.1557 12.6561 12.4534 12.6561H13.2972V12.2106H12.1954C12.0702 12.2106 12.0079 12.1326 12.0079 11.9759C12.0079 11.8349 12.0702 11.7651 12.1954 11.7651L15.0312 11.7659ZM12.4294 13.6409H13.2964V13.0559H12.5929C12.4677 13.0401 12.4129 13.1031 12.4287 13.2434V13.6409H12.4294ZM12.4294 14.4374C12.4137 14.5626 12.4767 14.6174 12.6169 14.6016H13.2964V14.0391H12.4294V14.4374ZM13.7419 13.6409H14.6329V13.2186C14.6487 13.0934 14.5932 13.0394 14.4687 13.0544H13.7419V13.6409ZM13.7419 14.0391V14.6016H14.4687C14.5932 14.6016 14.6479 14.5394 14.6329 14.4141V14.0391H13.7419Z"/>
6+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/edit.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1661421275650" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4653" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M960.041 254.408c20.628-20.593 20.628-51.482 0-72.075l-118.61-118.41c-20.628-20.592-51.569-20.592-72.197 0l-92.83 92.672L872.37 352.23l87.67-97.822zM47.258 779.53v195.64H243.22l567.266-571.458-195.963-195.635L47.258 779.53z m0 0" p-id="4654" fill="#00c8db"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/info.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path d="M9 0.75C11.4742 0.815 13.5348 1.67104 15.1819 3.31813C16.829 4.96521 17.685 7.02583 17.75 9.5C17.685 11.9742 16.829 14.0348 15.1819 15.6819C13.5348 17.329 11.4742 18.185 9 18.25C6.52583 18.185 4.46521 17.329 2.81813 15.6819C1.17104 14.0348 0.315 11.9742 0.25 9.5C0.315 7.02583 1.17104 4.96521 2.81813 3.31813C4.46521 1.67104 6.52583 0.815 9 0.75ZM10.3088 6.12125C10.6475 6.12125 10.9275 6.02042 11.1488 5.81875C11.37 5.61708 11.4806 5.34687 11.4806 5.00812C11.4806 4.66937 11.37 4.39917 11.1488 4.1975C10.9275 3.99583 10.6508 3.895 10.3188 3.895C9.98667 3.895 9.71 3.99583 9.48875 4.1975C9.2675 4.39917 9.15687 4.66937 9.15687 5.00812C9.15687 5.34687 9.2675 5.61708 9.48875 5.81875C9.71 6.02042 9.98354 6.12125 10.3094 6.12125H10.3088ZM10.5431 13.1525C10.5431 13.0742 10.5496 12.97 10.5625 12.84C10.5754 12.71 10.5754 12.5863 10.5625 12.4688L9.5275 13.66C9.42333 13.7771 9.31583 13.8683 9.205 13.9338C9.09417 13.9992 8.99979 14.0188 8.92188 13.9925C8.80479 13.9404 8.75271 13.8492 8.76562 13.7188L10.4844 8.30875C10.5494 7.94417 10.4908 7.63167 10.3088 7.37125C10.1267 7.11083 9.83375 6.95458 9.43 6.9025C8.97417 6.91542 8.47604 7.1075 7.93563 7.47875C7.39521 7.85 6.92313 8.32208 6.51937 8.895V9.18813C6.50646 9.31854 6.50646 9.44229 6.51937 9.55937L7.55438 8.36812C7.65854 8.25104 7.76604 8.15979 7.87687 8.09438C7.98771 8.02896 8.07562 8.00937 8.14062 8.03562C8.27104 8.10062 8.31667 8.20479 8.2775 8.34812L6.57812 13.7387C6.48688 14.0642 6.5325 14.354 6.715 14.6081C6.8975 14.8623 7.21646 15.0348 7.67188 15.1256C8.32312 15.1127 8.87 14.924 9.3125 14.5594C9.755 14.1948 10.1652 13.726 10.5431 13.1531V13.1525Z" fill="#0093FF"/>
3+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/lang.svg+1-0
@@ -0,0 +1 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1662350667724" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1671" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M573.44 481.0752h-8.8064l33.9968 188.928c22.3232-7.8848 42.8032-19.968 60.928-35.84-18.6368-22.3232-33.4848-47.5136-45.1584-74.0352l35.84-4.608c9.728 19.968 20.992 38.1952 33.9968 53.5552 26.5216-32.1536 46.592-74.9568 60.928-128.512L573.44 481.0752z m135.4752 153.088c20.48 17.7152 43.7248 30.72 69.8368 38.6048l16.2816 5.12-9.728 34.9184-16.2816-5.12c-31.232-9.728-60.0064-26.112-84.6848-47.9232-22.8352 20.48-49.7664 36.352-79.1552 46.08l22.8352 127.488H492.032l-18.1248 83.3536h426.8032c19.968 0 36.352-16.2816 36.352-36.352V263.168c0-19.968-16.2816-36.352-36.352-36.352H519.4752L547.84 385.024l-0.9216-0.512 3.2768 17.2032 0.512-2.3552 7.8848 45.1584h87.9616v-36.352h67.9936v36.352h113.5616v36.352h-47.5136c-15.872 63.8976-40.0384 115.0976-71.68 153.2928zM454.7584 832.9216H123.8016C83.7632 832.9216 51.2 800.3584 51.2 760.32V143.5648c0-40.448 32.5632-72.6016 72.6016-72.6016h363.008l22.3232 119.6032H900.096c40.0384 0 72.6016 32.5632 72.6016 72.6016v617.1648c0 40.0384-32.5632 72.6016-72.6016 72.6016H428.6464l26.112-120.0128zM283.4432 553.6768v-38.1952h-71.68v-56.32h66.56v-37.6832h-66.56v-47.9232h71.68V335.872H169.8816v217.8048h113.5616z m175.0016 0V444.7232c0-19.5584-4.608-34.9184-13.5168-45.568s-22.3232-15.872-40.0384-15.872c-10.24 0-19.5584 1.8432-27.4432 6.0416-7.8848 4.1984-14.4384 10.752-18.6368 18.6368h-2.3552l-5.632-21.4016h-31.6416v167.1168h40.96v-78.6432c0-19.5584 2.7648-33.4848 7.8848-42.3936 5.12-8.3968 13.5168-12.5952 25.088-12.5952 8.3968 0 14.4384 2.7648 18.6368 8.8064 3.6864 6.0416 6.0416 14.848 6.0416 27.0336v97.792h40.6528z" fill="#bcbcbc" p-id="1672"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/load.svg+5-0
@@ -0,0 +1,5 @@
1+<svg width="31" height="30" viewBox="0 0 31 30" xmlns="http://www.w3.org/2000/svg">
2+<path d="M27.083 16.875H4.58301C4.33437 16.875 4.09591 16.7762 3.9201 16.6004C3.74428 16.4246 3.64551 16.1861 3.64551 15.9375V2.8125C3.64551 2.56386 3.74428 2.3254 3.9201 2.14959C4.09591 1.97377 4.33437 1.875 4.58301 1.875H27.083C27.3316 1.875 27.5701 1.97377 27.7459 2.14959C27.9217 2.3254 28.0205 2.56386 28.0205 2.8125V15.9375C28.0205 16.1861 27.9217 16.4246 27.7459 16.6004C27.5701 16.7762 27.3316 16.875 27.083 16.875ZM5.52051 15H26.1455V3.75H5.52051V15ZM27.083 28.125H4.58301C4.33437 28.125 4.09591 28.0262 3.9201 27.8504C3.74428 27.6746 3.64551 27.4361 3.64551 27.1875V19.6875C3.64551 19.4389 3.74428 19.2004 3.9201 19.0246C4.09591 18.8488 4.33437 18.75 4.58301 18.75H27.083C27.3316 18.75 27.5701 18.8488 27.7459 19.0246C27.9217 19.2004 28.0205 19.4389 28.0205 19.6875V27.1875C28.0205 27.4361 27.9217 27.6746 27.7459 27.8504C27.5701 28.0262 27.3316 28.125 27.083 28.125ZM5.52051 26.25H26.1455V20.625H5.52051V26.25Z"/>
3+<path d="M9.27051 28.125C9.02187 28.125 8.78341 28.0262 8.6076 27.8504C8.43178 27.6746 8.33301 27.4361 8.33301 27.1875V23.9062C8.33301 23.6576 8.43178 23.4192 8.6076 23.2433C8.78341 23.0675 9.02187 22.9688 9.27051 22.9688C9.51915 22.9688 9.7576 23.0675 9.93342 23.2433C10.1092 23.4192 10.208 23.6576 10.208 23.9062V27.1875C10.208 27.4361 10.1092 27.6746 9.93342 27.8504C9.7576 28.0262 9.51915 28.125 9.27051 28.125ZM13.958 28.125C13.7094 28.125 13.4709 28.0262 13.2951 27.8504C13.1193 27.6746 13.0205 27.4361 13.0205 27.1875V23.9062C13.0205 23.6576 13.1193 23.4192 13.2951 23.2433C13.4709 23.0675 13.7094 22.9688 13.958 22.9688C14.2066 22.9688 14.4451 23.0675 14.6209 23.2433C14.7967 23.4192 14.8955 23.6576 14.8955 23.9062V27.1875C14.8955 27.4361 14.7967 27.6746 14.6209 27.8504C14.4451 28.0262 14.2066 28.125 13.958 28.125ZM18.6455 28.125C18.3969 28.125 18.1584 28.0262 17.9826 27.8504C17.8068 27.6746 17.708 27.4361 17.708 27.1875V23.9203C17.708 23.6717 17.8068 23.4332 17.9826 23.2574C18.1584 23.0816 18.3969 22.9828 18.6455 22.9828C18.8941 22.9828 19.1326 23.0816 19.3084 23.2574C19.4842 23.4332 19.583 23.6717 19.583 23.9203V27.1875C19.583 27.4361 19.4842 27.6746 19.3084 27.8504C19.1326 28.0262 18.8941 28.125 18.6455 28.125ZM23.333 28.125C23.0844 28.125 22.8459 28.0262 22.6701 27.8504C22.4943 27.6746 22.3955 27.4361 22.3955 27.1875V23.9203C22.3955 23.6717 22.4943 23.4332 22.6701 23.2574C22.8459 23.0816 23.0844 22.9828 23.333 22.9828C23.5816 22.9828 23.8201 23.0816 23.9959 23.2574C24.1717 23.4332 24.2705 23.6717 24.2705 23.9203V27.1875C24.2705 27.4361 24.1717 27.6746 23.9959 27.8504C23.8201 28.0262 23.5816 28.125 23.333 28.125Z"/>
4+<path d="M9.27051 7.5H14.8955V11.25H9.27051V7.5Z"/>
5+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/memory.svg+6-0
@@ -0,0 +1,6 @@
1+<svg width="25" height="24" viewBox="0 0 25 24" xmlns="http://www.w3.org/2000/svg">
2+<path d="M18.5002 22.5H6.50018C6.30127 22.5 6.1105 22.421 5.96985 22.2803C5.8292 22.1397 5.75018 21.9489 5.75018 21.75V2.25C5.75018 2.05109 5.8292 1.86032 5.96985 1.71967C6.1105 1.57902 6.30127 1.5 6.50018 1.5H18.5002C18.6991 1.5 18.8899 1.57902 19.0305 1.71967C19.1712 1.86032 19.2502 2.05109 19.2502 2.25V21.75C19.2502 21.9489 19.1712 22.1397 19.0305 22.2803C18.8899 22.421 18.6991 22.5 18.5002 22.5ZM7.25018 21H17.7502V3H7.25018V21ZM3.00293 4.536H4.50293V19.464H3.00293V4.536Z"/>
3+<path d="M8.88184 10.3261H16.0803V11.7331H8.88184V10.3261ZM8.88184 5.91162H16.0803V7.33212H8.88184V5.91162Z"/>
4+<path d="M11 17.25C11 17.6478 11.158 18.0294 11.4393 18.3107C11.7206 18.592 12.1022 18.75 12.5 18.75C12.8978 18.75 13.2794 18.592 13.5607 18.3107C13.842 18.0294 14 17.6478 14 17.25C14 16.8522 13.842 16.4706 13.5607 16.1893C13.2794 15.908 12.8978 15.75 12.5 15.75C12.1022 15.75 11.7206 15.908 11.4393 16.1893C11.158 16.4706 11 16.8522 11 17.25Z"/>
5+<path d="M20.4971 4.53613H21.9971V19.4641H20.4971V4.53613Z"/>
6+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/plus.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1660533823595" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2349" width="24" height="24" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m192 472c0 4.4-3.6 8-8 8H544v152c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V544H328c-4.4 0-8-3.6-8-8v-48c0-4.4 3.6-8 8-8h152V328c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v152h152c4.4 0 8 3.6 8 8v48z" p-id="2350" fill="#00c8db"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/speed_down.svg+5-0
@@ -0,0 +1,5 @@
1+<svg width="25" height="24" viewBox="0 0 25 24" xmlns="http://www.w3.org/2000/svg">
2+<path d="M23.3008 14.7197H17.9808C17.6208 14.7197 17.3008 14.9597 17.2208 15.2797L16.3408 18.1197H8.66078L7.78078 15.2797C7.66078 14.9597 7.38078 14.7197 7.02078 14.7197H1.70078C1.46078 14.7197 1.30078 14.8797 1.30078 15.1197V21.3197C1.30078 22.1997 2.02078 22.9197 2.90078 22.9197H22.1008C22.9808 22.9197 23.7008 22.1997 23.7008 21.3197V15.1197C23.7008 14.9197 23.5408 14.7197 23.3008 14.7197Z"/>
3+<path d="M23.7002 14.8801L21.0202 2.00008C20.9002 1.48008 20.4202 1.08008 19.9002 1.08008H5.14016C4.58016 1.08008 4.10016 1.48008 4.02016 2.00008L1.30016 14.8801C1.26016 15.0801 1.30016 15.2801 1.42016 15.4801C1.54016 15.6401 1.70016 15.7601 1.90016 15.8001H2.06016C2.22016 15.8001 2.34016 15.7601 2.50016 15.6801C2.66016 15.5601 2.78016 15.4001 2.82016 15.2001L5.42016 2.64008H19.5402L22.1802 15.2001C22.2602 15.6001 22.7002 15.8801 23.1002 15.8001C23.3002 15.7601 23.4602 15.6401 23.5802 15.4801C23.7002 15.2801 23.7402 15.0801 23.7002 14.8801Z"/>
4+<path d="M15.26 9.00047L13.3 10.9605V6.24047C13.3 5.80047 12.94 5.48047 12.54 5.48047C12.1 5.48047 11.78 5.84047 11.78 6.24047V10.9605L9.82 9.00047C9.5 8.68047 9.02 8.68047 8.74 9.00047C8.42 9.32047 8.42 9.80047 8.74 10.0805L12.02 13.3605C12.1 13.4405 12.18 13.4805 12.26 13.5205C12.3 13.5205 12.34 13.5605 12.38 13.5605H12.78C12.82 13.5605 12.86 13.5205 12.9 13.5205C12.98 13.4805 13.06 13.4405 13.14 13.3605L16.42 10.0805C16.74 9.76047 16.74 9.28047 16.42 9.00047C16.02 8.68047 15.54 8.68047 15.26 9.00047Z"/>
5+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/speed_up.svg+5-0
@@ -0,0 +1,5 @@
1+<svg width="25" height="24" viewBox="0 0 25 24" xmlns="http://www.w3.org/2000/svg">
2+<path d="M23.2998 14.7202H17.9798C17.6198 14.7202 17.2998 14.9602 17.2198 15.2802L16.3398 18.1202H8.65981L7.77981 15.2802C7.65981 14.9602 7.37981 14.7202 7.01981 14.7202H1.6998C1.4598 14.7202 1.2998 14.8802 1.2998 15.1202V21.3202C1.2998 22.2002 2.0198 22.9202 2.8998 22.9202H22.0998C22.9798 22.9202 23.6998 22.2002 23.6998 21.3202V15.1202C23.6998 14.9202 23.5398 14.7202 23.2998 14.7202Z"/>
3+<path d="M23.7002 14.8801L21.0202 2.00008C20.9002 1.48008 20.4202 1.08008 19.9002 1.08008H5.14016C4.58016 1.08008 4.10016 1.48008 4.02016 2.00008L1.30016 14.8801C1.26016 15.0801 1.30016 15.2801 1.42016 15.4801C1.54016 15.6401 1.70016 15.7601 1.90016 15.8001H2.06016C2.22016 15.8001 2.34016 15.7601 2.50016 15.6801C2.66016 15.5601 2.78016 15.4001 2.82016 15.2001L5.42016 2.64008H19.5402L22.1802 15.2001C22.2602 15.6001 22.7002 15.8801 23.1002 15.8001C23.3002 15.7601 23.4602 15.6401 23.5802 15.4801C23.7002 15.2801 23.7402 15.0801 23.7002 14.8801Z"/>
4+<path d="M9.89918 10.0401L11.8592 8.08006L11.8592 12.8001C11.8592 13.2401 12.2192 13.5601 12.6192 13.5601C13.0592 13.5601 13.3792 13.2001 13.3792 12.8001L13.3792 8.08006L15.3392 10.0401C15.6592 10.3601 16.1392 10.3601 16.4192 10.0401C16.7392 9.72006 16.7392 9.24006 16.4192 8.96006L13.1392 5.68006C13.0592 5.60006 12.9792 5.56006 12.8992 5.52006C12.8592 5.52006 12.8192 5.48006 12.7792 5.48006L12.3792 5.48006C12.3392 5.48006 12.2992 5.52006 12.2592 5.52006C12.1792 5.56006 12.0992 5.60006 12.0192 5.68006L8.73918 8.96006C8.41918 9.28006 8.41918 9.76006 8.73918 10.0401C9.13918 10.3601 9.61918 10.3601 9.89918 10.0401Z"/>
5+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/tip.svg+3-0
@@ -0,0 +1,3 @@
1+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<path d="M9 1.125C11.2268 1.1835 13.0813 1.95394 14.5637 3.43631C16.0461 4.91869 16.8165 6.77325 16.875 9C16.8165 11.2268 16.0461 13.0813 14.5637 14.5637C13.0813 16.0461 11.2268 16.8165 9 16.875C6.77325 16.8165 4.91869 16.0461 3.43631 14.5637C1.95394 13.0813 1.1835 11.2268 1.125 9C1.1835 6.77325 1.95394 4.91869 3.43631 3.43631C4.91869 1.95394 6.77325 1.1835 9 1.125ZM10.1779 5.95913C10.4827 5.95913 10.7347 5.86837 10.9339 5.68687C11.133 5.50537 11.2326 5.26219 11.2326 4.95731C11.2326 4.65244 11.133 4.40925 10.9339 4.22775C10.7347 4.04625 10.4858 3.9555 10.1869 3.9555C9.888 3.9555 9.639 4.04625 9.43987 4.22775C9.24075 4.40925 9.14119 4.65244 9.14119 4.95731C9.14119 5.26219 9.24075 5.50537 9.43987 5.68687C9.639 5.86837 9.88519 5.95913 10.1784 5.95913H10.1779ZM10.3888 12.2872C10.3888 12.2168 10.3946 12.123 10.4062 12.006C10.4179 11.889 10.4179 11.7776 10.4062 11.6719L9.47475 12.744C9.381 12.8494 9.28425 12.9315 9.1845 12.9904C9.08475 13.0492 8.99981 13.0669 8.92969 13.0433C8.82431 12.9964 8.77744 12.9143 8.78906 12.7969L10.3359 7.92788C10.3944 7.59975 10.3418 7.3185 10.1779 7.08413C10.014 6.84975 9.75037 6.70913 9.387 6.66225C8.97675 6.67387 8.52844 6.84675 8.04206 7.18087C7.55569 7.515 7.13081 7.93987 6.76744 8.4555V8.71931C6.75581 8.83669 6.75581 8.94806 6.76744 9.05344L7.69894 7.98131C7.79269 7.87594 7.88944 7.79381 7.98919 7.73494C8.08894 7.67606 8.16806 7.65844 8.22656 7.68206C8.34394 7.74056 8.385 7.83431 8.34975 7.96331L6.82031 12.8149C6.73819 13.1077 6.77925 13.3686 6.9435 13.5973C7.10775 13.8261 7.39481 13.9813 7.80469 14.0631C8.39081 14.0514 8.883 13.8816 9.28125 13.5534C9.6795 13.2253 10.0487 12.8034 10.3888 12.2878V12.2872Z" fill="#165DFF"/>
3+</svg>
Aplugins/alert-center/web-ui/src/assets/svg/vite.svg+1-0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/vue.svg+1-0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/warn_1.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1659077409267" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14079" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M504.109851 16.453128c-278.332339 0-503.966321 225.632974-503.966321 503.966321S225.776505 1024.384762 504.109851 1024.384762s503.966321-225.632974 503.966321-503.966321S782.44219 16.453128 504.109851 16.453128zM242.706054 566.655409l0.04734-94.679771 525.432441 0-0.017123 94.679771L242.706054 566.655409z" p-id="14080" fill="#f7777a"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/warn_2.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1659077382374" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13110" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M512.260431 5.116531C231.865323 5.116531 4.636089 232.376465 4.636089 512.740874c0 280.33371 227.229235 507.624343 507.624343 507.624343 280.396131 0 507.625366-227.291656 507.624343-507.624343C1019.885797 232.376465 792.656563 5.116531 512.260431 5.116531zM691.775968 647.330198c12.393261 12.393261 12.393261 32.502251 0 44.893466-12.391215 12.393261-32.531927 12.393261-44.923142 0L512.446673 557.790906l-135.333268 135.332245c-12.516058 12.485359-32.718169 12.485359-45.235251 0-12.516058-12.485359-12.516058-32.718169 0-45.205575L467.211423 512.555655 332.745918 378.119827c-12.393261-12.362562-12.393261-32.469506 0-44.833091 12.392238-12.393261 32.531927-12.393261 44.86379 0l134.465504 134.404106 136.325875-136.324852c12.516058-12.486382 32.718169-12.486382 45.234227 0 12.516058 12.486382 12.516058 32.718169 0 45.203528l-136.324852 136.355551L691.775968 647.330198z" p-id="13111" fill="#ff8a52"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/warn_3.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1659077038907" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4484" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.390625" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M1004.657 801.716 602.263 91.599c-49.213-86.817-129.646-86.817-178.866 0L21.004 801.716c-49.207 86.906-8.949 157.798 89.388 157.798l804.877 0C1013.606 959.514 1053.825 888.622 1004.657 801.716zM544.635 832.216l-63.649 0 0-63.649 63.649 0L544.635 832.216zM544.635 641.27l-63.649 0L480.986 259.377l63.649 0L544.635 641.27z" p-id="4485" fill="#ffcc00"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/warn_4.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1659077306393" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10197" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M1024 512A512 512 0 1 1 512 0a512 512 0 0 1 512 512z m-482.210909 114.269091l15.127273-380.974546a44.916364 44.916364 0 0 0-89.832728 0l15.127273 380.974546c0 10.472727 13.730909 18.618182 29.789091 18.618182s29.323636-8.145455 29.789091-18.618182zM512 712.145455a46.545455 46.545455 0 1 0 46.545455 46.545454 46.545455 46.545455 0 0 0-46.545455-46.545454z" p-id="10198" fill="#36c3fb"></path></svg>
Aplugins/alert-center/web-ui/src/assets/svg/yes.svg+2-0
@@ -0,0 +1,2 @@
1+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1661420616981" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1618" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
2+</style></defs><path d="M916.8 194.9a3.9 3.9 0 0 0-5.6 0L375.9 730.1l-263.1-263a3.9 3.9 0 0 0-5.6 0l-45.3 45.3a4 4 0 0 0 0 5.6l265.9 265.9 45.3 45.2a3.9 3.9 0 0 0 5.6 0l45.3-45.2 538.1-538.1a4.2 4.2 0 0 0 0-5.7z" p-id="1619" fill="#00c8db"></path></svg>
Aplugins/alert-center/web-ui/src/auto-imports.d.ts+57-0
@@ -0,0 +1,57 @@
1+// Generated by 'unplugin-auto-import'
2+export {}
3+declare global {
4+ const EffectScope: typeof import('vue')['EffectScope']
5+ const computed: typeof import('vue')['computed']
6+ const createApp: typeof import('vue')['createApp']
7+ const customRef: typeof import('vue')['customRef']
8+ const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
9+ const defineComponent: typeof import('vue')['defineComponent']
10+ const effectScope: typeof import('vue')['effectScope']
11+ const getCurrentInstance: typeof import('vue')['getCurrentInstance']
12+ const getCurrentScope: typeof import('vue')['getCurrentScope']
13+ const h: typeof import('vue')['h']
14+ const inject: typeof import('vue')['inject']
15+ const isProxy: typeof import('vue')['isProxy']
16+ const isReactive: typeof import('vue')['isReactive']
17+ const isReadonly: typeof import('vue')['isReadonly']
18+ const isRef: typeof import('vue')['isRef']
19+ const markRaw: typeof import('vue')['markRaw']
20+ const nextTick: typeof import('vue')['nextTick']
21+ const onActivated: typeof import('vue')['onActivated']
22+ const onBeforeMount: typeof import('vue')['onBeforeMount']
23+ const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
24+ const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
25+ const onDeactivated: typeof import('vue')['onDeactivated']
26+ const onErrorCaptured: typeof import('vue')['onErrorCaptured']
27+ const onMounted: typeof import('vue')['onMounted']
28+ const onRenderTracked: typeof import('vue')['onRenderTracked']
29+ const onRenderTriggered: typeof import('vue')['onRenderTriggered']
30+ const onScopeDispose: typeof import('vue')['onScopeDispose']
31+ const onServerPrefetch: typeof import('vue')['onServerPrefetch']
32+ const onUnmounted: typeof import('vue')['onUnmounted']
33+ const onUpdated: typeof import('vue')['onUpdated']
34+ const provide: typeof import('vue')['provide']
35+ const reactive: typeof import('vue')['reactive']
36+ const readonly: typeof import('vue')['readonly']
37+ const ref: typeof import('vue')['ref']
38+ const resolveComponent: typeof import('vue')['resolveComponent']
39+ const shallowReactive: typeof import('vue')['shallowReactive']
40+ const shallowReadonly: typeof import('vue')['shallowReadonly']
41+ const shallowRef: typeof import('vue')['shallowRef']
42+ const toRaw: typeof import('vue')['toRaw']
43+ const toRef: typeof import('vue')['toRef']
44+ const toRefs: typeof import('vue')['toRefs']
45+ const triggerRef: typeof import('vue')['triggerRef']
46+ const unref: typeof import('vue')['unref']
47+ const useAttrs: typeof import('vue')['useAttrs']
48+ const useCssModule: typeof import('vue')['useCssModule']
49+ const useCssVars: typeof import('vue')['useCssVars']
50+ const useRoute: typeof import('vue-router')['useRoute']
51+ const useRouter: typeof import('vue-router')['useRouter']
52+ const useSlots: typeof import('vue')['useSlots']
53+ const watch: typeof import('vue')['watch']
54+ const watchEffect: typeof import('vue')['watchEffect']
55+ const watchPostEffect: typeof import('vue')['watchPostEffect']
56+ const watchSyncEffect: typeof import('vue')['watchSyncEffect']
57+}
Aplugins/alert-center/web-ui/src/components.d.ts+66-0
@@ -0,0 +1,66 @@
1+// generated by unplugin-vue-components
2+// We suggest you to commit this file into source control
3+// Read more: https://github.com/vuejs/core/pull/3399
4+import '@vue/runtime-core'
5+ 
6+export {}
7+ 
8+declare module '@vue/runtime-core' {
9+ export interface GlobalComponents {
10+ AlertConfig: typeof import('./components/AlertConfig.vue')['default']
11+ ClusterCascader: typeof import('./components/ClusterCascader.vue')['default']
12+ ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
13+ ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
14+ ElButton: typeof import('element-plus/es')['ElButton']
15+ ElCard: typeof import('element-plus/es')['ElCard']
16+ ElCascader: typeof import('element-plus/es')['ElCascader']
17+ ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
18+ ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
19+ ElCol: typeof import('element-plus/es')['ElCol']
20+ ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
21+ ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
22+ ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
23+ ElDialog: typeof import('element-plus/es')['ElDialog']
24+ ElDivider: typeof import('element-plus/es')['ElDivider']
25+ ElForm: typeof import('element-plus/es')['ElForm']
26+ ElFormItem: typeof import('element-plus/es')['ElFormItem']
27+ ElIcon: typeof import('element-plus/es')['ElIcon']
28+ ElInput: typeof import('element-plus/es')['ElInput']
29+ ElMenu: typeof import('element-plus/es')['ElMenu']
30+ ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
31+ ElOption: typeof import('element-plus/es')['ElOption']
32+ ElPagination: typeof import('element-plus/es')['ElPagination']
33+ ElPopover: typeof import('element-plus/es')['ElPopover']
34+ ElRadio: typeof import('element-plus/es')['ElRadio']
35+ ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
36+ ElRow: typeof import('element-plus/es')['ElRow']
37+ ElSelect: typeof import('element-plus/es')['ElSelect']
38+ ElSpace: typeof import('element-plus/es')['ElSpace']
39+ ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
40+ ElSwitch: typeof import('element-plus/es')['ElSwitch']
41+ ElTable: typeof import('element-plus/es')['ElTable']
42+ ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
43+ ElTabPane: typeof import('element-plus/es')['ElTabPane']
44+ ElTabs: typeof import('element-plus/es')['ElTabs']
45+ ElTag: typeof import('element-plus/es')['ElTag']
46+ Machines: typeof import('./components/Machines.vue')['default']
47+ MonacoEditor: typeof import('./components/MonacoEditor.vue')['default']
48+ MyBar: typeof import('./components/MyBar.vue')['default']
49+ MyBarLine: typeof import('./components/MyBarLine.vue')['default']
50+ MyBreadcrumb: typeof import('./layout/MyBreadcrumb.vue')['default']
51+ MyCard: typeof import('./components/MyCard.vue')['default']
52+ MyDatePicker: typeof import('./components/MyDatePicker.vue')['default']
53+ MyHeader: typeof import('./layout/MyHeader.vue')['default']
54+ MyLine: typeof import('./components/MyLine.vue')['default']
55+ MyMenu: typeof import('./layout/MyMenu.vue')['default']
56+ MyMessage: typeof import('./components/MyMessage.vue')['default']
57+ MyOperator: typeof import('./components/MyOperator.vue')['default']
58+ MyPie: typeof import('./components/MyPie.vue')['default']
59+ MyProgress: typeof import('./components/MyProgress.vue')['default']
60+ MyTable: typeof import('./components/MyTable.vue')['default']
61+ Proxies: typeof import('./components/Proxies.vue')['default']
62+ RouterLink: typeof import('vue-router')['RouterLink']
63+ RouterView: typeof import('vue-router')['RouterView']
64+ SvgIcon: typeof import('./components/SvgIcon.vue')['default']
65+ }
66+}
Aplugins/alert-center/web-ui/src/components/AlertConfig.vue+87-0
@@ -0,0 +1,87 @@
1+<template>
2+ <div>
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('app.setAlertConfig') }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
8+ <el-form-item :label="$t('AlertClusterNodeConf.alertIp')">
9+ <el-input v-model="formData.alertIp" :disabled="!alertConfigEnv.isInstall"></el-input>
10+ </el-form-item>
11+ <el-form-item :label="$t('AlertClusterNodeConf.alertPort')">
12+ <el-input v-model="formData.alertPort" :disabled="!alertConfigEnv.isInstall"></el-input>
13+ </el-form-item>
14+ <el-form-item :label="$t('AlertClusterNodeConf.interfaceAddr')">
15+ <span>{{ 'http://' + (formData.alertIp ? formData.alertIp : 'IP') + ':' + (formData.alertPort ?
16+ formData.alertPort : 'port') + '/plugins/alert-center/alertCenter/api/v1/alerts' }}</span>
17+ </el-form-item>
18+ </el-form>
19+ <el-row>
20+ <el-button :disabled="!alertConfigEnv.isInstall" type="primary" @click="confirm">{{ t('app.confirm') }}</el-button>
21+ </el-row>
22+ <el-dialog v-model="dialogVisible" :title="t('app.tip')" width="30%">
23+ <span v-if="!alertConfigEnv.isInstall">
24+ {{ t('app.uninstallTip') }}
25+ </span>
26+ <span v-if="!alertConfigEnv.isConfig">
27+ {{ t('app.disConfigTip') }}
28+ </span>
29+ <template #footer>
30+ <span class="dialog-footer">
31+ <el-button type="primary" @click="closeTip">
32+ {{ t('app.confirm') }}
33+ </el-button>
34+ </span>
35+ </template>
36+ </el-dialog>
37+ 
38+ </div>
39+</template>
40+ 
41+<script setup lang='ts'>
42+import "element-plus/es/components/message-box/style/index";
43+import { useRequest } from "vue-request";
44+import request from "@/request";
45+import { ElMessage } from "element-plus";
46+import { useI18n } from "vue-i18n";
47+import { useAlertConfigStore } from "@/store/alertConfig";
48+ 
49+const { t } = useI18n();
50+const dialogVisible = ref<boolean>(false)
51+ 
52+const alertConfigEnv = useAlertConfigStore();
53+const formData = ref<any>({
54+ id: '',
55+ alertIp: '',
56+ alertPort: ''
57+})
58+ 
59+const confirm = () => {
60+ request.post(`/alertCenter/api/v1/alertConf`, formData.value).then((res: any) => {
61+ if (res && res.code === 200) {
62+ ElMessage({
63+ message: t('app.saveSuccess'),
64+ type: 'success'
65+ })
66+ alertConfigEnv.isConfig = true
67+ alertConfigEnv.showMain = true
68+ } else {
69+ ElMessage({
70+ message: t('app.saveFail'),
71+ type: 'error'
72+ })
73+ }
74+ })
75+}
76+ 
77+const closeTip = () => {
78+ dialogVisible.value = false
79+}
80+ 
81+onMounted(() => {
82+ if (!alertConfigEnv.showMain) {
83+ dialogVisible.value = true
84+ }
85+})
86+</script>
87+<style scoped lang='scss'></style>
Aplugins/alert-center/web-ui/src/components/ClusterCascader.vue+108-0
@@ -0,0 +1,108 @@
1+<template>
2+ <div class="filter">
3+ <span>{{ props.title }}&nbsp;</span>
4+ <el-cascader v-model="cluster" :options="clusterList" @change="getClusterValue" style="max-width: 200px" :style="{ width: width ? width + 'px' : 'auto' }" :clearable="!notClearable" />
5+ </div>
6+</template>
7+ 
8+<script lang="ts" setup>
9+import ogRequest from "../request";
10+import { useRequest } from "vue-request";
11+ 
12+type Rer =
13+ | [
14+ {
15+ [propName: string]: string | number;
16+ }
17+ ]
18+ | undefined;
19+const props = withDefaults(
20+ defineProps<{
21+ title?: string;
22+ width?: string;
23+ instanceValueKey?: string;
24+ notClearable?: boolean;
25+ clusterOnly?: boolean;
26+ autoSelectFirst?: boolean; // now only support one level
27+ }>(),
28+ {
29+ title: "",
30+ width: "",
31+ instanceValueKey: "nodeId",
32+ notClearable: false,
33+ clusterOnly: false,
34+ autoSelectFirst: false,
35+ }
36+);
37+const emit = defineEmits(["getCluster", "loaded"]);
38+ 
39+const cluster = ref<Array<any>>([]);
40+const clusterList = ref<Array<any>>([]);
41+ 
42+const treeTransform = (arr: any) => {
43+ let obj: any = [];
44+ if (arr instanceof Array) {
45+ arr.forEach((item) => {
46+ obj.push({
47+ label: item.clusterId ? item.clusterId : item.azName + "_" + item.publicIp,
48+ value: item.clusterId ? item.clusterId : item[props.instanceValueKey],
49+ children: props.clusterOnly ? null : treeTransform(item.clusterNodes),
50+ nodeId: item.nodeId ? item.nodeId : null,
51+ });
52+ });
53+ if (props.autoSelectFirst && obj.length > 0) {
54+ if (props.clusterOnly) cluster.value = [obj[0].value];
55+ else if (obj[0].children.length > 0) cluster.value = [obj[0].value, obj[0].children[0].value];
56+ emit("getCluster", cluster.value);
57+ }
58+ }
59+ return obj;
60+};
61+const getClusterValue = (val: string[]) => {
62+ if (val == null) emit("getCluster", []);
63+ else emit("getCluster", val);
64+};
65+const setNodeId = (val: string) => {
66+ if (val === "" || val === null || val === undefined) return;
67+ nextTick(() => {
68+ if (clusterList.value.length <= 0) return;
69+ for (let p1 = 0; p1 < clusterList.value.length; p1++) {
70+ const clusterTemp = clusterList.value[p1];
71+ for (let p2 = 0; p2 < clusterTemp.children.length; p2++) {
72+ const node = clusterTemp.children[p2];
73+ if (node.nodeId === val) {
74+ cluster.value = [clusterTemp.value, node.value];
75+ emit("getCluster", cluster.value);
76+ return;
77+ }
78+ }
79+ }
80+ });
81+};
82+defineExpose({ setNodeId });
83+ 
84+onMounted(() => {
85+ clusterData();
86+});
87+const { data: rer, run: clusterData } = useRequest(
88+ () => {
89+ return ogRequest.get("/observability/v1/topsql/cluster", "");
90+ },
91+ { manual: true }
92+);
93+ 
94+watch(rer, (rer: Rer) => {
95+ if (rer && Object.keys(rer).length) {
96+ clusterList.value = treeTransform(rer);
97+ emit("loaded", rer);
98+ }
99+});
100+</script>
101+ 
102+<style lang="scss" scoped>
103+.filter {
104+ display: flex;
105+ flex-wrap: nowrap;
106+ align-items: center;
107+}
108+</style>
Aplugins/alert-center/web-ui/src/components/Machines.vue+54-0
@@ -0,0 +1,54 @@
1+<template>
2+ <div>
3+ <el-select v-model="machineValue" @change="selectMachine" :clearable="!notClearable" :style="{ width: width ? width + 'px' : 'auto' }">
4+ <el-option v-for="item in machineList" :key="item.hostId" :label="item.privateIp + '(' + item.publicIp + ')'" :value="item.hostId" />
5+ </el-select>
6+ </div>
7+</template>
8+ 
9+<script lang="ts" setup>
10+import restRequest from "../request/restful";
11+import { useRequest } from "vue-request";
12+ 
13+const props = withDefaults(
14+ defineProps<{
15+ width?: string;
16+ notClearable?: boolean;
17+ autoSelectFirst?: boolean;
18+ }>(),
19+ {
20+ width: "",
21+ notClearable: false,
22+ autoSelectFirst: false,
23+ }
24+);
25+const emit = defineEmits(["change", "loaded"]);
26+ 
27+const machineValue = ref<any>();
28+const machineList = ref<Array<any>>([]);
29+ 
30+const selectMachine = (val: string[]) => {
31+ emit("change", machineValue);
32+};
33+ 
34+onMounted(() => {
35+ clusterData();
36+});
37+const { data: rer, run: clusterData } = useRequest(
38+ () => {
39+ return restRequest.get("/observability/v1/environment/hosts", "");
40+ },
41+ { manual: true }
42+);
43+ 
44+watch(rer, (rer) => {
45+ if (rer.length) {
46+ machineList.value = rer;
47+ emit("loaded");
48+ if (props.autoSelectFirst && machineList.value.length > 0) {
49+ machineValue.value = machineList.value[0].hostId;
50+ emit("change", machineValue.value);
51+ }
52+ }
53+});
54+</script>
Aplugins/alert-center/web-ui/src/components/MonacoEditor.vue+142-0
@@ -0,0 +1,142 @@
1+<script setup lang="ts">
2+import * as monaco from "monaco-editor";
3+import { format } from "sql-formatter";
4+import { uuid } from "../shared";
5+ 
6+const id = 'monaco_' + uuid()
7+const props = withDefaults(
8+ defineProps<{
9+ modelValue: string;
10+ /**
11+ * enable editor readOnly
12+ */
13+ readOnly?: boolean;
14+ sqlFormatter?: boolean;
15+ height?: number | string;
16+ /**
17+ * enable editor to resize height
18+ */
19+ resize?: boolean;
20+ }>(),
21+ {
22+ readOnly: true,
23+ sqlFormatter: true,
24+ height: '100%',
25+ }
26+);
27+const myEmit = defineEmits<{(event: 'update:modelValue', text: string): void}>()
28+ 
29+let editor:monaco.editor.IStandaloneCodeEditor;
30+const queryText = ref(props.modelValue)
31+if (props.sqlFormatter && props.modelValue) {
32+ let text = format(props.modelValue);
33+ let index = 0;
34+ while ((index = text.indexOf("$", index + 1)) > -1) {
35+ text =
36+ text.slice(0, index).trimEnd() +
37+ "$" +
38+ text.slice(index + 1).trimStart();
39+ }
40+ queryText.value = text;
41+}
42+watch(() => props.modelValue, v => {
43+ const pos = editor.getPosition()!
44+ editor.setValue(v)
45+ editor.setPosition(pos)
46+})
47+onMounted(() => {
48+ nextTick(() => {
49+ editor = monaco.editor.create(
50+ document.getElementById(id) as HTMLElement,
51+ {
52+ value: queryText.value,
53+ language: "sql",
54+ readOnly: props.readOnly,
55+ wordWrap: "on",
56+ lineNumbersMinChars: 3,
57+ rulers: [0],
58+ overviewRulerBorder: false,
59+ scrollbar: {
60+ useShadows: false,
61+ verticalScrollbarSize: 5,
62+ },
63+ renderLineHighlight: "gutter",
64+ scrollBeyondLastLine: false,
65+ autoIndent: 'keep',
66+ folding: true,
67+ foldingStrategy: "indentation",
68+ minimap: {
69+ enabled: false,
70+ },
71+ formatOnType: true,
72+ fontSize: 12,
73+ theme: 'vs-dark',
74+ }
75+ );
76+ editor.onDidChangeModelContent(() => {
77+ myEmit('update:modelValue', editor.getValue())
78+ })
79+ if (props.resize && window && 'ResizeObserver' in window) {
80+ const resizeObserver = new ResizeObserver(entries => {
81+ for (const entry of entries) {
82+ editor?.layout({ ...editor?.getLayoutInfo(), height: entry.contentRect.height })
83+ }
84+ })
85+ resizeObserver.observe(document.getElementById(id)!)
86+ }
87+ })
88+})
89+watch(() => props.height, height => {
90+ editor?.layout({ ...editor?.getLayoutInfo(), height: Number.parseFloat(`${height}`) })
91+})
92+</script>
93+ 
94+<template>
95+ <div
96+ class="monaco-container"
97+ :id="id"
98+ :style="{
99+ height: ['%', 'px'].includes(`${props.height}`) ? props.height : `${props.height}px`,
100+ width: '100%',
101+ resize: props.resize ? 'vertical' : 'none',
102+ }"
103+ ></div>
104+</template>
105+ 
106+<style scoped lang="scss">
107+ 
108+.monaco-container {
109+ border: none !important;
110+ overflow: hidden;
111+ margin: 0!important;
112+}
113+:deep(.monaco-editor .view-ruler) {
114+ display: none;
115+}
116+:deep(.monaco-editor .margin) {
117+ width: 1px;
118+ background-color: var(--el-bg-color-og);
119+}
120+ 
121+:deep(.monaco-editor .margin-view-overlays) {
122+ background-color: var(--el-bg-color-og);
123+ border-right: 1px solid var(--el-color--col-border-color);
124+ width: 34px !important;
125+}
126+ 
127+:deep(.monaco-editor .margin-view-overlays div) {
128+ color: var(--el-text-color-og);
129+}
130+ 
131+:deep(.monaco-editor .monaco-editor-background) {
132+ background-color: var(--el-bg-color-og);
133+}
134+ 
135+:deep(.monaco-editor .current-line-margin) {
136+ border: none !important;
137+}
138+ 
139+:deep(.monaco-editor .view-lines .mtk1) {
140+ color: var(--el-text-color-og);
141+}
142+</style>
Aplugins/alert-center/web-ui/src/components/MyBar.vue+155-0
@@ -0,0 +1,155 @@
1+<script setup lang="ts">
2+import * as echarts from "echarts/core";
3+import { GridComponent, GridComponentOption, TooltipComponent, TooltipComponentOption, LegendComponent, TitleComponent, ToolboxComponent, VisualMapComponent } from "echarts/components";
4+import { BarSeriesOption, BarChart } from "echarts/charts";
5+import { UniversalTransition } from 'echarts/features';
6+import { CanvasRenderer } from "echarts/renderers";
7+import { useEventListener } from "@vueuse/core";
8+import { uuid } from "../shared";
9+import { YAXisOption } from "echarts/types/dist/shared";
10+import { OptionDataValue } from 'echarts/types/src/util/types'
11+ 
12+export type LineData = {
13+ name: string,
14+ nameGap?: number,
15+ data: any[],
16+ unit?: string, // unit
17+ min?: number,
18+ max?: number,
19+ interval?: number,
20+ stack:string,
21+ }
22+type EchartsOption = echarts.ComposeOption<
23+ GridComponentOption | TooltipComponentOption | BarSeriesOption
24+>;
25+echarts.use([
26+ CanvasRenderer,
27+ UniversalTransition,
28+ GridComponent,
29+ TooltipComponent,
30+ ToolboxComponent,
31+ TitleComponent,
32+ BarChart,
33+ VisualMapComponent,
34+ LegendComponent
35+]);
36+const domId = uuid();
37+let myChart: echarts.ECharts
38+const props = withDefaults(defineProps<{
39+ // xAxis Data
40+ xData?: string[],
41+ yData?: string[],
42+ // yAxis Line Data
43+ data?: LineData[],
44+ maxData?:number,
45+ showNum?:boolean,
46+ title?:string,
47+ unit?: string,
48+ scatterUnit?: string,
49+ // yAxis Scatter Data
50+ // LineChart use areaStyle
51+ areaStyle?: boolean,
52+ // chart colors
53+ color?: string[],
54+ theme?: 'dark' | 'light',
55+}>(), {
56+ xData: () => [],
57+ yData: () => [],
58+ data: () => [],
59+ maxData: 10,
60+ unit: '',
61+ scatterUnit: '',
62+ areaStyle: false,
63+ color: () => ['#37D4D1', '#00C7F9', '#0D86E2', '#425ADD', '#E64A19', '#9CCC65', '#A97526', '#2830FF', '#8B00E1', '#0F866A'],
64+ theme: 'dark',
65+});
66+const renderChart = () => {
67+ let seriesData: Array<BarSeriesOption> = [];
68+ let yAxisData: Array<YAXisOption> = [];
69+ props.data.forEach(({ name, data, unit, interval, ...item }) => {
70+ let obj: BarSeriesOption = {
71+ type: 'bar',
72+ name,
73+ data,
74+ barWidth: 60,
75+ emphasis: {
76+ focus: 'series'
77+ },
78+ tooltip: {
79+ valueFormatter: function (value: OptionDataValue | OptionDataValue[]) {
80+ return value + ` ${unit}`;
81+ }
82+ },
83+ ...item
84+ }
85+ seriesData.push(obj)
86+ yAxisData.push({
87+ type: 'value',
88+ // name,
89+ axisLabel: {
90+ formatter: `{value} ${unit}`,
91+ color: '#d4d4d4'
92+ },
93+ nameTextStyle: {
94+ color: '#d4d4d4'
95+ },
96+ splitLine: {
97+ lineStyle: {
98+ color: '#888888'
99+ }
100+ },
101+ })
102+ })
103+ const chartDom = document.getElementById(domId);
104+ if (!myChart) {
105+ myChart = echarts.init(chartDom!);
106+ }
107+ 
108+ const option: EchartsOption = {
109+ tooltip: {
110+ trigger: 'axis',
111+ axisPointer: {
112+ type: 'shadow'
113+ }
114+ },
115+ legend: {
116+ textStyle: {
117+ color: '#D4D4D4'
118+ },
119+ },
120+ grid: {
121+ left: '3%',
122+ right: '4%',
123+ bottom: '3%',
124+ containLabel: true
125+ },
126+ xAxis: {
127+ type: 'category',
128+ axisLine: {
129+ lineStyle: {
130+ color: '#D4D4D4'
131+ }
132+ },
133+ data: props.xData,
134+ },
135+ yAxis: yAxisData,
136+ series: seriesData
137+ }
138+ myChart.setOption(option)
139+}
140+onMounted(() => {
141+ renderChart();
142+})
143+ 
144+watch(() => props.data, () => {
145+ nextTick(renderChart)
146+}, { deep: true })
147+ 
148+useEventListener(window, "resize", () => {
149+ myChart?.resize();
150+});
151+</script>
152+ 
153+<template>
154+ <div :id="domId" ref="loadRef" style="width: 100%; height: 100%;"></div>
155+</template>
Aplugins/alert-center/web-ui/src/components/MyBarLine.vue+177-0
@@ -0,0 +1,177 @@
1+<script lang="ts" setup>
2+import { uuid } from "../shared";
3+import * as echarts from "echarts/core";
4+import { GridComponent, GridComponentOption, TooltipComponent, TooltipComponentOption, LegendComponent, TitleComponent, ToolboxComponent } from "echarts/components";
5+import { useEventListener } from "@vueuse/core";
6+import { LineChart, LineSeriesOption, BarSeriesOption, BarChart } from "echarts/charts";
7+import { UniversalTransition } from 'echarts/features';
8+import { CanvasRenderer } from "echarts/renderers";
9+import { OptionDataValue } from 'echarts/types/src/util/types'
10+import { YAXisOption } from "echarts/types/dist/shared";
11+
12+export type LineData = {
13+ name: string,
14+ nameGap?: number,
15+ data: any[],
16+ unit?: string, // unit
17+ type: 'line' | 'bar',
18+ min?: number,
19+ max?: number,
20+ interval?: number,
21+ barWidth?:number,
22+ yAxisIndex?:number,
23+ }
24+
25+ type EchartsOption = echarts.ComposeOption<
26+ GridComponentOption | TooltipComponentOption | LineSeriesOption | BarSeriesOption
27+ >
28+
29+echarts.use([
30+ CanvasRenderer,
31+ LineChart,
32+ BarChart,
33+ UniversalTransition,
34+ GridComponent,
35+ TooltipComponent,
36+ LegendComponent,
37+ TitleComponent,
38+ ToolboxComponent
39+]);
40+
41+const props = withDefaults(defineProps<{
42+ // xAxis Data
43+ xData?: string[],
44+ // yAxis Bar Data | Line Data
45+ data: LineData[],
46+ // yAxis Line Data
47+ lineData?: LineData[],
48+ // yAxis Bar Data
49+ barData?: LineData[],
50+ // LineChart use areaStyle
51+ areaStyle?: boolean,
52+ // chart colors
53+ color?: string[],
54+ // yAxis Line Name
55+ lineName?: string,
56+ // yAxis Line NameGap
57+ lineNameGap?: number,
58+ }>(), {
59+ xData: () => ['10:00', '11:00', '12:00', '13:00'],
60+ data: () => [],
61+ areaStyle: false,
62+ color: () => ['#37D4D1', '#00C7F9', '#0D86E2', '#425ADD', '#E64A19', '#9CCC65', '#A97526', '#2830FF', '#8B00E1', '#0F866A'],
63+})
64+
65+const domId = uuid();
66+let myChart: echarts.ECharts;
67+const renderChart = () => {
68+ let seriesData: Array<LineSeriesOption | BarSeriesOption> = [];
69+ let yAxisData: Array<YAXisOption> = [];
70+ props.data.forEach(({ name, type, data, unit, min, max, interval, ...item }) => {
71+ let obj: LineSeriesOption | BarSeriesOption = {
72+ name,
73+ data,
74+ type,
75+ tooltip: {
76+ valueFormatter: function (value: OptionDataValue | OptionDataValue[]) {
77+ return value + ` ${unit}`;
78+ }
79+ },
80+ ...item
81+ }
82+ if (type === 'line') obj.yAxisIndex = 1
83+ seriesData.push(obj)
84+ yAxisData.push({
85+ type: 'value',
86+ name,
87+ min,
88+ max,
89+ interval,
90+ axisLabel: {
91+ formatter: `{value} ${unit}`,
92+ color: '#d4d4d4'
93+ },
94+ nameTextStyle: {
95+ color: '#d4d4d4'
96+ },
97+ splitLine: {
98+ lineStyle: {
99+ color: '#888888'
100+ }
101+ },
102+ })
103+ })
104+ const chartDom = document.getElementById(domId);
105+ myChart = echarts.init(chartDom!);
106+ const option: EchartsOption = {
107+ tooltip: {
108+ trigger: 'axis',
109+ axisPointer: {
110+ type: 'cross',
111+ crossStyle: {
112+ color: '#999'
113+ }
114+ }
115+ },
116+ toolbox: {
117+ left: '55%',
118+ feature: {
119+ dataView: { show: true, readOnly: false, title: '数据视图' },
120+ magicType: { show: true, type: ['line', 'bar'], title: { line: '切换为折线图', bar: '切换为柱状图' } },
121+ restore: { show: true, title: '还原' },
122+ saveAsImage: { show: true, title: '下载图片' }
123+ },
124+ iconStyle: {
125+ borderColor: '#d4d4d4',
126+ shadowColor: 'orange'
127+ }
128+ },
129+ legend: {
130+ left: '30%',
131+ data: [{
132+ name: 'Slow SQL',
133+ textStyle: {
134+ color: '#d4d4d4'
135+ }
136+ }, {
137+ name: 'cpu',
138+ textStyle: {
139+ color: '#d4d4d4'
140+ }
141+ }]
142+ },
143+ xAxis: {
144+ type: 'category',
145+ axisTick: { show: false },
146+ axisLine: {
147+ lineStyle: {
148+ color: '#d4d4d4'
149+ }
150+ },
151+ axisPointer: {
152+ type: 'shadow'
153+ },
154+ data: props.xData,
155+ },
156+ yAxis: yAxisData,
157+ // series: props.data
158+ series: seriesData
159+ }
160+ myChart.setOption(option);
161+}
162+
163+// onMounted(() => {
164+// renderChart();
165+// })
166+watch(() => props.data, () => {
167+ nextTick(renderChart)
168+}, { deep: true })
169+useEventListener(window, "resize", () => {
170+ myChart?.resize();
171+});
172+
173+</script>
174+
175+ <template>
176+ <div :id="domId" style="width: 100%;height: 100%;"></div>
177+ </template>
Aplugins/alert-center/web-ui/src/components/MyCard.vue+229-0
@@ -0,0 +1,229 @@
1+<script setup lang="ts">
2+import { useDebounceFn } from '@vueuse/core';
3+import { useWidthOverflow } from '../hooks/dom';
4+ 
5+const props = withDefaults(defineProps<{
6+ title: string,
7+ legend?: { color: string, name: string }[],
8+ bodyPadding?: boolean,
9+ height?: string | number,
10+ collapse?: boolean,
11+ skipBodyHeight?: boolean, // do not set body height
12+ // enable resize bodyHeight
13+ resize?: boolean,
14+ maxBodyHeight?: string,
15+ overflowHidden?: boolean,
16+ withTable?: boolean,
17+}>(), {
18+ bodyPadding: true,
19+ maxBodyHeight: 'unset',
20+ overflowHidden: true,
21+ withTable: false,
22+})
23+const hidden = ref(props.overflowHidden ? 'hidden' : 'unset')
24+const padding = ref(props.bodyPadding ? "24px 16px" : "0px");
25+const withTableBorderRadius = ref(props.withTable ? '8px 8px 0 0' : '8px');
26+const bodyRef = ref<HTMLDivElement>()
27+const bodyHeight = ref(`calc(100% - ${props.bodyPadding ? 90 : 42}px)`)
28+const minBodyHeight = ref(bodyHeight.value)
29+// unset transition animation when resize body height
30+onMounted(() => {
31+ nextTick(() => {
32+ if (!props.skipBodyHeight && bodyRef && bodyRef.value) {
33+ bodyHeight.value = `${bodyRef.value.offsetHeight + (props.bodyPadding && !props.collapse ? 48 : 0)}px`
34+ }
35+ if (bodyRef && bodyRef.value && props.resize && window && 'ResizeObserver' in window) {
36+ const resizeObserver = new ResizeObserver(entries => {
37+ for (const entry of entries) {
38+ if (!collapsing.value) {
39+ bodyHeight.value = `${entry.contentRect.height}px`
40+ if (minBodyHeight.value.startsWith('calc')) {
41+ minBodyHeight.value = bodyHeight.value
42+ }
43+ myEmit('resize-body-height', entry.contentRect.height)
44+ }
45+ }
46+ })
47+ resizeObserver.observe(bodyRef.value)
48+ }
49+ })
50+})
51+const myEmit = defineEmits<{
52+ (event: 'resize-body-height', height: number): void
53+}>()
54+// ctl height
55+const height = computed(() => {
56+ if (!props.height) {
57+ return '100%'
58+ }
59+ return (["%", "px"].includes(`${props.height}`)) ? props.height : `${props.height}px`
60+});
61+// ctl collapse
62+const isCollapse = ref(false)
63+// skip ResizeObserver event when collapsing
64+const collapsing = ref(false)
65+const resetCollapsing = useDebounceFn(() => {
66+ collapsing.value = false
67+ if (bodyRef && bodyRef.value) {
68+ bodyRef.value.style.transition = 'unset'
69+ bodyRef.value.style.minHeight = isCollapse.value ? '0px' : minBodyHeight.value
70+ }
71+}, 500)
72+const toggleCollapse = () => {
73+ collapsing.value = true
74+ if (bodyRef && bodyRef.value) {
75+ bodyRef.value.style.transition = 'height 0.5s cubic-bezier(0.23, 1, 0.32, 1)'
76+ bodyRef.value.style.minHeight = !isCollapse.value ? '0px' : minBodyHeight.value
77+ }
78+ isCollapse.value = !isCollapse.value
79+ resetCollapsing()
80+}
81+const extraRef = ref()
82+const dropdownRef = ref()
83+const { overflow, isFinish, trigger } = useWidthOverflow(extraRef, dropdownRef)
84+defineExpose({
85+ trigger
86+})
87+</script>
88+ 
89+<template>
90+<div class="og-card">
91+ <div class="og-card-header">
92+ <span class="og-card-header--title">
93+ <svg-icon
94+ v-if="props.collapse"
95+ name="arrow"
96+ :class="{'is-collapse': isCollapse}"
97+ @click="toggleCollapse"
98+ />
99+ {{ props.title }}
100+ </span>
101+ <slot name="extra">
102+ <div
103+ ref="extraRef"
104+ class="og-card-header--extra"
105+ :style="{justifyContent: overflow || !isFinish ? 'flex-start' : 'flex-end'}"
106+ >
107+ <el-dropdown ref="dropdownRef" :hide-on-click="false" :max-height="200">
108+ <div></div>
109+ <template #dropdown>
110+ <el-dropdown-menu>
111+ <el-dropdown-item v-for="l in props.legend" :key="l.name">
112+ <div class="hover">
113+ <div :style="{ background: l.color }" class="rect"></div>
114+ <span :title="l.name">{{ l.name }}</span>
115+ </div>
116+ </el-dropdown-item>
117+ </el-dropdown-menu>
118+ </template>
119+ </el-dropdown>
120+ <div v-for="l in props.legend" :key="l.name">
121+ <div :style="{ background: l.color }" class="rect"></div>
122+ <span :title="l.name">{{ l.name }}</span>
123+ </div>
124+ </div>
125+ <!-- <span v-show="overflow">...</span> -->
126+ </slot>
127+ </div>
128+ <div
129+ class="og-card-body"
130+ :class="{'is-collapse': isCollapse}"
131+ ref="bodyRef"
132+ :style="{
133+ resize: props.resize ? 'vertical' : 'none',
134+ }"
135+ >
136+ <div>
137+ <slot />
138+ </div>
139+ </div>
140+</div>
141+</template>
142+ 
143+<style scoped lang="scss">
144+.og-card {
145+ box-sizing: border-box;
146+ height: v-bind(height);
147+ border: 1px solid var(--el-og-border-color);
148+ border-radius: v-bind(withTableBorderRadius);
149+ overflow: v-bind(hidden);
150+ &-header {
151+ display: flex;
152+ overflow: hidden;
153+ background-color: var(--el-color-card-header-color);
154+ justify-content: space-between;
155+ padding: 0 16px;
156+ align-items: center;
157+ min-width: 0;
158+ height: 39px;
159+ border-bottom: 1px solid var(--el-og-border-color);
160+ &--title {
161+ font-weight: 700;
162+ font-size: 16px;
163+ flex-shrink: 0;
164+ > svg {
165+ position: relative;
166+ top: -2px;
167+ margin-right: 12px;
168+ transition: transform var(--el-transition-duration);
169+ &.is-collapse {
170+ transform: rotate(-90deg);
171+ }
172+ }
173+ }
174+ &--extra {
175+ position: relative;
176+ display: flex;
177+ margin-left: 16px;
178+ overflow: hidden;
179+ flex: 1;
180+ min-width: 0;
181+ align-items: center;
182+ font-size: 12px;
183+ height: 40px;
184+ > div {
185+ display: flex;
186+ flex-shrink: 0;
187+ margin-right: 12px;
188+ min-width: 0;
189+ > span {
190+ margin-left: 4px;
191+ }
192+ &:last-of-type {
193+ margin-right: 0;
194+ }
195+ }
196+ }
197+ }
198+ &-body {
199+ box-sizing: border-box;
200+ height: v-bind(bodyHeight);
201+ min-height: v-bind(minBodyHeight);
202+ max-height: v-bind(maxBodyHeight);
203+ overflow: v-bind(hidden);
204+ background-color: var(--el-bg-color-og);
205+ border-radius: 0 0 8px 8px;
206+ will-change: height;
207+ > div {
208+ box-sizing: border-box;
209+ padding: v-bind(padding);
210+ height: v-bind(bodyHeight);
211+ }
212+ &.is-collapse {
213+ height: 0!important;
214+ }
215+ }
216+}
217+.hover {
218+ display: flex;
219+ align-items: center;
220+ > span {
221+ margin-left: 4px;
222+ }
223+}
224+.rect {
225+ width: 16px;
226+ height: 16px;
227+ flex-shrink: 0;
228+}
229+</style>
Aplugins/alert-center/web-ui/src/components/MyDatePicker.vue+103-0
@@ -0,0 +1,103 @@
1+<script setup lang="ts">
2+import dayjs from 'dayjs';
3+import utc from 'dayjs/plugin/utc';
4+import timezone from 'dayjs/plugin/timezone';
5+ 
6+dayjs.extend(utc);
7+dayjs.extend(timezone);
8+ 
9+interface ShortcutsConfigObj {
10+ text: string;
11+ value: number;
12+}
13+ 
14+const props = withDefaults(
15+ defineProps<{
16+ modelValue: any | any[];
17+ type: any;
18+ showShortcuts?: boolean,
19+ startPlaceholder?: string;
20+ endPlaceholder?: string;
21+ valueFormatToISO?: boolean; // 是否处理为ISO8601 格式字符串 '2020-04-02T08:02:17-05:00'
22+ shortcutsConfig?: any[];
23+ }>(),
24+ {
25+ startPlaceholder: '',
26+ endPlaceholder: '',
27+ showShortcuts: true,
28+ valueFormatToISO: false,
29+ shortcutsConfig: () => []
30+ }
31+);
32+const myEmit = defineEmits<{
33+ (event: 'change', value: any | any[]): void;
34+ (event: 'update:modelValue', value: any | any[]): void;
35+}>();
36+ 
37+const getShortcutsConfig = () => {
38+ const { showShortcuts, type, shortcutsConfig } = props;
39+ let shortcutsArr: any[] = [];
40+ if ((type === 'datetimerange' || type === 'daterange') && showShortcuts) {
41+ shortcutsConfig.forEach((item: ShortcutsConfigObj) => {
42+ shortcutsArr.push({
43+ text: item.text,
44+ value: (() => {
45+ const end = new Date();
46+ const start = new Date();
47+ start.setTime(start.getTime() - 3600 * 1000 * item.value);
48+ return [start, end];
49+ })()
50+ })
51+ })
52+ }
53+ return shortcutsArr;
54+}
55+const shortcuts = getShortcutsConfig();
56+ 
57+const dateValue = computed({
58+ get: () => props.modelValue,
59+ set: val => {
60+ let value;
61+ if (val && props.valueFormatToISO) {
62+ value = Array.isArray(val) ? val.map(item => dayjs(item).tz(dayjs.tz.guess()).format()) : dayjs(val).tz(dayjs.tz.guess()).format();
63+ } else if (val && !props.valueFormatToISO) {
64+ let fmt: string;
65+ switch (props.type) {
66+ case 'date':
67+ case 'daterange':
68+ fmt = 'YYYY-MM-DD'
69+ break;
70+ case 'datetime':
71+ case 'datetimerange':
72+ fmt = 'YYYY-MM-DD HH:mm:ss';
73+ break;
74+ case 'year':
75+ fmt = 'YYYY';
76+ break;
77+ case 'month':
78+ fmt = 'YYYY-MM';
79+ break;
80+ default:
81+ fmt = 'YYYY-MM-DD';
82+ break;
83+ }
84+ value = Array.isArray(val) ? val.map(item => dayjs(item).format(fmt)) : dayjs(val).format(fmt);
85+ } else {
86+ value = Array.isArray(val) ? [] : null;
87+ }
88+ myEmit('update:modelValue', value);
89+ myEmit('change', value);
90+ }
91+})
92+</script>
93+ 
94+<template>
95+ <el-date-picker
96+ v-bind="$attrs"
97+ v-model="dateValue"
98+ :type="type"
99+ :shortcuts="shortcuts"
100+ :start-placeholder="startPlaceholder"
101+ :end-placeholder="endPlaceholder"
102+ />
103+</template>
Aplugins/alert-center/web-ui/src/components/MyLine.vue+306-0
@@ -0,0 +1,306 @@
1+<script setup lang="ts">
2+import * as echarts from "echarts/core";
3+import {
4+ GridComponent,
5+ GridComponentOption,
6+ TooltipComponent,
7+ TooltipComponentOption,
8+ ToolboxComponent,
9+ ToolboxComponentOption,
10+ BrushComponent,
11+ BrushComponentOption,
12+ TitleComponent,
13+ TitleComponentOption,
14+ DataZoomComponent,
15+ DataZoomComponentOption,
16+} from "echarts/components";
17+import { LineChart, LineSeriesOption, ScatterChart, ScatterSeriesOption, BarChart, BarSeriesOption } from "echarts/charts";
18+import { UniversalTransition } from 'echarts/features';
19+import { CanvasRenderer } from "echarts/renderers";
20+import { uuid } from "../shared";
21+import { useEventListener, useIntersectionObserver } from "@vueuse/core";
22+import moment from "moment";
23+import { useI18n } from 'vue-i18n';
24+import { useDataZoom } from "../hooks/echarts";
25+import { storeToRefs } from "pinia";
26+import { useMonitorStore } from "../store/monitor";
27+ 
28+export interface LineData {
29+ name: string,
30+ data: any[],
31+ [other: string]: any,
32+}
33+ 
34+type EchartsOption = echarts.ComposeOption<
35+ GridComponentOption | TooltipComponentOption | LineSeriesOption | ScatterSeriesOption |
36+ ToolboxComponentOption | BrushComponentOption | TitleComponentOption | BarSeriesOption |
37+ DataZoomComponentOption
38+>
39+echarts.use([
40+ CanvasRenderer,
41+ LineChart,
42+ UniversalTransition,
43+ GridComponent,
44+ TooltipComponent,
45+ ScatterChart,
46+ ToolboxComponent,
47+ BrushComponent,
48+ TitleComponent,
49+ BarChart,
50+ DataZoomComponent,
51+]);
52+ 
53+const { t } = useI18n();
54+ 
55+const props = withDefaults(defineProps<{
56+ // xAxis Data
57+ xData?: string[],
58+ // yAxis Line Data
59+ data?: LineData[],
60+ unit?: string,
61+ scatterUnit?: string,
62+ // yAxis Scatter Data
63+ scatterData?: LineData,
64+ // LineChart use areaStyle
65+ areaStyle?: boolean,
66+ // chart colors
67+ color?: string[],
68+ theme?: 'dark' | 'light',
69+ /**
70+ * enable brush for select range time, the value is uuid
71+ */
72+ brush?: string,
73+ /**
74+ * brush default select range
75+ */
76+ defaultBrushArea?: string[],
77+ /**
78+ * bar chart
79+ */
80+ bar?: boolean,
81+ stack?: boolean,
82+ enterable?: boolean,
83+ translate?: boolean,
84+ countByDataTimePicker: boolean,
85+ 
86+}>(), {
87+ xData: () => [],
88+ data: () => [],
89+ unit: '',
90+ scatterUnit: '',
91+ areaStyle: false,
92+ color: () => ['#37D4D1', '#00C7F9', '#0D86E2', '#425ADD', '#E64A19', '#9CCC65', '#A97526', '#2830FF', '#8B00E1', '#0F866A'],
93+ theme: 'dark',
94+ translate: true,
95+ countByDataTimePicker: true,
96+})
97+const { brushRange, tab } = storeToRefs(useMonitorStore())
98+const domId = uuid();
99+let myChart: echarts.ECharts
100+const renderChart = () => {
101+ let data: Array<LineSeriesOption | ScatterSeriesOption | BarSeriesOption> = [];
102+ props.data.forEach(d => {
103+ const o: Record<string, any> = {
104+ type: 'line',
105+ symbol: "none",
106+ yAxisIndex: 0,
107+ zlevel: 1,
108+ areaStyle: props.areaStyle ? {} : undefined,
109+ ...d,
110+ }
111+ if (props.bar) {
112+ o.type = 'bar'
113+ o['stack'] = 'total'
114+ o['barMaxWidth'] = 12
115+ }
116+ if (props.stack) {
117+ o['stack'] = 'total'
118+ }
119+ data.push(o)
120+ })
121+ if (props.scatterData) {
122+ data.push({
123+ type: 'scatter',
124+ symbol: "circle",
125+ symbolSize: 6,
126+ yAxisIndex: 1,
127+ zlevel: 1,
128+ ...props.scatterData,
129+ })
130+ }
131+ const chartDom = document.getElementById(domId);
132+ if (!myChart || myChart.isDisposed()) {
133+ myChart = echarts.init(chartDom!);
134+ }
135+ const option: EchartsOption = {
136+ title: {
137+ show: props.data?.length === 0 && (!props.scatterData || props.scatterData?.data?.length === 0),
138+ text: t('sql.isHasData'),
139+ left: 'center',
140+ top: 'center',
141+ textStyle: {
142+ color: props.theme === 'dark' ? '#ddd' : '#AEB3BC',
143+ }
144+ },
145+ color: props.color,
146+ grid: {
147+ left: 15,
148+ right: 15,
149+ top: 25,
150+ bottom: 10,
151+ containLabel: true,
152+ },
153+ tooltip: {
154+ trigger: 'axis',
155+ confine: true,
156+ enterable: props.enterable,
157+ backgroundColor: '#212121',
158+ borderColor: '#4a4a4a',
159+ formatter: params => {
160+ let str = `<div style="${props.enterable ? 'max-height: 155px;' : ''}overflow: auto;color: #D4D4D4">`;
161+ if (Array.isArray(params)) {
162+ params.forEach((p, i) => {
163+ const seriesName = props.translate ? t(`metric.${p.seriesName}`) : p.seriesName;
164+ if (i === 0) {
165+ str += p.name
166+ }
167+ str += `<div style="display: flex;justify-content: space-between;"><div style="margin-right: 24px;">${p.marker} ${seriesName} </div>${p.data}</div>`
168+ })
169+ } else {
170+ const seriesName = props.translate ? t(`metric.${params.seriesName}`) : params.seriesName;
171+ str += `${params.name}<div style="display: flex;justify-content: space-between;"><div style="margin-right: 24px;">${params.marker} ${seriesName}</div>${params.data}</div>`
172+ }
173+ str += '</div>'
174+ return str
175+ }
176+ },
177+ toolbox: {
178+ right: -1000,
179+ feature: {
180+ dataZoom: { yAxisIndex: false },
181+ brush: { show: false }
182+ }
183+ },
184+ brush: {
185+ brushStyle: {
186+ borderWidth: 0,
187+ color: props.theme === 'dark' ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)',
188+ },
189+ transformable: !props.defaultBrushArea,
190+ outOfBrush: props.scatterData && props.defaultBrushArea && props.xData.length > 0
191+ ? {
192+ color: props.scatterData.itemStyle.color
193+ }
194+ : undefined
195+ },
196+ xAxis: {
197+ type: 'category',
198+ boundaryGap: false,
199+ axisTick: { show: false },
200+ axisLine: {
201+ lineStyle: {
202+ color: props.theme === 'dark' ? '#D4D4D4' : '#e0e0e0'
203+ }
204+ },
205+ axisLabel: {
206+ color: props.theme === 'dark' ? '#D4D4D4' : '#868F9C',
207+ formatter: v => moment(new Date(v)).format("HH:mm")
208+ },
209+ data: props.xData,
210+ },
211+ yAxis: props.scatterData
212+ ? [
213+ {
214+ type: 'value',
215+ axisLabel: {
216+ color: props.theme === 'dark' ? '#D4D4D4' : '#868F9C',
217+ formatter: `{value}${props.unit}`
218+ },
219+ splitLine: {
220+ lineStyle: { color: props.theme === 'dark' ? '#4a4a4a' : '#e0e0e0' }
221+ }
222+ },
223+ {
224+ type: 'value',
225+ axisLabel: {
226+ color: props.theme === 'dark' ? '#D4D4D4' : '#868F9C',
227+ formatter: `{value}${props.scatterUnit}`
228+ },
229+ splitLine: { show: false }
230+ }
231+ ]
232+ : {
233+ type: 'value',
234+ axisLabel: {
235+ color: props.theme === 'dark' ? '#D4D4D4' : '#868F9C',
236+ formatter: `{value}${props.unit}`
237+ },
238+ splitLine: {
239+ lineStyle: { color: props.theme === 'dark' ? '#4a4a4a' : '#e0e0e0' }
240+ }
241+ },
242+ series: data.length > 0 ? data : []
243+ }
244+ myChart.setOption(option, true)
245+ if (!props.countByDataTimePicker && Array.isArray(props.defaultBrushArea) && props.defaultBrushArea.length === 2 && props.xData.length > 0) {
246+ console.log("defaultBrushArea", props.defaultBrushArea);
247+ nextTick(() => {
248+ let interval = (moment(props.defaultBrushArea![1]).valueOf() - moment(props.defaultBrushArea![0]).valueOf()) / 1000;
249+ const startIndexPosi = myChart.convertToPixel({ xAxisIndex: 0 }, 0);
250+ const endIndexPosi = myChart.convertToPixel({ xAxisIndex: 0 }, props.xData.length - 1);
251+ let range: any[] = [];
252+ if (interval <= 120) {
253+ const startIndex = props.xData?.length === 3 ? 1 : props.xData.indexOf(props.defaultBrushArea![0])
254+ const r = myChart.convertToPixel({ xAxisIndex: 0 }, startIndex)
255+ const allWidth = endIndexPosi - startIndexPosi;
256+ const width = interval / 120 * allWidth;
257+ const start = Math.round(r - width / 2);
258+ const end = Math.round(r + width / 2);
259+ range = [start, end]
260+ } else {
261+ range = [startIndexPosi, endIndexPosi]
262+ }
263+ myChart.dispatchAction({
264+ type: 'brush',
265+ areas: [
266+ {
267+ brushType: 'lineX',
268+ range,
269+ }
270+ ]
271+ })
272+ })
273+ }
274+ if (props.brush) {
275+ useDataZoom(myChart)
276+ }
277+ if (brushRange.value.length > 0 && tab.value === 0) {
278+ myChart.dispatchAction({ type: 'dataZoom', start: 0, end: 100 })
279+ }
280+}
281+// lazy load
282+const myEmit = defineEmits<{
283+ (event: 'load'): void,
284+}>()
285+const loadRef = ref<HTMLDivElement>()
286+const { stop } = useIntersectionObserver(
287+ loadRef,
288+ ([{ isIntersecting }]) => {
289+ if (isIntersecting) {
290+ myEmit('load')
291+ nextTick(renderChart)
292+ stop()
293+ }
294+ }
295+)
296+watch(() => props.data, () => {
297+ nextTick(renderChart)
298+}, { deep: true })
299+useEventListener(window, "resize", () => {
300+ myChart?.resize();
301+});
302+</script>
303+ 
304+<template>
305+<div :id="domId" ref="loadRef" style="width: 100%; height: 100%;"></div>
306+</template>
Aplugins/alert-center/web-ui/src/components/MyMessage.vue+71-0
@@ -0,0 +1,71 @@
1+<script setup lang="ts">
2+import { WarningFilled, CircleCloseFilled } from '@element-plus/icons-vue'
3+ 
4+const props = withDefaults(defineProps<{
5+ type: string;
6+ tip?: Error | undefined | string;
7+ defaultTip?: string;
8+}>(), {
9+ type: 'info',
10+ tip: undefined,
11+ defaultTip: '',
12+})
13+ 
14+const baseColor = ref('');
15+ 
16+const finalTip = ref<Error | undefined | string >();
17+ 
18+onMounted(() => {
19+ switch (props.type) {
20+ case 'info':
21+ baseColor.value = '#0093FF';
22+ break;
23+ case 'error':
24+ baseColor.value = '#ff4d4f99';
25+ break;
26+ default:
27+ baseColor.value = '#FFF'
28+ }
29+ 
30+ finalTip.value = props.tip !== undefined ? props.tip : props.defaultTip
31+})
32+ 
33+</script>
34+ 
35+<template>
36+ <div class="message-error">
37+ <div class="message-error-info" :style="{ border: `1px solid ${baseColor}` }">
38+ <el-icon :color="baseColor" size="18px" v-if="props.type === 'info'">
39+ <WarningFilled />
40+ </el-icon>
41+ <el-icon :color="baseColor" size="18px" v-if="props.type === 'error'">
42+ <CircleCloseFilled />
43+ </el-icon>
44+ <p>
45+ {{ finalTip }}
46+ </p>
47+ </div>
48+ </div>
49+</template>
50+ 
51+<style scoped lang="scss">
52+.message-error {
53+ display: flex;
54+ justify-content: center;
55+ 
56+ &-info {
57+ display: flex;
58+ align-items: center;
59+ max-width: 60%;
60+ padding: 4px 20px;
61+ border: 1px solid #0093FF;
62+ 
63+ &>p {
64+ margin: 0 0 0 10px;
65+ line-height: 24px;
66+ color: var(--el-text-color-og);
67+ font-size: 12px;
68+ }
69+ }
70+}
71+</style>
Aplugins/alert-center/web-ui/src/components/MyOperator.vue+23-0
@@ -0,0 +1,23 @@
1+<template>
2+ <div class="operator">
3+ <div class="left"><slot name="left" /></div>
4+ <div class="right"><slot name="right" /></div>
5+ </div>
6+</template>
7+ 
8+<style scoped lang="scss">
9+ .operator {
10+ display: flex;
11+ justify-content: space-between;
12+ align-items: center;
13+ margin-bottom: 16px;
14+ .left {
15+ display: flex;
16+ }
17+ .right {
18+ display: flex;
19+ justify-content: flex-end;
20+ gap: 12px;
21+ }
22+ }
23+</style>
Aplugins/alert-center/web-ui/src/components/MyPie.vue+178-0
@@ -0,0 +1,178 @@
1+<script setup lang="ts">
2+import * as echarts from 'echarts/core';
3+import {
4+ TooltipComponent,
5+ TooltipComponentOption,
6+ LegendComponent,
7+ LegendComponentOption,
8+ GridComponentOption,
9+ TitleComponent,
10+ TitleComponentOption
11+} from 'echarts/components';
12+import { PieChart, PieSeriesOption } from 'echarts/charts';
13+import { LabelLayout } from 'echarts/features';
14+import { CanvasRenderer } from 'echarts/renderers';
15+import { uuid } from '../shared';
16+import { useDebounceFn, useEventListener, useIntersectionObserver } from '@vueuse/core';
17+ 
18+echarts.use([
19+ TooltipComponent,
20+ LegendComponent,
21+ PieChart,
22+ CanvasRenderer,
23+ LabelLayout,
24+ TitleComponent
25+]);
26+ 
27+const colorArray: Array<string> = [
28+ '#37D4D1',
29+ '#00C7F9',
30+ '#0D86E2',
31+ '#425ADD',
32+ '#8B00E1',
33+ '#9CCC65',
34+ '#A97526',
35+ '#2830FF',
36+ '#E64A19',
37+ '#0F866A',
38+ '#5CDF73',
39+ '#FEEC21'
40+]
41+ 
42+type EChartsOption = echarts.ComposeOption<
43+ TooltipComponentOption | LegendComponentOption | PieSeriesOption | GridComponentOption | TitleComponentOption
44+>;
45+ 
46+const props = withDefaults(
47+ defineProps<{
48+ smooth?: boolean
49+ areaColor?: echarts.graphic.LinearGradient,
50+ radius?: [string, string]
51+ center?: [string, string]
52+ data: {value: number, name: string}[],
53+ text?: string,
54+ height?: string,
55+ showLegend?: boolean,
56+ color?: Array<string>,
57+ theme?: string
58+ }>(),
59+ {
60+ smooth: true,
61+ radius: () => ['50%', '70%'],
62+ center: () => ['30%', '50%'],
63+ data: () => [],
64+ text: '总数',
65+ height: '100px',
66+ showLegend: true,
67+ theme: 'dark'
68+ }
69+);
70+ 
71+const domId = uuid()
72+const percent2num = (percent: string) => Number.parseFloat(percent) / 100
73+const position = reactive({ left: '0', top: '0', lineLength: '0' })
74+const calcPosition = (chartDom: HTMLElement) => {
75+ const cs = getComputedStyle(chartDom.parentNode as Element)
76+ const paddingLeft = Number.parseFloat(cs.getPropertyValue('padding-left'))
77+ const paddingTop = Number.parseFloat(cs.getPropertyValue('padding-top'))
78+ const base = chartDom.offsetWidth > chartDom.offsetHeight ? chartDom.offsetHeight : chartDom.offsetWidth
79+ const radius = base * percent2num(props.radius[1]) * 0.5
80+ position.left = chartDom.offsetWidth * percent2num(props.center[0]) - radius + paddingLeft + 'px'
81+ position.top = chartDom.offsetHeight * percent2num(props.center[1]) - radius + paddingTop + 'px'
82+ position.lineLength = radius * 2 + 'px'
83+}
84+const renderChart = () => {
85+ const chartDom = document.getElementById(domId);
86+ if (chartDom) {
87+ const instance = echarts.getInstanceByDom(chartDom)
88+ instance && instance.dispose()
89+ }
90+ const myChart = echarts.init(chartDom!);
91+ const option: EChartsOption = {
92+ legend: {
93+ show: props.showLegend,
94+ icon: "rect",
95+ type: "scroll",
96+ pageIconSize: 10,
97+ pageTextStyle: {
98+ fontSize: 10,
99+ },
100+ textStyle: {
101+ color: '#D4D4D4'
102+ },
103+ itemHeight: 10,
104+ itemGap: 10,
105+ orient: 'vertical',
106+ selectedMode: false,
107+ right: '10%',
108+ bottom: '5%',
109+ },
110+ color: props.color ? props.color : colorArray,
111+ series: [
112+ {
113+ name: 'dbtypes',
114+ type: 'pie',
115+ radius: props.radius,
116+ center: props.center,
117+ avoidLabelOverlap: true,
118+ label: {
119+ show: false
120+ },
121+ itemStyle: {
122+ borderColor: props.theme === 'dark' ? '#4A4A4A' : '#fff',
123+ borderWidth: 1
124+ },
125+ minAngle: 10,
126+ data: props.data
127+ }
128+ ]
129+ };
130+ myChart.setOption(option);
131+}
132+// lazy load
133+const loadRef = ref<HTMLDivElement>()
134+const { stop } = useIntersectionObserver(
135+ loadRef,
136+ ([{ isIntersecting }]) => {
137+ if (isIntersecting) {
138+ nextTick(renderChart)
139+ stop()
140+ }
141+ }
142+)
143+watch(() => props.data, () => {
144+ nextTick(renderChart)
145+}, { deep: true })
146+ 
147+useEventListener(window, 'resize', useDebounceFn(() => {
148+ const chartDom = document.getElementById(domId);
149+ if (chartDom) {
150+ const instance = echarts.getInstanceByDom(chartDom)
151+ instance && instance.resize()
152+ calcPosition(chartDom)
153+ }
154+}, 500))
155+</script>
156+ 
157+<template>
158+<div ref="loadRef" :id="domId" style="width: 100%; height: 100%;position: relative;"></div>
159+<!-- <div class="text">
160+ <div style="font-size: 20px;">{{ props.data.reduce((acc, cur) => acc + cur.value, 0) }}</div>
161+ <div style="font-size: 12px;">{{ props.text }}</div>
162+</div> -->
163+</template>
164+ 
165+<style scoped>
166+.text {
167+ position: relative;
168+ left: v-bind('position.left');
169+ top: v-bind('position.top');
170+ width: v-bind('position.lineLength');
171+ height: v-bind('position.lineLength');
172+ text-align: center;
173+ display: flex;
174+ flex-direction: column;
175+ justify-content: center;
176+ align-items: center;
177+}
178+</style>
Aplugins/alert-center/web-ui/src/components/MyProgress.vue+146-0
@@ -0,0 +1,146 @@
1+<script setup lang="ts">
2+ 
3+type optionType = { label: string, value: number, total?: boolean, color?: string, hide?: boolean }
4+ 
5+const props = withDefaults(defineProps<{
6+ data: Array<optionType>;
7+ width?: string;
8+ height?: string;
9+ text?: string;
10+ onlyOne?: boolean
11+ fixTotal?: number
12+ fixColor?: string[]
13+}>(), {
14+ width: '70%',
15+ height: '15px',
16+ onlyOne: false,
17+});
18+ 
19+const progressData = reactive<{
20+ line: Array<{ percentage: string, backgroundColor: string }>
21+ isHasTotal: boolean,
22+ totalBackgroundColor: string,
23+ contentData: Array<optionType>
24+}>({
25+ line: [],
26+ isHasTotal: false,
27+ totalBackgroundColor: '',
28+ contentData: []
29+});
30+ 
31+onMounted(() => {
32+ let curTotal = 0;
33+ let isHasTotal = false;
34+ props.data.forEach(item => {
35+ if (item.total) {
36+ isHasTotal = true;
37+ progressData.isHasTotal = true;
38+ curTotal = item.value
39+ }
40+ });
41+ 
42+ let tempProgressData = props.data;
43+ if (!isHasTotal) {
44+ curTotal = props.data.reduce((sum: number, item: optionType) => sum + item.value, 0);
45+ } else {
46+ tempProgressData = props.data.filter((item) => {
47+ if (item.total) {
48+ progressData.isHasTotal = true;
49+ progressData.totalBackgroundColor = item.color || '';
50+ return false;
51+ }
52+ return true
53+ });
54+ }
55+ 
56+ progressData.line = tempProgressData.map((item) => {
57+ const percentage = `${(item.value / curTotal * 100).toFixed(2)}%`;
58+ return {
59+ percentage,
60+ backgroundColor: item.color || ''
61+ }
62+ })
63+ 
64+ progressData.contentData = props.onlyOne ? [props.data[0]] : props.data;
65+});
66+ 
67+</script>
68+ 
69+<template>
70+ <div class="my-progress" :style="{ height: props.height, width: props.width }">
71+ <el-tooltip
72+ effect="light"
73+ placement="right"
74+ >
75+ <template #content>
76+ <div class="my-progress-content" v-for="(item, index) in progressData.contentData" :key="index">
77+ <div class="my-progress-content-item" v-show="!item.hide">
78+ <div
79+ class="my-progress-content-legend"
80+ :style="{
81+ backgroundColor: props.fixColor ? props.fixColor[index] : item.color
82+ }"
83+ />
84+ <div class="my-progress-content-label">{{ item.label }}</div>
85+ </div>
86+ <p class="my-progress-content-label" v-show="!item.hide">{{ index === 1 ? props.fixTotal || item.value : item.value }}</p>
87+ </div>
88+ </template>
89+ <div
90+ class="my-progress-line"
91+ :style="{
92+ backgroundColor: progressData.isHasTotal ? progressData.totalBackgroundColor : 'transparent'
93+ }"
94+ >
95+ <div
96+ v-for="(item, index) in progressData.line"
97+ :key="index"
98+ :style="{
99+ height: props.height,
100+ width: item.percentage,
101+ backgroundColor: item.backgroundColor
102+ }"
103+ />
104+ </div>
105+ </el-tooltip>
106+ <div class="my-progress-text" v-if="props.text !== undefined">{{ props.text }}</div>
107+ </div>
108+</template>
109+ 
110+<style scoped lang="scss">
111+.my-progress {
112+ width: 100%;
113+ 
114+ &-line {
115+ width: 80%;
116+ display: flex;
117+ }
118+ 
119+ &-content {
120+ display: flex;
121+ align-items: center;
122+ justify-content: space-between;
123+ width: 160px;
124+ 
125+ &-item {
126+ display: flex;
127+ align-items: center;
128+ }
129+
130+ &-legend {
131+ width: 16px;
132+ height: 4px;
133+ margin-right: 6px;
134+ }
135+ 
136+ &-label {
137+ font-size: 12px;
138+ color: #D4D4D4;
139+ }
140+ }
141+ 
142+ &-text {
143+ font-size: 12px;
144+ }
145+}
146+</style>
Aplugins/alert-center/web-ui/src/components/MyTable.vue+149-0
@@ -0,0 +1,149 @@
1+<script setup lang="ts">
2+import { useDebounceFn, useEventListener } from "@vueuse/core";
3+import { ElTable } from "element-plus";
4+import { useRequest } from "vue-request";
5+import ogRequest from "../request";
6+ 
7+const props = withDefaults(
8+ defineProps<{
9+ api: string;
10+ post?: boolean;
11+ maxHeight?: string | number;
12+ extra?: Record<string, any>;
13+ flag?: boolean;
14+ formatter?: (d: any) => void;
15+ frontPagination?: boolean
16+ }>(),
17+ {}
18+);
19+ 
20+const state = reactive({
21+ data: [] as any[],
22+ total: 0,
23+ page: 1,
24+ size: 10,
25+});
26+const maxHeight = ref(props.maxHeight || 500);
27+ 
28+const myEmit = defineEmits<{(event: 'load-data', data: unknown[]): void}>()
29+const multipleTableRef = ref<InstanceType<typeof ElTable>>()
30+const toggleRowSelection = (row: any, selected: boolean, prop: string) => {
31+ nextTick(() => {
32+ multipleTableRef.value!.toggleRowSelection(state.data.find(d => d[prop] === row[prop]), selected)
33+ })
34+}
35+defineExpose({ toggleRowSelection })
36+ 
37+interface TableData {
38+ list: any[],
39+ total: number,
40+}
41+const { run, data } = useRequest(() => {
42+ const payload = {
43+ page: state.page,
44+ pageSize: state.size,
45+ ...(props.extra || {}),
46+ sortKey: sortKey.value,
47+ sortOrder: sortOrder.value,
48+ }
49+ if (props.post) {
50+ return ogRequest.post<TableData>(props.api, payload)
51+ } else {
52+ return ogRequest.get<TableData>(props.api, payload)
53+ }
54+}, { manual: true, debounceInterval: 200 });
55+watch(data, d => {
56+ state.data = d?.list.map(item => {
57+ if (props.formatter) {
58+ props.formatter(item)
59+ }
60+ const r = { ...item }
61+ if (r.children && Array.isArray(r.children)) {
62+ r["__vem_id"] = Math.random().toString(36).slice(2);
63+ r.children.forEach(
64+ (c: any) =>
65+ (c["__vem_id"] = Math.random().toString(36).slice(2))
66+ );
67+ }
68+ return r;
69+ })!;
70+ state.total = d?.total!;
71+ myEmit('load-data', d?.list || [])
72+})
73+const top = ref(0);
74+useEventListener(
75+ window,
76+ "resize",
77+ useDebounceFn(
78+ () => (maxHeight.value = document.body.clientHeight - top.value - 60),
79+ 500
80+ )
81+);
82+onMounted(() => {
83+ if (!props.maxHeight) {
84+ const topRect = document
85+ .querySelector(".z-table")
86+ ?.getBoundingClientRect() as DOMRect;
87+ top.value = topRect.top;
88+ maxHeight.value = document.body.clientHeight - topRect.top - 60;
89+ state.size = Math.max(Number.parseInt(`${maxHeight.value / 32}`) - 1, 10)
90+ }
91+ run();
92+});
93+if (!props.frontPagination) {
94+ watch(() => state.page, run);
95+ if (props.extra) {
96+ watch(() => props.extra, run, { deep: true });
97+ }
98+ watch(() => props.flag, run);
99+}
100+const _data = computed(() => {
101+ if (!props.frontPagination) {
102+ return state.data
103+ }
104+ return state.data.filter((d, i) => i >= (state.page - 1) * state.size && i < state.page * state.size)
105+})
106+ 
107+// sort
108+const sortKey = ref('')
109+const sortOrder = ref('')
110+const handleSortChange = (cell: {prop: string | null, order: 'ascending' | 'descending' | null}) => {
111+ sortKey.value = cell.prop || ''
112+ sortOrder.value = cell.order || ''
113+ run()
114+}
115+</script>
116+ 
117+<template>
118+<el-table
119+ ref="multipleTableRef"
120+ v-bind="$attrs"
121+ class="z-table"
122+ :data="_data"
123+ :max-height="maxHeight"
124+ :style="{ 'max-height': `${maxHeight}px` }"
125+ row-key="__vem_id"
126+ border
127+ @sort-change="handleSortChange"
128+>
129+ <slot />
130+</el-table>
131+<el-pagination
132+ v-model:currentPage="state.page"
133+ v-model:pageSize="state.size"
134+ :total="state.total"
135+ class="pagination"
136+ layout="total,prev,pager,next"
137+ hide-on-single-page
138+ background
139+ small
140+/>
141+</template>
142+ 
143+<style scoped>
144+.pagination {
145+ margin-top: 16px;
146+ display: flex;
147+ justify-content: flex-end;
148+}
149+</style>
Aplugins/alert-center/web-ui/src/components/Proxies.vue+54-0
@@ -0,0 +1,54 @@
1+<template>
2+ <div>
3+ <el-select v-model="machineValue" @change="selectMachine" :clearable="!notClearable" :style="{ width: width ? width + 'px' : 'auto' }">
4+ <el-option v-for="item in machineList" :key="item.hostId" :label="item.privateIp + '(' + item.publicIp + ')'" :value="item.hostId" />
5+ </el-select>
6+ </div>
7+</template>
8+ 
9+<script lang="ts" setup>
10+import ogRequest from "../request";
11+import { useRequest } from "vue-request";
12+ 
13+const props = withDefaults(
14+ defineProps<{
15+ width?: string;
16+ notClearable?: boolean;
17+ autoSelectFirst?: boolean;
18+ }>(),
19+ {
20+ width: "",
21+ notClearable: false,
22+ autoSelectFirst: false,
23+ }
24+);
25+const emit = defineEmits(["change", "loaded"]);
26+ 
27+const machineValue = ref<any>();
28+const machineList = ref<Array<any>>([]);
29+ 
30+const selectMachine = (val: string[]) => {
31+ emit("change", machineValue);
32+};
33+ 
34+onMounted(() => {
35+ clusterData();
36+});
37+const { data: rer, run: clusterData } = useRequest(
38+ () => {
39+ return ogRequest.get("/observability/v1/environment/prometheus", "");
40+ },
41+ { manual: true }
42+);
43+ 
44+watch(rer, (rer) => {
45+ if (rer && rer.data && Object.keys(rer.data).length) {
46+ machineList.value = rer.data.rows;
47+ emit("loaded");
48+ if (props.autoSelectFirst && machineList.value.length > 0) {
49+ machineValue.value = machineList.value[0].hostId;
50+ emit("change", machineValue.value);
51+ }
52+ }
53+});
54+</script>
Aplugins/alert-center/web-ui/src/components/SvgIcon.vue+31-0
@@ -0,0 +1,31 @@
1+<template>
2+ <svg
3+ aria-hidden="true"
4+ style="vertical-align: middle; cursor: pointer;"
5+ :class="$style.icon"
6+ :fill="color"
7+ >
8+ <use :href="symbolId" />
9+ </svg>
10+</template>
11+ 
12+<script setup lang="ts">
13+const props = withDefaults(
14+ defineProps<{
15+ prefix?: string;
16+ name: string;
17+ color?: string;
18+ }>(),
19+ {
20+ prefix: "icon",
21+ }
22+);
23+const symbolId = computed(() => `#${props.prefix}-${props.name}`);
24+</script>
25+ 
26+<style scoped module>
27+.icon {
28+ width: 14px;
29+ height: 14px;
30+}
31+</style>
Aplugins/alert-center/web-ui/src/hooks/DelModal.vue+53-0
@@ -0,0 +1,53 @@
1+<script setup lang="ts">
2+const props = withDefaults(
3+ defineProps<{
4+ content: string
5+ id: string
6+ resolve:() => void
7+ }>(),
8+ {}
9+);
10+const visible = ref(true)
11+const close = () => document.getElementById(props.id)?.remove()
12+const confirm = () => {
13+ props.resolve()
14+ visible.value = false
15+}
16+</script>
17+ 
18+<template>
19+<el-dialog
20+ :width="400"
21+ v-model="visible"
22+ :close-on-click-modal="false"
23+ custom-class="del-dialog"
24+ center
25+ draggable
26+ @close="close"
27+>
28+ <template #header></template>
29+ <div class="del-text">确认删除</div>
30+ <div class="content-text">{{props.content}}</div>
31+ <template #footer>
32+ <el-button style="padding: 5px 20px;" type="primary" @click="confirm">确定</el-button>
33+ <el-button style="padding: 5px 20px;" @click="() => visible = false">取消</el-button>
34+ </template>
35+</el-dialog>
36+</template>
37+ 
38+<style>
39+.del-dialog .el-dialog__header {
40+ display: none;
41+}
42+</style>
43+<style scoped>
44+.del-text {
45+ text-align: center;
46+ font-weight: bold;
47+ font-size: 16px;
48+ margin-bottom: 20px;
49+}
50+.content-text {
51+ text-align: center;
52+}
53+</style>
Aplugins/alert-center/web-ui/src/hooks/SelectTable.vue+120-0
@@ -0,0 +1,120 @@
1+<script setup lang="ts">
2+const props = withDefaults(
3+ defineProps<{
4+ id: string
5+ resolve:(value: unknown[]) => void
6+ api: string
7+ post?: boolean
8+ width?: number
9+ title?: string
10+ filter?: any
11+ selections?: unknown[]
12+ uid: string
13+ type: 'radio' | 'selection'
14+ }>(),
15+ {
16+ width: 700,
17+ title: '选择项',
18+ post: false,
19+ type: 'radio',
20+ selections: () => ([])
21+ }
22+);
23+ 
24+const multipleTableRef = ref()
25+let cacheData: string[] = []
26+let cacheList: unknown[] = []
27+const loadData = (data: unknown[]) => {
28+ cacheList = data
29+ cacheData = data.map((d: any) => d[props.uid])
30+ selections.value.forEach((s: any) => {
31+ if (cacheData.includes(s[props.uid])) {
32+ multipleTableRef.value!.toggleRowSelection(s, true, props.uid)
33+ }
34+ })
35+}
36+const selections = ref<unknown[]>(props.selections)
37+const selectionChange = (rows: unknown[]) => {
38+ const dbids = selections.value.map((s: any) => s[props.uid])
39+ const selectedDbids: string[] = []
40+ rows.forEach((r: any) => {
41+ selectedDbids.push(r[props.uid])
42+ if (!dbids.includes(r[props.uid])) {
43+ selections.value.push(r)
44+ }
45+ })
46+ const removeDbids = cacheData.filter(key => !selectedDbids.includes(key))
47+ selections.value = selections.value.filter((s: any) => !removeDbids.includes(s[props.uid]))
48+}
49+ 
50+const radio = ref('')
51+ 
52+const visible = ref(true)
53+const close = () => document.getElementById(props.id)?.remove()
54+const confirm = () => {
55+ if (props.type === 'selection') {
56+ props.resolve(selections.value)
57+ } else {
58+ props.resolve([cacheList.find((l: any) => l[props.uid] === radio.value)])
59+ }
60+ visible.value = false
61+}
62+</script>
63+ 
64+<template>
65+<el-dialog
66+ :width="$props.width"
67+ :title="$props.title"
68+ v-model="visible"
69+ custom-class="nodes-dialog"
70+ :close-on-click-modal="false"
71+ draggable
72+ @close="close"
73+>
74+ <div class="flex-end" style="margin-bottom: 10px;">
75+ <slot></slot>
76+ </div>
77+ <my-table
78+ ref="multipleTableRef"
79+ :api="$props.api"
80+ :max-height="360"
81+ :extra="$props.filter"
82+ @selection-change="selectionChange"
83+ @load-data="loadData"
84+ :post="$props.post"
85+ >
86+ <el-table-column v-if="$props.type === 'radio'" width="50" align="center">
87+ <template #default="{row}">
88+ <el-radio v-model="radio" :label="row[$props.uid]"></el-radio>
89+ </template>
90+ </el-table-column>
91+ <el-table-column v-if="$props.type === 'selection'" width="50" align="center" type="selection"></el-table-column>
92+ <slot name="column"></slot>
93+ </my-table>
94+ <template #footer>
95+ <el-button style="padding: 5px 20px;" @click="() => visible = false">取消</el-button>
96+ <el-button style="padding: 5px 20px;" type="primary" @click="confirm">确定</el-button>
97+ </template>
98+</el-dialog>
99+</template>
100+ 
101+<style>
102+.nodes-dialog .el-dialog__footer {
103+ border-top: 1px solid #eee;
104+ padding-bottom: 10px;
105+}
106+.nodes-dialog .el-dialog__header {
107+ border-bottom: 1px solid #eee;
108+ margin-right: 0px;
109+ padding-top: 10px;
110+}
111+.nodes-dialog .el-dialog__header .el-dialog__title {
112+ font-size: 14px;
113+}
114+.nodes-dialog .el-dialog__headerbtn {
115+ top: -2px;
116+}
117+.nodes-dialog .el-radio .el-radio__label {
118+ display: none;
119+}
120+</style>
Aplugins/alert-center/web-ui/src/hooks/dialog.ts+16-0
@@ -0,0 +1,16 @@
1+import { uuid } from "../shared";
2+import DelModalVue from "./DelModal.vue";
3+ 
4+/**
5+ * delete comfirm
6+ * @param content delete tip
7+ * @returns void
8+ */
9+export const useDelConfirm = (content: string) => {
10+ return new Promise((resolve) => {
11+ const el = document.createElement('div');
12+ el.id = 'del_' + uuid()
13+ document.body.appendChild(el)
14+ createApp(DelModalVue, { id: el.id, resolve, content }).mount(`#${el.id}`)
15+ })
16+}
Aplugins/alert-center/web-ui/src/hooks/dom.ts+46-0
@@ -0,0 +1,46 @@
1+import { useEventListener } from "@vueuse/core";
2+import { Ref } from "vue";
3+ 
4+/**
5+ * is dom element overflow
6+ * @param targetRef Ref<dom>
7+ * @param dropdownRef element-plus dropdownRef
8+ * @returns is overflow
9+ */
10+export const useWidthOverflow = (targetRef: Ref<HTMLElement>, dropdownRef: Ref<any>) => {
11+ const overflow = ref(false)
12+ const isFinish = ref(false)
13+ const stopWatch = watch(targetRef, t => {
14+ if (t) {
15+ overflow.value = t.scrollWidth > t.offsetWidth
16+ isFinish.value = true
17+ useEventListener(t, 'mouseenter', (e: MouseEvent) => {
18+ if (dropdownRef && dropdownRef.value) {
19+ // const l = t.getBoundingClientRect().left - document.querySelector('#app')!.getBoundingClientRect().left
20+ // dropdownRef.value.$el.style.left = e.pageX - l + 'px'
21+ dropdownRef.value.$el.style.left = '300px'
22+ if (overflow.value) {
23+ dropdownRef.value.handleOpen()
24+ }
25+ }
26+ })
27+ useEventListener(t, 'mouseleave', (e: MouseEvent) => {
28+ if (dropdownRef && dropdownRef.value) {
29+ dropdownRef.value.handleClose()
30+ }
31+ })
32+ stopWatch()
33+ }
34+ }, { immediate: true })
35+ const trigger = () => {
36+ isFinish.value = false
37+ overflow.value = false
38+ if (targetRef && targetRef.value) {
39+ targetRef.value.style.justifyContent = 'flex-start';
40+ overflow.value = targetRef.value.scrollWidth > targetRef.value.offsetWidth
41+ isFinish.value = true
42+ }
43+ }
44+ useEventListener(window, 'resize', trigger)
45+ return { overflow, isFinish, trigger }
46+}
Aplugins/alert-center/web-ui/src/hooks/echarts.ts+133-0
@@ -0,0 +1,133 @@
1+import { EChartsOption } from "echarts";
2+import { ECharts } from "echarts/core";
3+import { Ref } from "vue";
4+import { useMonitorStore } from "../store/monitor";
5+ 
6+type XAxisType = EChartsOption['XAXisOption'] & { data: string[] }
7+ 
8+const monitorStore = useMonitorStore()
9+ 
10+export const brushMap = reactive<Record<string, any>>({})
11+const toggleBrush = (brushType: string | boolean, uuid: string) => {
12+ brushMap[uuid]['chart']?.dispatchAction({
13+ type: 'takeGlobalCursor',
14+ key: 'brush',
15+ brushOption: {
16+ brushType,
17+ }
18+ })
19+ brushMap[uuid]['range'].start = ''
20+ brushMap[uuid]['range'].end = ''
21+}
22+const addBrushEvent = (uuid: string) => {
23+ const ev = (params: any) => {
24+ if (brushMap[uuid]['chart']?.containPixel('grid', [params.offsetX, params.offsetY])) {
25+ toggleBrush('lineX', uuid)
26+ brushMap[uuid]['chart']?.getZr().off('mousedown', ev)
27+ }
28+ }
29+ brushMap[uuid]['chart']?.getZr().off('mousedown', ev)
30+ brushMap[uuid]['chart']?.getZr().on('mousedown', ev)
31+}
32+export const useClearBrushSelect = (uuid: string) => {
33+ brushMap[uuid]['chart']?.dispatchAction({
34+ type: 'brush',
35+ command: 'clear',
36+ areas: []
37+ })
38+ // toggleBrush(false, uuid)
39+ addBrushEvent(uuid)
40+}
41+export const useRange = (uuid: string) => {
42+ const range = ref({ start: '', end: '' })
43+ watch(() => brushMap[uuid], m => {
44+ if (m) {
45+ range.value = m['range']
46+ }
47+ })
48+ return range
49+}
50+ 
51+/**
52+ * brush select range
53+ * @param myChart echarts instance
54+ * @param uuid uuid
55+ * @returns range
56+ */
57+export const useRangeXAixsByBrush = (myChart: ECharts, uuid: string) => {
58+ brushMap[uuid] = Object.create(null)
59+ brushMap[uuid]['range'] = shallowReactive({
60+ start: '',
61+ end: ''
62+ })
63+ brushMap[uuid]['chart'] = myChart
64+ addBrushEvent(uuid)
65+ const brushEnd = (params: any) => {
66+ const option = myChart.getOption() as EChartsOption
67+ const xAxis = option.xAxis as XAxisType | XAxisType[]
68+ const xData = Array.isArray(xAxis) ? xAxis[0].data : xAxis.data
69+ if (params.areas[0]) {
70+ brushMap[uuid]['range'].start = xData[myChart.convertFromPixel({ xAxisIndex: 0 }, params.areas[0].range[0])]
71+ brushMap[uuid]['range'].end = xData[myChart.convertFromPixel({ xAxisIndex: 0 }, params.areas[0].range[1])]
72+ if (monitorStore.tab === 0) {
73+ monitorStore.filters[0].rangeTime = -1
74+ monitorStore.filters[0].time = [new Date(brushMap[uuid]['range'].start), new Date(brushMap[uuid]['range'].end)]
75+ }
76+ }
77+ }
78+ myChart.off('brushEnd', brushEnd)
79+ myChart.on('brushEnd', brushEnd)
80+ return brushMap[uuid]['range']
81+}
82+ 
83+const toggleDataZoomBrush = (myChart: ECharts, a: boolean) => {
84+ myChart.dispatchAction({
85+ type: 'takeGlobalCursor',
86+ key: 'dataZoomSelect',
87+ dataZoomSelectActive: a
88+ })
89+}
90+ 
91+const toggleDataZoomSelect = (myChart: ECharts, a: boolean) => {
92+ toggleDataZoomBrush(myChart, a);
93+}
94+ 
95+const watchBrush = (myChart: ECharts) => {
96+ const startBrush = (params: { offsetX: number, offsetY: number }) => {
97+ if (myChart.containPixel('grid', [params.offsetX, params.offsetY])) {
98+ toggleDataZoomSelect(myChart, true)
99+ myChart.getZr().off('mousedown', startBrush)
100+ }
101+ }
102+ myChart.getZr().off('mousedown', startBrush)
103+ myChart.getZr().on('mousedown', startBrush)
104+}
105+const addZoomEvent = (myChart: ECharts, inZoom: Ref<boolean>) => {
106+ myChart.on('datazoom', (params: any) => {
107+ toggleDataZoomBrush(myChart, false)
108+ watchBrush(myChart)
109+ if (params.startValue !== undefined) {
110+ return
111+ }
112+ if (params.batch && params.batch[0].start === 0 && params.batch[0].end === 0) {
113+ return
114+ }
115+ if (params.start !== undefined && params.start === 0 && params.end === 100) {
116+ return
117+ }
118+ inZoom.value = true
119+ const start = params.batch[0].startValue
120+ const end = params.batch[0].endValue
121+ const option = myChart.getOption() as any
122+ monitorStore.timeRange = [option.xAxis[0].data[start], option.xAxis[0].data[end]]
123+ monitorStore.brushRange = [start, end]
124+ })
125+}
126+export const useDataZoom = (myChart: ECharts) => {
127+ const inZoom = ref(false)
128+ watchBrush(myChart)
129+ addZoomEvent(myChart, inZoom)
130+ return {
131+ inZoom,
132+ }
133+}
Aplugins/alert-center/web-ui/src/hooks/time.ts+17-0
@@ -0,0 +1,17 @@
1+import { ComputedGetter, Ref } from "vue"
2+ 
3+export const useIntervalTime = (cb: Function, timeFn: ComputedGetter<number> | Ref<number>) => {
4+ const timer = ref<number>()
5+ timer.value = setInterval(cb, typeof timeFn === 'function' ? timeFn() : timeFn.value)
6+ watch(timeFn, t => {
7+ if (timer.value) {
8+ clearInterval(timer.value)
9+ timer.value = setInterval(cb, t)
10+ }
11+ })
12+ onBeforeUnmount(() => {
13+ if (timer.value) {
14+ clearInterval(timer.value)
15+ }
16+ })
17+}
Aplugins/alert-center/web-ui/src/i18n/Lang.vue+24-0
@@ -0,0 +1,24 @@
1+<script setup lang="ts">
2+import { i18n, LocaleType } from '.';
3+ 
4+const changeLang = (locale: LocaleType) => {
5+ i18n.global.locale.value = locale
6+}
7+</script>
8+ 
9+<template>
10+ <el-dropdown>
11+ <svg-icon name="lang" style="width: 20px;height: 20px;" />
12+ <template #dropdown>
13+ <el-dropdown-menu>
14+ <el-dropdown-item
15+ v-for="locale in $i18n.availableLocales"
16+ :key="`locale-${locale}`"
17+ @click="changeLang(locale as LocaleType)"
18+ >
19+ {{ locale === 'en' ? 'en' : 'zhCn' }}
20+ </el-dropdown-item>
21+ </el-dropdown-menu>
22+ </template>
23+ </el-dropdown>
24+</template>
Aplugins/alert-center/web-ui/src/i18n/en.ts+210-0
@@ -0,0 +1,210 @@
1+export default {
2+ app: {
3+ name: 'WorkBench',
4+ menuName: 'Monitor',
5+ refresh: 'refresh',
6+ autoRefresh: 'refresh',
7+ custom: 'Custom',
8+ startDate: 'Start Date',
9+ endDate: 'End Date',
10+ to: 'To',
11+ running: 'running',
12+ disconnect: 'disconnect',
13+ unknown: 'unknown',
14+ query: 'query',
15+ delete: 'delete',
16+ cancel: 'cancel',
17+ confirm: 'confirm',
18+ add: 'add',
19+ edit: 'edit',
20+ operate: 'operate',
21+ view: 'view',
22+ download: 'download',
23+ reset: 'Reset',
24+ back: 'Back',
25+ preview: 'Preview',
26+ queryFail: 'Query failed',
27+ saveSuccess: 'Save successful',
28+ saveFail: 'Save failed',
29+ testSuccess: 'Test successful',
30+ testFail: 'Test failed',
31+ delSuccess: 'Deletion successful',
32+ delFail: 'Deletion failed',
33+ selecteDataTip: 'Please select a piece of data',
34+ selectDatasTip: 'Please select the datas',
35+ tip: 'Tip',
36+ setAlertConfig: 'Set up the Alert interface',
37+ selectUpdateDataTip: 'Please select the data first before editing',
38+ deleteTip: 'Are you sure you want to delete this data?',
39+ keepOneData: 'Please keep at least one piece of data',
40+ uninstallTip: 'Before using the Alert Center module and the Notify Configuration module, you need to install Prometheus. Please go to "Intelligent OPS - Instance Monitoring " to install the server Prometheus. Otherwise, it will not be available.',
41+ disConfigTip: 'Before using the Alert Center module and the Notify Configuration module, set up the alert interface first.',
42+ },
43+ alertRule: {
44+ title: 'alert rule',
45+ alertTitle: 'alert rule',
46+ notifyTitle: 'Notify rule',
47+ addTitle: 'add rule',
48+ detailTitle: 'rule detail',
49+ editTitle: 'edit rule',
50+ ruleName: 'Rule Name',
51+ ruleType: 'Rule Type',
52+ level: 'level',
53+ index: 'index',
54+ ruleItem: 'alert rule',
55+ ruleExpComb: 'alert rule combination',
56+ ruleContent: 'alert content',
57+ notifyDuration: 'statistical duration',
58+ alertDesc: 'alert description',
59+ repeat: 'duplicate',
60+ silence: 'silence',
61+ silenceTime: 'silence time',
62+ alertNotify: 'alert notify',
63+ notifyWay: 'notify way',
64+ second: 'second',
65+ minute: 'minute',
66+ hour: 'hour',
67+ day: 'day',
68+ log: 'log',
69+ serious: 'serious',
70+ warn: 'warn',
71+ info: 'info',
72+ ruleNamePlaceholder: 'select the rule name please',
73+ selectRuleType: 'select the rule type please',
74+ selectLevelType: 'select the level please',
75+ isRepeat: 'Yes',
76+ isNotRepeat: 'No',
77+ isSilence: 'Yes',
78+ isNotSilence: 'No',
79+ to: 'to',
80+ firing: 'firing',
81+ recover: 'recover',
82+ and: 'and',
83+ or: 'or',
84+ table: ['ruleName', 'ruleType', 'level', 'isRepeat', 'isSilence', 'alertNotify', 'alertContent', 'operate', '告警规则', '规则组合'],
85+ ruleItemNum: 'number',
86+ ruleItemExp: 'rule expression',
87+ normalAction: 'keeping happen',
88+ increaseAction: 'keeping increase',
89+ decreaseAction: 'keeping decrease',
90+ alertContentTitle: 'Alert content description',
91+ alertContentTip: 'The main purpose of the alert content is to notify the user, and it supports configuring parameters using ${xxx} to represent the parameters. For example, the alert content is "In the last minute, the CPU usage rate of ${nodeName} has been greater than 90%", where ${nodeName} is a parameter. The configurable parameters are as follows:'
92+ },
93+ alertTemplate: {
94+ title: 'alert template',
95+ addTitle: 'add template',
96+ editTitle: 'edit template',
97+ detailTitle: 'template detail',
98+ templateName: 'template name',
99+ selectRule: 'select the rule',
100+ templateNamePlaceholder: 'Please enter template name',
101+ table: ['template name', 'operate'],
102+ },
103+ alertRecord: {
104+ title: 'alert record',
105+ alertTotal: 'total number of alert',
106+ alerting: 'firing',
107+ alerted: 'recover',
108+ unread: 'unread',
109+ read: 'read',
110+ markAsRead: 'mark as read',
111+ markAsUnread: 'mark as unread',
112+ recordStatus: 'record status',
113+ alertStatus: 'alert status',
114+ level: 'alert level',
115+ recordStatusTip: 'Alert handling statistics',
116+ alertStatusTip: 'Alert status statistics',
117+ levelTip: 'Alert level statistics',
118+ cluster: 'cluster/instance',
119+ alertTimeRange: 'alert time range',
120+ startTimePlaceholder: 'start time',
121+ endTimePlaceholder: 'end time',
122+ table: ['instance', 'IP', 'datasoure type', 'alert template', 'alert rule', 'rule type', 'alert level', 'start time', 'end time', 'duration(sec)', 'alert status', 'notify way', 'record status', 'operate'],
123+ tableDataSelectTip: 'Please select at least one data',
124+ detailTitle: 'alert detail',
125+ alertInstance: 'alert instance',
126+ alertRule: 'alert rule',
127+ alertContent: 'alert content',
128+ alertRelationView: 'related view',
129+ clusterName: 'cluster name',
130+ IPAndPort: 'IP and port',
131+ nodeRole: 'cluster node role',
132+ },
133+ AlertClusterNodeConf: {
134+ title: 'alert configuration',
135+ confBtn: 'configuration',
136+ table: ['cluster/instance', 'datasoure type', 'IP', 'alert template', 'operate'],
137+ detailTitle: 'alert template config',
138+ selectedInstance: 'selected instances',
139+ alertTemplateTab: 'select alert template',
140+ alertRuleTab: 'select alert rule',
141+ generateTip1: 'The system will generate a default template name for the selected rules:"',
142+ generateTip2: '".You can also customize and modify the name.',
143+ setInterface: 'set alert interface',
144+ alertIp: 'Interface IP',
145+ alertPort: 'Interface Port',
146+ interfaceAddr: 'Full interface',
147+ nodeNamePlaceholder: 'Please input cluster/instance name'
148+ },
149+ notifyTemplate: {
150+ title: 'notify template',
151+ addTitle: 'add template',
152+ editTitle: 'edit template',
153+ detailTitle: 'template detail',
154+ templateNamePlaceholder: 'Please enter the name of the notify template',
155+ templateTypePlaceholder: 'Please enter the type of the notify template',
156+ templateDescPlaceholder: 'Please enter the description of the notify template',
157+ notifyTitlePlaceholder: 'Please enter the title',
158+ notifyContentPlaceholder: 'Please enter the notify content',
159+ templateName: 'template name',
160+ templateType: 'template type',
161+ notifyTitle: 'notify title',
162+ notifyContent: 'notify content',
163+ templateDesc: 'template description',
164+ email: 'email',
165+ WeCom: 'WeCom',
166+ DingTalk: 'DingTalk',
167+ previewContent: 'message preview',
168+ notifyContentTitle: 'notify content description',
169+ notifyContentTip: 'The message content mainly wraps the alert content, adds additional content, and finally sends the wrapped alert content to the user. It supports configuring parameters using ${xxx} to represent the parameters. The configurable parameter meanings are as follows:'
170+ },
171+ notifyWay: {
172+ title: 'notify way',
173+ addTitle: 'add notify way',
174+ editTitle: 'edit notify way',
175+ detailTile: 'notify way detail',
176+ namePlaceholder: 'Please enter the name of the notify way',
177+ name: 'name',
178+ notifyType: 'notify type',
179+ notifyTarget: 'notify target',
180+ personId: 'person ID',
181+ deptId: 'department ID',
182+ emailPlaceholder: 'Please enter email address(es). If there are multiple email addresses, please separate them with commas',
183+ personIdPlaceholder: 'Please enter person ID(s). If there are multiple person, please separate them with commas. Either person ID or department ID is required (or both).',
184+ deptIdPlaceholder: 'Please enter department ID(s). If there are multiple department, please separate them with commas. Either person ID or department ID is required (or both).',
185+ templatePlaceholder: 'Please select a notify template',
186+ emailTemplate: 'email template',
187+ WeComTemplate: 'WeCom template',
188+ DingTalkTemplate: 'DingTalk template',
189+ existOneOfTargetTip: 'Either person ID or department ID is required (or both)',
190+ },
191+ notifyConfig: {
192+ title: 'notify config',
193+ emailConfig: 'eamil config',
194+ weComConfig: 'WeCom config',
195+ dingTalkConfig: 'DingTalk config',
196+ senderEmail: 'sender email address',
197+ sender: 'sender',
198+ sever: 'server',
199+ port: 'SMTP port',
200+ account: 'SMTP account',
201+ passwd: 'SMTP password',
202+ agentId: 'agent ID',
203+ weComAppKey: 'WeCom appKey',
204+ weComSecret: 'WeCom secret',
205+ appKey: 'appKey',
206+ dingSecret: 'app secret',
207+ inputTip: 'Pleasse input',
208+ test: 'test',
209+ }
210+}
Aplugins/alert-center/web-ui/src/i18n/index.ts+14-0
@@ -0,0 +1,14 @@
1+import { createI18n } from 'vue-i18n';
2+import zhCn from './zhCn';
3+import en from './en';
4+ 
5+export type LocaleType = "zhCn" | "en"
6+ 
7+export const i18n = createI18n({
8+ legacy: false,
9+ locale: 'zhCn',
10+ messages: {
11+ zhCn,
12+ en,
13+ },
14+})
Aplugins/alert-center/web-ui/src/i18n/zhCn.ts+210-0
@@ -0,0 +1,210 @@
1+export default {
2+ app: {
3+ name: '一体化平台',
4+ menuName: '监控告警',
5+ refresh: '刷新',
6+ autoRefresh: '自动刷新',
7+ custom: '自定义',
8+ startDate: '开始日期',
9+ endDate: '结束日期',
10+ to: '至',
11+ running: '运行中',
12+ disconnect: '连接异常',
13+ unknown: '未知',
14+ query: '查询',
15+ delete: '删除',
16+ cancel: '取消',
17+ confirm: '确定',
18+ add: '新增',
19+ edit: '编辑',
20+ operate: '操作',
21+ view: '查看',
22+ download: '下载',
23+ reset: '重置',
24+ back: '返回',
25+ preview: '预览',
26+ queryFail: '查询失败',
27+ saveSuccess: '保存成功',
28+ saveFail: '保存失败',
29+ delSuccess: '删除成功',
30+ delFail: '删除失败',
31+ testSuccess: '测试成功',
32+ testFail: '测试失败',
33+ selecteDataTip: '请选择一个数据',
34+ selectDatasTip: '请选择数据',
35+ tip: '提示',
36+ setAlertConfig: '设置告警接口',
37+ selectUpdateDataTip: '请先勾选数据,再进行编辑',
38+ deleteTip: '确定删除该数据吗?',
39+ keepOneData: '至少保留一个数据',
40+ uninstallTip: '在监控告警模块和通知配置模块使用前,需要先安装prometheus,请到“智能运维——实例监控”中安装服务端prometheus,否则将无法使用。',
41+ disConfigTip: '在监控告警模块和通知配置模块使用前,先设置告警接口',
42+ },
43+ alertRule: {
44+ title: '告警规则',
45+ alertTitle: '告警规则',
46+ notifyTitle: '通知规则',
47+ addTitle: '新增规则',
48+ editTitle: '编辑规则',
49+ detailTitle: '规则详细',
50+ ruleName: '规则名称',
51+ ruleType: '规则类型',
52+ level: '告警级别',
53+ ruleItem: '告警规则',
54+ ruleExpComb: '告警规则组合',
55+ ruleContent: '告警内容',
56+ notifyDuration: '统计周期',
57+ alertDesc: '告警描述',
58+ repeat: '是否重复通知',
59+ silence: '是否静默',
60+ silenceTime: '静默时间',
61+ alertNotify: '告警通知',
62+ notifyWay: '通知方式',
63+ second: '秒',
64+ minute: '分',
65+ hour: '时',
66+ day: '天',
67+ index: '指标',
68+ log: '日志',
69+ serious: '严重',
70+ warn: '警告',
71+ info: '提示',
72+ ruleNamePlaceholder: '请输入规则名称',
73+ selectRuleType: '请选择规则类型',
74+ selectLevelType: '请选择告警级别',
75+ isRepeat: '是',
76+ isNotRepeat: '否',
77+ isSilence: '是',
78+ isNotSilence: '否',
79+ to: '至',
80+ firing: '告警时',
81+ recover: '告警恢复时',
82+ and: '和',
83+ or: '或',
84+ table: ['规则名称', '规则类型', '告警级别', '是否重复告警', '静默设置', '告警通知', '告警内容', '操作', '告警规则', '规则组合'],
85+ ruleItemNum: '编号',
86+ ruleItemExp: '规则表达式',
87+ normalAction: '持续发生',
88+ increaseAction: '持续增长',
89+ decreaseAction: '持续下降',
90+ alertContentTitle: '告警内容说明',
91+ alertContentTip: '告警内容主用来通知用户,它支持配置参数,用${xxx}来表示参数。比如:告警内容为“最近一分钟,${nodeName}的CPU使用率一直大于90%”,其中${nodeName}为参数。可配置参数如下:',
92+ },
93+ alertTemplate: {
94+ title: '告警模板',
95+ addTitle: '新增模板',
96+ editTitle: '编辑模板',
97+ detailTitle: '模板详细',
98+ templateName: '模板名称',
99+ templateNamePlaceholder: '请输入模板名称',
100+ selectRule: '选择规则',
101+ table: ['模板名称', '操作'],
102+ },
103+ alertRecord: {
104+ title: '告警记录',
105+ alertTotal: '告警总数',
106+ alerting: '告警中',
107+ alerted: '已结束',
108+ unread: '未读',
109+ read: '已读',
110+ markAsRead: '标记为已读',
111+ markAsUnread: '标记为未读',
112+ recordStatus: '信息状态',
113+ alertStatus: '告警状态',
114+ level: '告警等级',
115+ recordStatusTip: '告警处理统计',
116+ alertStatusTip: '告警状态统计',
117+ levelTip: '告警级别统计',
118+ cluster: '集群/实例',
119+ alertTimeRange: '告警时间范围',
120+ startTimePlaceholder: '开始时间',
121+ endTimePlaceholder: '结束时间',
122+ table: ['实例名称', '主机IP', '数据库类型', '告警模板', '告警规则', '规则类型', '告警等级', '开始时间', '结束时间', '持续时间(秒)', '告警状态', '通知方式', '信息状态', '操作'],
123+ tableDataSelectTip: '至少选择一个数据',
124+ detailTitle: '告警详细',
125+ alertInstance: '告警对象',
126+ alertRule: '告警规则',
127+ alertContent: '告警内容',
128+ alertRelationView: '关联视图',
129+ clusterName: '集群名称',
130+ IPAndPort: '实例IP与端口',
131+ nodeRole: '实例角色',
132+ },
133+ AlertClusterNodeConf: {
134+ title: '告警配置',
135+ confBtn: '配置',
136+ table: ['集群/实例', '数据库类型', '主机IP', '告警模板', '操作'],
137+ detailTitle: '告警模板配置',
138+ selectedInstance: '已选实例',
139+ alertTemplateTab: '选择告警模板',
140+ alertRuleTab: '选择告警规则',
141+ generateTip1: '系统将为已选的规则生成默认的模板名称:“',
142+ generateTip2: '”。您也可以自定义修改名称:',
143+ setInterface: '设置告警接口',
144+ alertIp: '接口IP',
145+ alertPort: '接口端口',
146+ interfaceAddr: '完整接口',
147+ nodeNamePlaceholder: '请输入集群/实例名称',
148+ },
149+ notifyTemplate: {
150+ title: '通知模板',
151+ addTitle: '新增模板',
152+ editTitle: '修改模板',
153+ detailTitle: '模板详细',
154+ templateNamePlaceholder: '请输入通知模板名称',
155+ templateTypePlaceholder: '请输入通知模板类型',
156+ templateDescPlaceholder: '请输入通知模板描述',
157+ notifyTitlePlaceholder: '请输入主题',
158+ notifyContentPlaceholder: '请输入通知内容',
159+ templateName: '模板名称',
160+ templateType: '模板类型',
161+ notifyTitle: '通知主题',
162+ notifyContent: '通知内容',
163+ templateDesc: '模板描述',
164+ email: '邮箱',
165+ WeCom: '企业微信',
166+ DingTalk: '钉钉',
167+ previewContent: '消息预览',
168+ notifyContentTitle: '消息内容说明',
169+ notifyContentTip: '消息内容主要将告警内容进行包装,添加额外内容,最终将包装的告警内容发送给用户。它支持配置参数,用${xxx}来表示参数。可配置的参数含义如下:'
170+ },
171+ notifyWay: {
172+ title: '通知方式',
173+ addTitle: '新增通知方式',
174+ editTitle: '编辑通知方式',
175+ detailTile: '通知方式详细',
176+ namePlaceholder: '请输入通知方式名称',
177+ name: '通知方式名称',
178+ notifyType: '发送方式',
179+ notifyTarget: '发送目标',
180+ personId: '用户ID',
181+ deptId: '部门ID',
182+ emailPlaceholder: '请输入邮箱地址,存在多个邮箱地址,可以用逗号分隔。',
183+ personIdPlaceholder: '请输入用户ID,存在多个用户,可以用逗号分隔。用户ID和部门ID,至少有一个必填',
184+ deptIdPlaceholder: '请输入部门ID,存在多个部门,可以用逗号分隔。用户ID和部门ID,至少有一个必填',
185+ templatePlaceholder: '请选择通知模板',
186+ emailTemplate: '邮箱模板',
187+ WeComTemplate: '企业微信模板',
188+ DingTalkTemplate: '钉钉模板',
189+ existOneOfTargetTip: '用户ID和部门ID,至少有一个必填',
190+ },
191+ notifyConfig: {
192+ title: '通知渠道',
193+ emailConfig: '邮箱配置',
194+ weComConfig: '企业微信配置',
195+ dingTalkConfig: '钉钉配置',
196+ senderEmail: '发件邮箱',
197+ sender: '发件人',
198+ sever: '服务器',
199+ port: 'SMTP端口号',
200+ account: 'SMTP账号',
201+ passwd: 'SMTP密码',
202+ agentId: '应用ID',
203+ weComAppKey: '企业ID',
204+ weComSecret: '企业密钥',
205+ appKey: 'appKey',
206+ dingSecret: 'app密钥',
207+ inputTip: '请输入',
208+ test: '测试',
209+ }
210+}
Aplugins/alert-center/web-ui/src/layout/MyBreadcrumb.vue+62-0
@@ -0,0 +1,62 @@
1+<script setup lang="ts">
2+import { routes } from '../router/index';
3+ 
4+const route = useRoute()
5+const hasComponents: Record<string, string> = {}
6+for (const r of routes) {
7+ if (r.component) {
8+ hasComponents[r.meta?.title!] = r.path
9+ }
10+ if (r.children && r.children.length > 0) {
11+ for (const child of r.children) {
12+ if (child.component) {
13+ hasComponents[child.meta?.title!] = child.path
14+ }
15+ }
16+ }
17+}
18+const paths = computed(() => {
19+ const arr: { title?: string, path?: string }[] = []
20+ for (const r of routes) {
21+ if (r.path === route.path) {
22+ arr.push({ title: r.meta?.title })
23+ return arr
24+ }
25+ if (r.children && r.children.length > 0) {
26+ for (const child of r.children) {
27+ let isWithithParamPath = false;
28+ if (child.path.includes('/:')) {
29+ isWithithParamPath = route.path.includes(child.path.slice(0, child.path.indexOf('/:')))
30+ }
31+ if ((child.path === route.path || isWithithParamPath) && child.meta?.breadcrumb) {
32+ child.meta.breadcrumb.forEach(title => {
33+ arr.push(hasComponents[title] ? { title, path: hasComponents[title] } : { title })
34+ })
35+ arr.push({ title: child.meta.title })
36+ return arr
37+ }
38+ }
39+ }
40+ }
41+ return arr;
42+})
43+</script>
44+ 
45+<template>
46+<el-breadcrumb>
47+ <el-breadcrumb-item v-for="p in paths" :key="p.title" :to="p.path">{{ $t(p.title!) }}</el-breadcrumb-item>
48+</el-breadcrumb>
49+</template>
50+ 
51+<style scoped lang="scss">
52+ :deep(.el-breadcrumb__inner.is-link, .el-breadcrumb__inner a) {
53+ // color: $og-text-color;
54+ font-weight: 400;
55+ }
56+ :deep(.el-breadcrumb__inner.is-link:hover, .el-breadcrumb__inner a:hover) {
57+ text-decoration: underline;
58+ }
59+ :deep(.el-breadcrumb__item:last-of-type .el-breadcrumb__inner) {
60+ font-weight: 700;
61+ }
62+</style>
Aplugins/alert-center/web-ui/src/layout/MyHeader.vue+70-0
@@ -0,0 +1,70 @@
1+<script setup lang="ts">
2+import { Fold, Expand } from '@element-plus/icons-vue';
3+import Lang from '../i18n/Lang.vue';
4+ 
5+const props = withDefaults(defineProps<{
6+ showIcon: boolean
7+}>(), {
8+ showIcon: true
9+})
10+const themeText = ref("dark")
11+const collapse = ref(false)
12+const myEmit = defineEmits<{
13+ (event: 'collapse', collapse: boolean): void,
14+ (event: 'changeTheme'): void
15+}>()
16+const emitCollapse = (c: boolean) => {
17+ myEmit('collapse', c)
18+ collapse.value = c
19+}
20+const onChangeTheme = () => {
21+ myEmit('changeTheme')
22+ themeText.value = themeText.value === 'dark' ? 'light' : 'dark';
23+}
24+</script>
25+ 
26+<template>
27+ <div class="header-container">
28+ <div class="left">
29+ <el-icon :size="18" v-if="collapse && props.showIcon" style="cursor: pointer;" @click="() => emitCollapse(false)"><Expand /></el-icon>
30+ <el-icon :size="18" v-if="!collapse && props.showIcon" style="cursor: pointer;" @click="() => emitCollapse(true)"><Fold /></el-icon>
31+ <my-breadcrumb class="bread" />
32+ </div>
33+ <div class="right">
34+ <span class="header-change-theme" @click.stop="onChangeTheme()">{{ themeText }}</span>
35+ <Lang />
36+ </div>
37+ </div>
38+</template>
39+ 
40+<style scoped lang="scss">
41+.header-container {
42+ height: var(--vem-header-height);
43+ background-color: var(--el-bg-color-sub);
44+ font-size: 16px;
45+ padding: 0 16px;
46+ letter-spacing: 1px;
47+ display: flex;
48+ align-items: center;
49+ justify-content: space-between;
50+ flex-wrap: nowrap;
51+ flex-shrink: 0;
52+}
53+.left {
54+ display: flex;
55+ align-items: center;
56+}
57+.right {
58+ display: flex;
59+ align-items: center;
60+}
61+.bread {
62+ margin-left: 10px;
63+}
64+ 
65+.header-change-theme {
66+ font-size: 12px;
67+ margin-right: 20px;
68+ cursor: pointer;
69+}
70+</style>
Aplugins/alert-center/web-ui/src/layout/MyMenu.vue+92-0
@@ -0,0 +1,92 @@
1+<script setup lang="ts">
2+import { RouteRecordRaw } from 'vue-router';
3+import _ from 'lodash-es';
4+import { routes } from '../router/index';
5+ 
6+const props = withDefaults(defineProps<{
7+ collapse: boolean
8+}>(), {})
9+ 
10+const route = useRoute();
11+ 
12+type appDataType = {
13+ curRoutes: Array<RouteRecordRaw>
14+}
15+const appData = reactive<appDataType>({
16+ curRoutes: []
17+});
18+ 
19+const deatRoute = (r: Array<RouteRecordRaw>) => {
20+ return r.filter(item => !item.meta?.hidden).map(m => {
21+ if (Array.isArray(m.children)) {
22+ m.children = deatRoute(m.children);
23+ }
24+ return m;
25+ });
26+}
27+ 
28+onMounted(() => {
29+ appData.curRoutes = deatRoute(_.cloneDeep(routes));
30+});
31+</script>
32+ 
33+<template>
34+<div class="logo" v-if="!props.collapse">{{ $t('app.name') }}</div>
35+<el-menu
36+ class="vem-menu"
37+ :default-active="route.path"
38+ router
39+ :collapse="props.collapse"
40+ :collapse-transition="false"
41+ :style="{width: collapse ? 'auto' : '200px'}"
42+>
43+ <div v-for="route in appData.curRoutes" :key="route.path">
44+ <el-sub-menu v-if="route.children && route.children.length > 0" :index="route.path">
45+ <template #title>
46+ <svg-icon
47+ :name="route.meta?.icon!"
48+ style="margin-right: 6px"
49+ />
50+ <span>{{ $t(route.meta?.title!) }}</span>
51+ </template>
52+ <el-menu-item v-for="sub in route.children" :key="sub.path" :index="sub.path">
53+ {{ $t(sub.meta?.title!) }}
54+ </el-menu-item>
55+ </el-sub-menu>
56+ <el-menu-item v-else :index="route.path">
57+ <svg-icon
58+ :name="route.meta?.icon!"
59+ style="margin-right: 6px"
60+ />
61+ <template #title>{{ $t(route.meta?.title!) }}</template>
62+ </el-menu-item>
63+ </div>
64+</el-menu>
65+</template>
66+ 
67+<style scoped lang="scss">
68+.logo {
69+ width: auto;
70+ height: 60px;
71+ // color: $og-text-color;
72+ line-height: 60px;
73+ text-align: center;
74+ font-size: 18px;
75+ font-weight: bold;
76+ background-color: var(--el-menu-bg-color);
77+}
78+.vem-menu {
79+ overflow-y: auto;
80+ overflow-x: hidden;
81+ height: 100%;
82+ border-right: 0;
83+}
84+.vem-menu.el-menu--collapse .el-sub-menu__title {
85+ span {
86+ display: none;
87+ }
88+ .el-sub-menu__icon-arrow {
89+ display: none!important;
90+ }
91+}
92+</style>
Aplugins/alert-center/web-ui/src/main.ts+22-0
@@ -0,0 +1,22 @@
1+import { createApp } from "vue";
2+import 'element-plus/theme-chalk/src/message.scss'
3+import 'element-plus/theme-chalk/src/table.scss'
4+import 'element-plus/theme-chalk/el-loading.css'
5+import 'element-plus/theme-chalk/el-message.css'
6+import 'element-plus/theme-chalk/dark/css-vars.css';
7+import '@/assets/style/dark.scss'
8+import '@/assets/style/reset.scss';
9+import '@/assets/style/common.scss';
10+import App from "./App.vue";
11+import { createPinia } from "pinia";
12+import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
13+import "virtual:svg-icons-register";
14+import router from "./router";
15+import { vLoading } from "element-plus";
16+import { i18n } from './i18n'
17+ 
18+const pinia = createPinia();
19+pinia.use(piniaPluginPersistedstate);
20+const app = createApp(App)
21+app.directive('loading', vLoading)
22+app.use(i18n).use(router).use(pinia).mount("#app");
Aplugins/alert-center/web-ui/src/request/axios.ts+99-0
@@ -0,0 +1,99 @@
1+import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
2+import { ElMessage } from "element-plus";
3+ 
4+const observabilityBaseURL = process.env.mode === 'production' ? '/plugins/alert-center' : '';
5+// const observabilityBaseURL ='/plugins/observability-instance-zh25';
6+interface ApiResponse<T = any> {
7+ code: string | number;
8+ data: T;
9+ error: any;
10+ msg: string;
11+}
12+ 
13+const handleData = <T>(res: AxiosResponse<ApiResponse<T>, any>) => {
14+ if (res.status === 200 && res.data) {
15+ return Promise.resolve(res.data)
16+ } else {
17+ ElMessage.error(res?.data.msg || "请求错误")
18+ return Promise.reject(res?.data.msg || "请求错误")
19+ }
20+}
21+ 
22+export class Request {
23+ constructor(config?: AxiosRequestConfig) {
24+ if (config) {
25+ for (const key in config) {
26+ if (key in axios.defaults) {
27+ // @ts-ignore
28+ axios.defaults[key] = config[key]
29+ }
30+ }
31+ }
32+ }
33+ 
34+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
35+ try {
36+ return handleData<T>(await axios.get<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config }))
37+ } catch (error) {
38+ const err = error as AxiosError<any>
39+ if (err.response && err.response.data && err.response.data.msg) {
40+ return Promise.reject(err.response.data.msg)
41+ }
42+ return Promise.reject(error)
43+ }
44+ }
45+ 
46+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
47+ try {
48+ return handleData<T>(await axios.delete<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config }))
49+ } catch (error) {
50+ const err = error as AxiosError<any>
51+ if (err.response && err.response.data && err.response.data.msg) {
52+ return Promise.reject(err.response.data.msg)
53+ }
54+ return Promise.reject(error)
55+ }
56+ }
57+ 
58+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
59+ try {
60+ return handleData<T>(await axios.post<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
61+ } catch (error) {
62+ const err = error as AxiosError<any>
63+ if (err.response && err.response.data && err.response.data.msg) {
64+ return Promise.reject(err.response.data.msg)
65+ }
66+ return Promise.reject(error)
67+ }
68+ }
69+ 
70+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
71+ try {
72+ return handleData<T>(await axios.put<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
73+ } catch (error) {
74+ const err = error as AxiosError<any>
75+ if (err.response && err.response.data && err.response.data.msg) {
76+ return Promise.reject(err.response.data.msg)
77+ }
78+ return Promise.reject(error)
79+ }
80+ }
81+ 
82+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
83+ try {
84+ return handleData<T>(await axios.patch<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
85+ } catch (error) {
86+ const err = error as AxiosError<any>
87+ if (err.response && err.response.data && err.response.data.msg) {
88+ return Promise.reject(err.response.data.msg)
89+ }
90+ return Promise.reject(error)
91+ }
92+ }
93+}
94+ 
95+const ogRequest = new Request({
96+ baseURL: import.meta.env.VITE_BASE_URL
97+})
98+ 
99+export default ogRequest
Aplugins/alert-center/web-ui/src/request/index.ts+170-0
@@ -0,0 +1,170 @@
1+import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
2+ 
3+const observabilityBaseURL = process.env.mode === 'production' ? '/plugins/alert-center' : '';
4+// const observabilityBaseURL ='/plugins/observability-instance-zh25';
5+ 
6+interface ApiResponse<T = any> {
7+ code: string | number;
8+ data: T;
9+ error: any;
10+ msg: string;
11+}
12+ 
13+const handleData = <T>(res: AxiosResponse<ApiResponse<T>, any>) => {
14+ if (res.data && (res.data.code === "200" || res.data.code === 200)) {
15+ return Promise.resolve(res.data)
16+ } else {
17+ return Promise.reject(res?.data)
18+ }
19+}
20+ 
21+export class Request {
22+ constructor(config?: AxiosRequestConfig) {
23+ if (config) {
24+ for (const key in config) {
25+ if (key in axios.defaults) {
26+ // @ts-ignore
27+ axios.defaults[key] = config[key]
28+ }
29+ }
30+ }
31+ }
32+ 
33+ async getNative<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
34+ const token = localStorage.getItem("opengauss-token");
35+ if (token) {
36+ if (!config) {
37+ config = {};
38+ }
39+ if (!config.headers) {
40+ config.headers = {};
41+ }
42+ config.headers.Authorization = `Bearer ${token}`;
43+ }
44+ try {
45+ return await axios.get<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config })
46+ } catch (error) {
47+ const err = error as AxiosError<any>
48+ if (err.response && err.response.data && err.response.data.msg) {
49+ return Promise.reject(err.response.data.msg)
50+ }
51+ return Promise.reject(error)
52+ }
53+ }
54+ 
55+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
56+ const token = localStorage.getItem("opengauss-token");
57+ if (token) {
58+ if (!config) {
59+ config = {};
60+ }
61+ if (!config.headers) {
62+ config.headers = {};
63+ }
64+ config.headers.Authorization = `Bearer ${token}`;
65+ }
66+ try {
67+ return handleData<T>(await axios.get<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config }))
68+ } catch (error) {
69+ const err = error as AxiosError<any>
70+ if (err.response && err.response.data && err.response.data.msg) {
71+ return Promise.reject(err.response.data.msg)
72+ }
73+ return Promise.reject(error)
74+ }
75+ }
76+ 
77+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
78+ const token = localStorage.getItem("opengauss-token");
79+ if (token) {
80+ if (!config) {
81+ config = {};
82+ }
83+ if (!config.headers) {
84+ config.headers = {};
85+ }
86+ config.headers.Authorization = `Bearer ${token}`;
87+ }
88+ try {
89+ return handleData<T>(await axios.delete<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config }))
90+ } catch (error) {
91+ const err = error as AxiosError<any>
92+ if (err.response && err.response.data && err.response.data.msg) {
93+ return Promise.reject(err.response.data.msg)
94+ }
95+ return Promise.reject(error)
96+ }
97+ }
98+ 
99+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
100+ const token = localStorage.getItem("opengauss-token");
101+ if (token) {
102+ if (!config) {
103+ config = {};
104+ }
105+ if (!config.headers) {
106+ config.headers = {};
107+ }
108+ config.headers.Authorization = `Bearer ${token}`;
109+ }
110+ try {
111+ return handleData<T>(await axios.post<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
112+ } catch (error) {
113+ const err = error as AxiosError<any>
114+ if (err.response && err.response.data && err.response.data.msg) {
115+ return Promise.reject(err.response.data.msg)
116+ }
117+ return Promise.reject(error)
118+ }
119+ }
120+ 
121+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
122+ const token = localStorage.getItem("opengauss-token");
123+ if (token) {
124+ if (!config) {
125+ config = {};
126+ }
127+ if (!config.headers) {
128+ config.headers = {};
129+ }
130+ config.headers.Authorization = `Bearer ${token}`;
131+ }
132+ try {
133+ return handleData<T>(await axios.put<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
134+ } catch (error) {
135+ const err = error as AxiosError<any>
136+ if (err.response && err.response.data && err.response.data.msg) {
137+ return Promise.reject(err.response.data.msg)
138+ }
139+ return Promise.reject(error)
140+ }
141+ }
142+ 
143+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
144+ const token = localStorage.getItem("opengauss-token");
145+ if (token) {
146+ if (!config) {
147+ config = {};
148+ }
149+ if (!config.headers) {
150+ config.headers = {};
151+ }
152+ config.headers.Authorization = `Bearer ${token}`;
153+ }
154+ try {
155+ return handleData<T>(await axios.patch<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
156+ } catch (error) {
157+ const err = error as AxiosError<any>
158+ if (err.response && err.response.data && err.response.data.msg) {
159+ return Promise.reject(err.response.data.msg)
160+ }
161+ return Promise.reject(error)
162+ }
163+ }
164+}
165+ 
166+const ogRequest = new Request({
167+ baseURL: import.meta.env.VITE_BASE_URL
168+})
169+ 
170+export default ogRequest
Aplugins/alert-center/web-ui/src/request/indexEb.ts+124-0
@@ -0,0 +1,124 @@
1+ 
2+import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
3+import { ElMessage } from "element-plus";
4+ 
5+const observabilityBaseURL = process.env.mode === 'production' ? '/plugins/alert-center' : '';
6+interface ApiResponse<T = any> {
7+ code: string | number;
8+ data: T;
9+ error: any;
10+ msg: string;
11+ config:{
12+ url:string
13+ };
14+}
15+ 
16+const handleData = <T>(res: AxiosResponse<ApiResponse<T>, any>) => {
17+ if (res.status === 200 && res.config.url === '/getOffCpuSvg') {
18+ return Promise.resolve(res)
19+ } else {
20+ if (res.data && (res.data.code === "200" || res.data.code === 200)) {
21+ return Promise.resolve(res.data.data)
22+ } else {
23+ ElMessage.error(res?.data.msg || "请求错误")
24+ return Promise.reject(res?.data.msg || "请求错误")
25+ }
26+ }
27+}
28+ 
29+export class Request {
30+ constructor(config?: AxiosRequestConfig) {
31+ if (config) {
32+ axios.defaults.baseURL = 'http://10.10.9.221:5000'
33+ for (const key in config) {
34+ if (key in axios.defaults) {
35+ // @ts-ignore
36+ axios.defaults[key] = config[key]
37+ }
38+ }
39+ }
40+ }
41+ 
42+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
43+ axios.defaults.baseURL = 'http://10.10.9.221:5000'
44+ try {
45+ return handleData<T>(await axios.get<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config }))
46+ } catch (error) {
47+ const err = error as AxiosError<any>
48+ if (err.response && err.response.status === 500) {
49+ ElMessage.error("服务器错误,请稍后重试")
50+ }
51+ if (err.response && err.response.data && err.response.data.msg) {
52+ return Promise.reject(err.response.data.msg)
53+ }
54+ return Promise.reject(error)
55+ }
56+ }
57+ 
58+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
59+ axios.defaults.baseURL = 'http://10.10.9.221:5000'
60+ try {
61+ return handleData<T>(await axios.delete<ApiResponse<T>>(`${observabilityBaseURL}${url}`, { params, ...config }))
62+ } catch (error) {
63+ const err = error as AxiosError<any>
64+ if (err.response && err.response.data && err.response.data.msg) {
65+ return Promise.reject(err.response.data.msg)
66+ }
67+ return Promise.reject(error)
68+ }
69+ }
70+ 
71+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
72+ axios.defaults.baseURL = 'http://10.10.9.221:5000'
73+ try {
74+ return handleData<T>(await axios.post<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
75+ } catch (error) {
76+ const err = error as AxiosError<any>
77+ if (err.response && err.response.status === 500) {
78+ ElMessage.error("服务器错误,请稍后重试")
79+ }
80+ if (err.response && err.response.data && err.response.data.msg) {
81+ return Promise.reject(err.response.data.msg)
82+ }
83+ return Promise.reject(error)
84+ }
85+ }
86+ 
87+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
88+ axios.defaults.baseURL = 'http://10.10.9.221:5000'
89+ try {
90+ return handleData<T>(await axios.put<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
91+ } catch (error) {
92+ const err = error as AxiosError<any>
93+ if (err.response && err.response.status === 500) {
94+ ElMessage.error("服务器错误,请稍后重试")
95+ }
96+ if (err.response && err.response.data && err.response.data.msg) {
97+ return Promise.reject(err.response.data.msg)
98+ }
99+ return Promise.reject(error)
100+ }
101+ }
102+ 
103+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
104+ axios.defaults.baseURL = 'http://10.10.9.221:5000'
105+ try {
106+ return handleData<T>(await axios.patch<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config))
107+ } catch (error) {
108+ const err = error as AxiosError<any>
109+ if (err.response && err.response.status === 500) {
110+ ElMessage.error("服务器错误,请稍后重试")
111+ }
112+ if (err.response && err.response.data && err.response.data.msg) {
113+ return Promise.reject(err.response.data.msg)
114+ }
115+ return Promise.reject(error)
116+ }
117+ }
118+}
119+ 
120+const ogRequest = new Request({
121+ baseURL: import.meta.env.VITE_BASE_URL
122+})
123+ 
124+export default ogRequest
Aplugins/alert-center/web-ui/src/request/platform.ts+175-0
@@ -0,0 +1,175 @@
1+import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
2+ 
3+const platformBaseURL = "";
4+ 
5+interface ApiResponse<T = any> {
6+ code: string | number;
7+ data: T;
8+ error: any;
9+ msg: string;
10+}
11+ 
12+const handleData = <T>(res: AxiosResponse<ApiResponse<T>, any>) => {
13+ if (res.data && (res.data.code === "200" || res.data.code === 200)) {
14+ return Promise.resolve(res.data.data);
15+ } else {
16+ return Promise.reject(res?.data.msg || "Request Error").catch((err) => {
17+ console.log(err);
18+ });
19+ }
20+};
21+ 
22+const handleRESTfulData = <T>(res: AxiosResponse<T, any>) => {
23+ return Promise.resolve(res.data);
24+};
25+ 
26+export class Request {
27+ constructor(config?: AxiosRequestConfig) {
28+ if (config) {
29+ for (const key in config) {
30+ if (key in axios.defaults) {
31+ // @ts-ignore
32+ axios.defaults[key] = config[key];
33+ }
34+ }
35+ }
36+ }
37+ 
38+ async getNative<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
39+ const token = localStorage.getItem("opengauss-token");
40+ if (token) {
41+ if (!config) {
42+ config = {};
43+ }
44+ if (!config.headers) {
45+ config.headers = {};
46+ }
47+ config.headers.Authorization = `Bearer ${token}`;
48+ }
49+ try {
50+ return await axios.get<ApiResponse<T>>(`${platformBaseURL}${url}`, { params, ...config });
51+ } catch (error) {
52+ const err = error as AxiosError<any>;
53+ if (err.response && err.response.data && err.response.data.msg) {
54+ return Promise.reject(err.response.data.msg);
55+ }
56+ return Promise.reject(error);
57+ }
58+ }
59+ 
60+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
61+ const token = localStorage.getItem("opengauss-token");
62+ if (token) {
63+ if (!config) {
64+ config = {};
65+ }
66+ if (!config.headers) {
67+ config.headers = {};
68+ }
69+ config.headers.Authorization = `Bearer ${token}`;
70+ }
71+ try {
72+ return handleRESTfulData<T>(await axios.get<T>(`${platformBaseURL}${url}`, { params, ...config }));
73+ } catch (error) {
74+ const err = error as AxiosError<any>;
75+ if (err.response && err.response.data && err.response.data.msg) {
76+ return Promise.reject(err.response.data.msg);
77+ }
78+ return Promise.reject(error);
79+ }
80+ }
81+ 
82+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
83+ const token = localStorage.getItem("opengauss-token");
84+ if (token) {
85+ if (!config) {
86+ config = {};
87+ }
88+ if (!config.headers) {
89+ config.headers = {};
90+ }
91+ config.headers.Authorization = `Bearer ${token}`;
92+ }
93+ try {
94+ return handleRESTfulData<T>(await axios.delete<T>(`${platformBaseURL}${url}`, { params, ...config }));
95+ } catch (error) {
96+ const err = error as AxiosError<any>;
97+ if (err.response && err.response.data && err.response.data.msg) {
98+ return Promise.reject(err.response.data.msg);
99+ }
100+ return Promise.reject(error);
101+ }
102+ }
103+ 
104+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
105+ const token = localStorage.getItem("opengauss-token");
106+ if (token) {
107+ if (!config) {
108+ config = {};
109+ }
110+ if (!config.headers) {
111+ config.headers = {};
112+ }
113+ config.headers.Authorization = `Bearer ${token}`;
114+ }
115+ try {
116+ return handleRESTfulData<T>(await axios.post<T>(`${platformBaseURL}${url}`, data, config));
117+ } catch (error) {
118+ const err = error as AxiosError<any>;
119+ if (err.response && err.response.data && err.response.data.msg) {
120+ return Promise.reject(err.response.data.msg);
121+ }
122+ return Promise.reject(error);
123+ }
124+ }
125+ 
126+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
127+ const token = localStorage.getItem("opengauss-token");
128+ if (token) {
129+ if (!config) {
130+ config = {};
131+ }
132+ if (!config.headers) {
133+ config.headers = {};
134+ }
135+ config.headers.Authorization = `Bearer ${token}`;
136+ }
137+ try {
138+ return handleData<T>(await axios.put<ApiResponse<T>>(`${platformBaseURL}${url}`, data, config));
139+ } catch (error) {
140+ const err = error as AxiosError<any>;
141+ if (err.response && err.response.data && err.response.data.msg) {
142+ return Promise.reject(err.response.data.msg);
143+ }
144+ return Promise.reject(error);
145+ }
146+ }
147+ 
148+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
149+ const token = localStorage.getItem("opengauss-token");
150+ if (token) {
151+ if (!config) {
152+ config = {};
153+ }
154+ if (!config.headers) {
155+ config.headers = {};
156+ }
157+ config.headers.Authorization = `Bearer ${token}`;
158+ }
159+ try {
160+ return handleData<T>(await axios.patch<ApiResponse<T>>(`${platformBaseURL}${url}`, data, config));
161+ } catch (error) {
162+ const err = error as AxiosError<any>;
163+ if (err.response && err.response.data && err.response.data.msg) {
164+ return Promise.reject(err.response.data.msg);
165+ }
166+ return Promise.reject(error);
167+ }
168+ }
169+}
170+ 
171+const platformRequest = new Request({
172+ baseURL: import.meta.env.VITE_BASE_URL,
173+});
174+ 
175+export default platformRequest;
Aplugins/alert-center/web-ui/src/request/restful.ts+166-0
@@ -0,0 +1,166 @@
1+import axios, { AxiosError, AxiosRequestConfig } from "axios";
2+import { ElMessage } from "element-plus";
3+ 
4+const observabilityBaseURL = process.env.mode === "production" ? "/plugins/alert-center" : "";
5+ 
6+interface ApiResponse<T = any> {
7+ code: string | number;
8+ data: T;
9+ error: any;
10+ msg: string;
11+}
12+ 
13+const handleRESTfulData = (res: any) => {
14+ if (isSuccessResponse(res)) {
15+ return Promise.resolve(res.data);
16+ } else {
17+ ElMessage.error(res?.data.msg || "Request Error");
18+ return Promise.reject(res?.data?.msg || "Request Error").catch((err) => {
19+ console.log(err);
20+ });
21+ }
22+};
23+ 
24+const isSuccessResponse = (res: any) => {
25+ if (res.status === 200) {
26+ if (res.data === undefined) {
27+ return true;
28+ } else if (Object.keys(res.data).length === 2 && typeof res.data.code !== 'undefined' && typeof res.data.msg !== 'undefined' && res.data.code !== 200) {
29+ return false;
30+ } else return true;
31+ } else return false;
32+};
33+ 
34+export class Request {
35+ constructor(config?: AxiosRequestConfig) {
36+ if (config) {
37+ for (const key in config) {
38+ if (key in axios.defaults) {
39+ // @ts-ignore
40+ axios.defaults[key] = config[key];
41+ }
42+ }
43+ }
44+ }
45+ 
46+ async get<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
47+ const token = localStorage.getItem("opengauss-token");
48+ if (token) {
49+ if (!config) {
50+ config = {};
51+ }
52+ if (!config.headers) {
53+ config.headers = {};
54+ }
55+ config.headers.Authorization = `Bearer ${token}`;
56+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
57+ }
58+ try {
59+ return handleRESTfulData<T>(await axios.get<T>(`${observabilityBaseURL}${url}`, { params, ...config }));
60+ } catch (error) {
61+ const err = error as AxiosError<any>;
62+ if (err.response && err.response.data && err.response.data.msg) {
63+ return Promise.reject(err.response.data.msg);
64+ }
65+ return Promise.reject(error);
66+ }
67+ }
68+ 
69+ async delete<T = any>(url: string, params?: any, config?: AxiosRequestConfig) {
70+ const token = localStorage.getItem("opengauss-token");
71+ if (token) {
72+ if (!config) {
73+ config = {};
74+ }
75+ if (!config.headers) {
76+ config.headers = {};
77+ }
78+ config.headers.Authorization = `Bearer ${token}`;
79+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
80+ }
81+ try {
82+ return handleRESTfulData<T>(await axios.delete<T>(`${observabilityBaseURL}${url}`, { params, ...config }));
83+ } catch (error) {
84+ const err = error as AxiosError<any>;
85+ if (err.response && err.response.data && err.response.data.msg) {
86+ return Promise.reject(err.response.data.msg);
87+ }
88+ return Promise.reject(error);
89+ }
90+ }
91+ 
92+ async post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
93+ const token = localStorage.getItem("opengauss-token");
94+ if (token) {
95+ if (!config) {
96+ config = {};
97+ }
98+ if (!config.headers) {
99+ config.headers = {};
100+ }
101+ config.headers.Authorization = `Bearer ${token}`;
102+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
103+ }
104+ try {
105+ return handleRESTfulData<T>(await axios.post<T>(`${observabilityBaseURL}${url}`, data, config));
106+ } catch (error) {
107+ const err = error as AxiosError<any>;
108+ if (err.response && err.response.data && err.response.data.msg) {
109+ return Promise.reject(err.response.data.msg);
110+ }
111+ return Promise.reject(error);
112+ }
113+ }
114+ 
115+ async put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
116+ const token = localStorage.getItem("opengauss-token");
117+ if (token) {
118+ if (!config) {
119+ config = {};
120+ }
121+ if (!config.headers) {
122+ config.headers = {};
123+ }
124+ config.headers.Authorization = `Bearer ${token}`;
125+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
126+ }
127+ try {
128+ return handleRESTfulData<T>(await axios.put<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config));
129+ } catch (error) {
130+ const err = error as AxiosError<any>;
131+ if (err.response && err.response.data && err.response.data.msg) {
132+ return Promise.reject(err.response.data.msg);
133+ }
134+ return Promise.reject(error);
135+ }
136+ }
137+ 
138+ async patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) {
139+ const token = localStorage.getItem("opengauss-token");
140+ if (token) {
141+ if (!config) {
142+ config = {};
143+ }
144+ if (!config.headers) {
145+ config.headers = {};
146+ }
147+ config.headers.Authorization = `Bearer ${token}`;
148+ config.headers["Content-Language"] = localStorage.getItem("locale") === "en-US" ? "en_US" : "zh_CN";
149+ }
150+ try {
151+ return handleRESTfulData<T>(await axios.patch<ApiResponse<T>>(`${observabilityBaseURL}${url}`, data, config));
152+ } catch (error) {
153+ const err = error as AxiosError<any>;
154+ if (err.response && err.response.data && err.response.data.msg) {
155+ return Promise.reject(err.response.data.msg);
156+ }
157+ return Promise.reject(error);
158+ }
159+ }
160+}
161+ 
162+const restRequest = new Request({
163+ baseURL: import.meta.env.VITE_BASE_URL,
164+});
165+ 
166+export default restRequest;
Aplugins/alert-center/web-ui/src/router/index.ts+100-0
@@ -0,0 +1,100 @@
1+import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
2+ 
3+declare module 'vue-router' {
4+ // eslint-disable-next-line no-unused-vars
5+ interface RouteMeta {
6+ icon?: string;
7+ title: string;
8+ hidden?: boolean;
9+ breadcrumb?: string[];
10+ }
11+}
12+ 
13+export const routes: RouteRecordRaw[] = [
14+ {
15+ path: "/vem",
16+ meta: {
17+ icon: "vite",
18+ title: 'app.menuName',
19+ },
20+ children: [
21+ {
22+ path: "/vem/alert/alertRecord",
23+ meta: {
24+ title: 'alertRecord.title',
25+ },
26+ component: () => import("@/views/alert/AlertRecord/Index.vue"),
27+ },
28+ {
29+ path: "/vem/alert/recordDetail",
30+ meta: {
31+ title: 'alertRecord.title',
32+ hidden: true
33+ },
34+ component: () => import("@/views/alert/AlertRecord/RecordDetail.vue"),
35+ },
36+ {
37+ path: "/vem/alert/AlertClusterNodeConf",
38+ meta: {
39+ title: 'AlertClusterNodeConf.title',
40+ },
41+ component: () => import("@/views/alert/AlertClusterNodeConf/Index.vue"),
42+ },
43+ {
44+ path: "/vem/alert/alertTemplate",
45+ meta: {
46+ title: 'alertTemplate.title',
47+ },
48+ component: () => import("@/views/alert/AlertTemplate/Index.vue"),
49+ },
50+ {
51+ path: "/vem/alert/alertRule",
52+ component: () => import("@/views/alert/AlertRule/Index.vue"),
53+ meta: {
54+ title: 'alertRule.title',
55+ // breadcrumb: ['app.menuName', 'dashboard.name'],
56+ breadcrumb: [],
57+ // hidden: true,
58+ },
59+ },
60+ {
61+ path: "/vem/notify/notifyTemplate",
62+ component: () => import("@/views/notify/NotifyTemplate/Index.vue"),
63+ meta: {
64+ title: 'notifyTemplate.title',
65+ },
66+ },
67+ {
68+ path: "/vem/notify/notifyWay",
69+ component: () => import("@/views/notify/NotifyWay/Index.vue"),
70+ meta: {
71+ title: 'notifyWay.title',
72+ },
73+ },
74+ // {
75+ // path: "/vem/sql_detail",
76+ // meta: {
77+ // icon: "vite",
78+ // title: 'sql.sqlDetail',
79+ // breadcrumb: ['app.menuName', 'dashboard.instance'],
80+ // hidden: true
81+ // },
82+ // name: "SqlDetail",
83+ // component: () => import("@/pages/sql_detail/Index.vue"),
84+ // }
85+ ]
86+ },
87+];
88+ 
89+const router = createRouter({
90+ history: createWebHashHistory(),
91+ routes,
92+});
93+ 
94+router.beforeEach((to, from, next) => {
95+ if (from.path.includes(`/vem/track_detail/`) && to.path.includes(`/vem/log/track`)) {
96+ sessionStorage.removeItem('nodes');
97+ }
98+ next();
99+})
100+export default router;
Aplugins/alert-center/web-ui/src/shared.ts+18-0
@@ -0,0 +1,18 @@
1+export const uuid = () => Math.random().toString(36).slice(2);
2+ 
3+export const toFixed = (n: number | string, r: number = 2) => (Number.parseInt(`${Number.parseFloat(`${n}`) * (10 ** r)}`) / (10 ** r)).toFixed(r);
4+ 
5+export const downloadCsvFile = (filename: string, title: string, data: Record<any, string | number>[]) => {
6+ let str = `${title}\n`;
7+ data.forEach(d => {
8+ for (const k in d) {
9+ str += `${d[k]}\t,`
10+ }
11+ str += `\n`
12+ })
13+ const link = document.createElement("a")
14+ link.href = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str)
15+ link.download = filename
16+ link.click()
17+ link.remove()
18+}
Aplugins/alert-center/web-ui/src/store/alertConfig.ts+27-0
@@ -0,0 +1,27 @@
1+import { defineStore } from "pinia";
2+ 
3+interface State {
4+ showMain: boolean,
5+ isInstall: boolean,
6+ isConfig: boolean
7+ 
8+}
9+export const useAlertConfigStore = defineStore("alertConfig", {
10+ state: (): State => ({
11+ showMain: true,
12+ isInstall: false,
13+ isConfig: false
14+ }),
15+ actions: {
16+ setShow(showMain: boolean) {
17+ this.$state.showMain = showMain;
18+ },
19+ setIsInstall(isInstall: boolean) {
20+ this.$state.isInstall = isInstall;
21+ },
22+ setIsConifg(isConfig: boolean) {
23+ this.$state.isConfig = isConfig;
24+ },
25+
26+ }
27+})
Aplugins/alert-center/web-ui/src/store/monitor.ts+88-0
@@ -0,0 +1,88 @@
1+import { defineStore } from "pinia";
2+ 
3+interface State {
4+ instanceId: string,
5+ databaseData: Record<string, {data: string[], time: string[], name: string}[]>,
6+ /**
7+ * current tab index
8+ */
9+ tab: number,
10+ filters: {
11+ /**
12+ * auto refresh interval
13+ */
14+ refreshTime: number,
15+ /**
16+ * range time
17+ */
18+ rangeTime: number,
19+ /**
20+ * custom range time
21+ */
22+ time: [Date, Date] | null
23+ }[],
24+ autoRefresh: boolean,
25+ instanceTimeRange: [Date, Date] | null,
26+ /**
27+ * brush select
28+ */
29+ brushRange: string[],
30+ timeRange: string[],
31+ /**
32+ * topsql server
33+ */
34+ fixedRangeTime: Array<string>,
35+ serverData: Record<string, {data: string[], time: string[], name: string}[]>,
36+ promethuesStart: number,
37+ promethuesEnd: number,
38+ promethuesStep: number,
39+}
40+export const useMonitorStore = defineStore("monitor", {
41+ state: (): State => ({
42+ instanceId: '',
43+ databaseData: {},
44+ tab: 0,
45+ filters: [
46+ {
47+ refreshTime: 30,
48+ rangeTime: 1,
49+ time: null
50+ },
51+ {
52+ refreshTime: 30,
53+ rangeTime: 1,
54+ time: null
55+ },
56+ {
57+ refreshTime: 30,
58+ rangeTime: 1,
59+ time: null
60+ },
61+ {
62+ refreshTime: 30,
63+ rangeTime: 1,
64+ time: null
65+ },
66+ ],
67+ autoRefresh: false,
68+ instanceTimeRange: null,
69+ brushRange: [],
70+ timeRange: [],
71+ fixedRangeTime: [],
72+ serverData: {},
73+ promethuesStart: 0,
74+ promethuesEnd: 0,
75+ promethuesStep: 60,
76+ }),
77+ getters: {
78+ refreshTime: (state: State) => {
79+ return state.filters[state.tab].refreshTime
80+ },
81+ rangeTime: (state: State) => {
82+ return state.filters[state.tab].rangeTime
83+ },
84+ time: (state: State) => {
85+ return state.filters[state.tab].time
86+ },
87+ },
88+})
Aplugins/alert-center/web-ui/src/store/window.ts+26-0
@@ -0,0 +1,26 @@
1+import { defineStore } from 'pinia';
2+ 
3+export const useWindowStore = defineStore('window', {
4+ state() {
5+ return {
6+ mainHeight: 0,
7+ mainWidth: 0,
8+ serverInfoText: '',
9+ theme: 'auto', // dark or auto
10+ }
11+ },
12+ actions: {
13+ setMainHeight(height: number) {
14+ this.$state.mainHeight = height;
15+ },
16+ setMainWidth(width: number) {
17+ this.$state.mainWidth = width;
18+ },
19+ setServerInfoText(value: string) {
20+ this.$state.serverInfoText = value;
21+ },
22+ setTheme(value: string) {
23+ this.$state.theme = value;
24+ }
25+ }
26+});
Aplugins/alert-center/web-ui/src/utils/commonUtil.ts+31-0
@@ -0,0 +1,31 @@
1+ 
2+ 
3+export function parseContent(content: string, param: any) {
4+ if (!content || !param) {
5+ return content
6+ }
7+ const paramOpen = "${";
8+ const paramClose = "}";
9+ let start = content.indexOf(paramOpen);
10+ if (start === -1) {
11+ return content;
12+ }
13+ let result = ''
14+ let offset = 0
15+ while (start > -1) {
16+ result += content.slice(offset, start)
17+ offset = start + paramOpen.length
18+ let end = content.indexOf(paramClose, offset)
19+ if (end === -1) {
20+ return content
21+ }
22+ let paramName = content.slice(offset, end)
23+ let paramVal = param[paramName] || ''
24+ result += paramVal
25+ offset = end + paramClose.length
26+ start = content.indexOf(paramOpen, offset)
27+ }
28+ result += content.slice(offset, content.length)
29+ 
30+ return result
31+}
Aplugins/alert-center/web-ui/src/utils/jsencrypt.ts+29-0
@@ -0,0 +1,29 @@
1+import JsEncrypt from "jsencrypt";
2+import platformRequest from "../request/platform";
3+ 
4+interface KeyValue {
5+ [key: string]: any;
6+}
7+interface Res {
8+ data: KeyValue;
9+}
10+// host password encryption
11+export async function encryptPassword(pwd: string) {
12+ let publicKey = "";
13+ const getPublicKey: KeyValue = await getEntryKey();
14+ if (Number(getPublicKey.code) === 200 && getPublicKey.key) {
15+ const newKey = getPublicKey.key;
16+ publicKey = newKey;
17+ }
18+ const encryptor = new JsEncrypt();
19+ encryptor.setPublicKey(publicKey);
20+ return encryptor.encrypt(pwd);
21+}
22+const getEntryKey: KeyValue = (data) => {
23+ return platformRequest
24+ .getNative("/encryption/getKey", {})
25+ .then(function (res: Res) {
26+ return res.data;
27+ })
28+ .catch(function (res) {});
29+};
Aplugins/alert-center/web-ui/src/utils/websocket.ts+102-0
@@ -0,0 +1,102 @@
1+export default class WebSocketClass {
2+ // ws = null;
3+ ws: WebSocket;
4+ name = null;
5+ sessionId: null;
6+ instance = null;
7+ callback = null;
8+ connected = false;
9+ setIntervalWesocketPush = null;
10+ static instance: any;
11+ 
12+ static getInstance(name, sessionId) {
13+ if (!this.instance) {
14+ this.instance = new WebSocketClass(name, sessionId);
15+ }
16+ return this.instance;
17+ }
18+ 
19+ constructor(name: string, sessionId, callback?) {
20+ this.name = name;
21+ this.sessionId = sessionId;
22+ this.instance = null;
23+ this.connect(name, sessionId, callback);
24+ }
25+ 
26+ connect(name: string, sessionId, callback?) {
27+ if (!window.WebSocket) {
28+ return console.log("Your browser does not support WebSocket");
29+ }
30+ const baseURL = import.meta.env.DEV ? `${import.meta.env.VITE_WS_BASE_URL}` : `${location.protocol == "http:" ? "ws:" : "wss:"}//${location.host}`;
31+ const url = `${baseURL}/ws/alert-center/${sessionId}`;
32+ // const url = `ws://127.0.0.1:9494/ws/observability-instance/${sessionId}`;
33+ this.ws = new WebSocket(url);
34+ 
35+ this.ws.onopen = () => {
36+ this.connected = true;
37+ // this.sendPing();
38+ };
39+ if (callback) this.callback = callback;
40+ 
41+ this.ws.onclose = () => {
42+ this.connected = false;
43+ clearInterval(this.setIntervalWesocketPush);
44+ };
45+ 
46+ this.ws.onmessage = (msg: any) => {
47+ if (this.callback) {
48+ this.callback.call(this, msg.data);
49+ }
50+ };
51+ 
52+ this.ws.onerror = () => {
53+ if (this.ws.readyState !== 3) {
54+ this.connect(name, sessionId);
55+ }
56+ };
57+ }
58+ 
59+ registerCallBack(callBack) {
60+ this.callback = callBack;
61+ }
62+ 
63+ unRegisterCallBack() {
64+ this.callback = null;
65+ }
66+ 
67+ send(data) {
68+ if (!!this.ws && this.ws.readyState === 3) {
69+ this.ws.close();
70+ this.connect(this.name, this.sessionId);
71+ } else if (this.ws.readyState === 1) {
72+ this.ws.send(JSON.stringify(data));
73+ } else if (this.ws.readyState === 0) {
74+ this.connecting(data);
75+ }
76+ }
77+ 
78+ sendPing(time = 1000 * 20, ping = "ping") {
79+ clearInterval(this.setIntervalWesocketPush);
80+ this.ws.send(ping);
81+ this.setIntervalWesocketPush = setInterval(() => {
82+ this.ws.send(ping);
83+ }, time);
84+ }
85+ 
86+ // When sending data but the connection is not established, it will be processed and wait for retransmission
87+ connecting(message: any) {
88+ setTimeout(() => {
89+ if (this.ws.readyState === 0) {
90+ this.connecting(message);
91+ } else if (this.ws.readyState === 3) {
92+ return;
93+ } else {
94+ this.ws.send(JSON.stringify(message));
95+ }
96+ }, 1000);
97+ }
98+ 
99+ close() {
100+ this.ws.close();
101+ }
102+}
Aplugins/alert-center/web-ui/src/views/alert/AlertClusterNodeConf/Detail.vue+434-0
@@ -0,0 +1,434 @@
1+<template>
2+ <div v-if="showMain">
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('AlertClusterNodeConf.title') }}</el-breadcrumb-item>
5+ <el-breadcrumb-item>{{ t('AlertClusterNodeConf.detailTitle') }}</el-breadcrumb-item>
6+ </el-breadcrumb>
7+ <el-divider />
8+ <el-form label-position="right" label-width="80" size="large">
9+ <el-form-item :label="$t('AlertClusterNodeConf.selectedInstance') + ':'">
10+ <el-tag v-for="(item, index) in clusterNodeList0" :key="item.clusterNodeId" type="info" size="large"
11+ class="mx-1" style="margin-right: 5px;" effect="dark" closable @close="closeTag(index)">{{ item.nodeName
12+ }}</el-tag>
13+ </el-form-item>
14+ </el-form>
15+ <el-tabs v-model="activeName" class="node-tabs" type="border-card">
16+ <el-tab-pane :label="$t('AlertClusterNodeConf.alertTemplateTab')" name="template">
17+ <el-row style="min-height: 350px;">
18+ <!-- style="border-right-style:solid;border-color: #E6E8EB;padding-right: 2px;" -->
19+ <el-col :span="4">
20+ <div>
21+ <el-table size="small" :data="tableDatas" style="width: 100%" ref="templateTable" header-cell-class-name="grid-header" border>
22+ <el-table-column width="50">
23+ <template #default="scope">
24+ <el-radio :label="scope.$index + ''" v-model="templateIndex"
25+ @change="getCurrentRow(scope.row.id)">&nbsp;</el-radio>
26+ </template>
27+ </el-table-column>
28+ <el-table-column prop="templateName" :label="$t('alertTemplate.table[0]')" />
29+ </el-table>
30+ </div>
31+ </el-col>
32+ <el-col :span="20" style="padding-left: 5px;">
33+ <el-descriptions :title="t('alertTemplate.detailTitle')">
34+ 
35+ </el-descriptions>
36+ <div class="template-table">
37+ <el-table size="small" :data="ruleTableDatas" style="width: 100%" header-cell-class-name="grid-header" border>
38+ <el-table-column :label="$t('alertRule.table[0]')" prop="ruleName"
39+ width="140"></el-table-column>
40+ <el-table-column :label="$t('alertRule.table[1]')" prop="ruleType" width="100">
41+ <template #default="scope">
42+ <span>{{ $t(`alertRule.${scope.row.ruleType}`) }}</span>
43+ </template>
44+ </el-table-column>
45+ <el-table-column prop="level" :label="$t('alertRule.table[2]')" width="100">
46+ <template #default="scope">
47+ <span>{{ $t(`alertRule.${scope.row.level}`) }}</span>
48+ </template>
49+ </el-table-column>
50+ <el-table-column prop="ruleExpDesc" :label="$t('alertRule.table[8]')" min-width="200">
51+ <template #default="scope">
52+ <span v-html="scope.row.ruleExpDesc"></span>
53+ </template>
54+ </el-table-column>
55+ <el-table-column prop="ruleExpComb" :label="$t('alertRule.table[9]')">
56+ </el-table-column>
57+ <el-table-column prop="isRepeat" :label="$t('alertRule.table[3]')" width="100">
58+ <template #default="scope">
59+ <span>{{ scope.row.isRepeat === 1 ? $t('alertRule.isRepeat') :
60+ $t('alertRule.isNotRepeat') }}</span>
61+ </template>
62+ </el-table-column>
63+ <el-table-column prop="isSilence" :label="$t('alertRule.table[4]')" width="200">
64+ <template #default="scope">
65+ <span>{{ scope.row.isSilence === 1 ? scope.row.silenceStartTime +
66+ $t('alertRule.to') +
67+ scope.row.silenceEndTime : $t('alertRule.isNotSilence') }}</span>
68+ </template>
69+ </el-table-column>
70+ <el-table-column prop="alertNotify" :label="$t('alertRule.table[5]')" width="120">
71+ <template #default="scope">
72+ <span v-text="showAlertNotify(scope.row.alertNotify)"></span>
73+ </template>
74+ </el-table-column>
75+ <!-- <el-table-column prop="ruleContent" :label="$t('alertRule.table[6]')" /> -->
76+ </el-table>
77+ </div>
78+ </el-col>
79+ </el-row>
80+ </el-tab-pane>
81+ <el-tab-pane :label="$t('AlertClusterNodeConf.alertRuleTab')" name="rule">
82+ <el-row style="min-height: 350px;">
83+ <el-table size="small" :data="ruleTableDataList" ref="ruleTable" style="width: 100%" header-cell-class-name="grid-header" border>
84+ <el-table-column type="selection" width="55" />
85+ <el-table-column :label="$t('alertRule.table[0]')" prop="ruleName" width="140"></el-table-column>
86+ <el-table-column :label="$t('alertRule.table[1]')" prop="ruleType" width="100">
87+ <template #default="scope">
88+ <span>{{ $t(`alertRule.${scope.row.ruleType}`) }}</span>
89+ </template>
90+ </el-table-column>
91+ <el-table-column prop="level" :label="$t('alertRule.table[2]')" width="100">
92+ <template #default="scope">
93+ <span>{{ $t(`alertRule.${scope.row.level}`) }}</span>
94+ </template>
95+ </el-table-column>
96+ <el-table-column prop="ruleExpDesc" :label="$t('alertRule.table[8]')" min-width="200">
97+ <template #default="scope">
98+ <span v-html="scope.row.ruleExpDesc"></span>
99+ </template>
100+ </el-table-column>
101+ <el-table-column prop="ruleExpComb" :label="$t('alertRule.table[9]')">
102+ </el-table-column>
103+ <el-table-column prop="isRepeat" :label="$t('alertRule.table[3]')" width="100">
104+ <template #default="scope">
105+ <span>{{ scope.row.isRepeat === 1 ? $t('alertRule.isRepeat') :
106+ $t('alertRule.isNotRepeat') }}</span>
107+ </template>
108+ </el-table-column>
109+ <el-table-column prop="isSilence" :label="$t('alertRule.table[4]')" width="200">
110+ <template #default="scope">
111+ <span>{{ scope.row.isSilence === 1 ? scope.row.silenceStartTime + $t('alertRule.to') +
112+ scope.row.silenceEndTime : $t('alertRule.isNotSilence') }}</span>
113+ </template>
114+ </el-table-column>
115+ <el-table-column prop="alertNotify" :label="$t('alertRule.table[5]')" width="120">
116+ <template #default="scope">
117+ <span v-text="showAlertNotify(scope.row.alertNotify)"></span>
118+ </template>
119+ </el-table-column>
120+ <el-table-column :label="$t('alertRule.table[7]')" fixed="right">
121+ <template #default="scope">
122+ <el-button link type="primary" size="small" @click.prevent="editRule(scope.row)">
123+ {{ t('app.edit') }}
124+ </el-button>
125+ </template>
126+ </el-table-column>
127+ </el-table>
128+ </el-row>
129+ </el-tab-pane>
130+ </el-tabs>
131+ <el-row style="margin-top: 10px;">
132+ <el-button type="primary" @click="confirm">{{ t('app.confirm') }}</el-button>
133+ <el-button @click="cancel">{{ t('app.cancel') }}</el-button>
134+ </el-row>
135+ 
136+ <el-dialog v-model="visible" :show-close="false" width="30%">
137+ <template #header="{ titleId, titleClass }">
138+ <div>
139+ <h4 :id="titleId" :class="titleClass">
140+ <InfoFilled style="width: 1em; height: 1em; margin-right: 8px" />{{ $t('app.tip') }}
141+ </h4>
142+ </div>
143+ </template>
144+ <div style="margin-bottom: 10px;"
145+ v-text="`${$t('AlertClusterNodeConf.generateTip1') + templateName + $t('AlertClusterNodeConf.generateTip2')}`">
146+ </div>
147+ <el-input v-model="templateName" size="large"></el-input>
148+ <template #footer>
149+ <span>
150+ <el-button @click="visible = false">{{ t('app.cancel') }}</el-button>
151+ <el-button type="primary" @click="saveTemplateAndConfig">{{ t('app.confirm') }}</el-button>
152+ </span>
153+ </template>
154+ </el-dialog>
155+ </div>
156+ <div v-else>
157+ <TemplateRuleDetail :ruleId="editRuleId" :templateRuleId="editTemplateRuleId" :titleList="titleList"
158+ @updateTemplateRuleSuccess="updateTemplateRuleSuccess" @cancelUpdateTemplateRule="cancelUpdateTemplateRule" />
159+ </div>
160+</template>
161+ 
162+<script setup lang='ts'>
163+import { InfoFilled } from "@element-plus/icons-vue";
164+import "element-plus/es/components/message-box/style/index";
165+import { useRequest } from "vue-request";
166+import request from "@/request";
167+import { i18n } from "@/i18n";
168+import { ElMessageBox, ElMessage } from "element-plus";
169+import { useI18n } from "vue-i18n";
170+import { ref, onMounted, nextTick } from 'vue'
171+import TemplateRuleDetail from "@/views/alert/AlertTemplate/TemplateRuleDetail.vue";
172+const { t } = useI18n();
173+ 
174+const props = withDefaults(
175+ defineProps<{
176+ clusterNodeList: any[],
177+ }>(),
178+ {
179+ clusterNodeList: () => [],
180+ }
181+);
182+ 
183+const emit = defineEmits(["updateConfigSuccess", "cancelConfig"]);
184+ 
185+const showMain = ref<boolean>(true)
186+const ruleTable = ref()
187+const templateTable = ref()
188+const activeName = ref<string>('template')
189+const templateIndex = ref<string>()
190+const tableDatas = ref<any[]>([])
191+const ruleTableDatas = ref<any[]>()
192+const ruleTableDataList = ref<any[]>([])
193+const currentId = ref<number>()
194+const selectedData = ref<any>()
195+const visible = ref<boolean>(false)
196+const templateName = ref<string>('')
197+ 
198+const selectedRuleRows = ref<any[]>([])
199+const editTemplateRuleId = ref<number>()
200+const editRuleId = ref<number>()
201+const titleList = ref<string[]>([])
202+const clusterNodeList0 = ref<any[]>([])
203+ 
204+const closeTag = (index: number) => {
205+ if (clusterNodeList0.value.length === 1) {
206+ ElMessage({
207+ message: t('app.keepOneData'),
208+ type: 'warning'
209+ })
210+ return;
211+ }
212+ clusterNodeList0.value.splice(index, 1)
213+}
214+ 
215+const { data: res, run: requestData } = useRequest(
216+ (clusterNodeId) => {
217+ return request.get(`/alertCenter/api/v1/alertClusterNodeConf/clusterNode/${clusterNodeId}`)
218+ }
219+)
220+ 
221+const { data: templateRes, run: requestTemplateData } = useRequest(
222+ () => {
223+ return request.get("/alertCenter/api/v1/alertTemplate/list")
224+ },
225+ { manual: true }
226+);
227+const { data: ruleRes, run: requestRuleData } = useRequest(
228+ () => {
229+ return request.get(`/alertCenter/api/v1/alertTemplate/${currentId.value}/rule/list`)
230+ },
231+ { manual: true }
232+);
233+watch(res, (res: any) => {
234+ if (res && res.code === 200) {
235+ selectedData.value = res.data || {}
236+ if (tableDatas.value.length > 0) {
237+ for (let i = 0; i < tableDatas.value.length; i++) {
238+ let row = tableDatas.value[i]
239+ if (row.id === selectedData.value.templateId) {
240+ templateIndex.value = i + ''
241+ currentId.value = row.id
242+ requestRuleData()
243+ break;
244+ }
245+ }
246+ }
247+ } else {
248+ selectedData.value = {}
249+ }
250+});
251+watch(templateRes, (templateRes: any) => {
252+ if (templateRes && templateRes.code === 200) {
253+ tableDatas.value = templateRes.data || []
254+ if (selectedData.value && selectedData.value.id) {
255+ for (let i = 0; i < tableDatas.value.length; i++) {
256+ let row = tableDatas.value[i]
257+ if (row.id === selectedData.value.templateId) {
258+ templateIndex.value = i + ''
259+ currentId.value = row.id
260+ requestRuleData()
261+ break;
262+ }
263+ }
264+ }
265+ } else {
266+ tableDatas.value = []
267+ ruleTableDatas.value = []
268+ }
269+});
270+watch(ruleRes, (ruleRes: any) => {
271+ if (ruleRes && ruleRes.code === 200) {
272+ ruleTableDatas.value = ruleRes.data || []
273+ } else {
274+ ruleTableDatas.value = []
275+ }
276+}, { deep: true });
277+ 
278+const getCurrentRow = (id: number) => {
279+ if (id) {
280+ currentId.value = id
281+ requestRuleData()
282+ }
283+}
284+ 
285+const showAlertNotify = (val: string) => {
286+ if (!val) {
287+ return ''
288+ }
289+ let arr = val.split(',');
290+ let result = arr.map(item => t(`alertRule.${item}`)).join(',')
291+ return result
292+}
293+ 
294+const { data: ruleListRes, run: requestRuleList } = useRequest(
295+ () => {
296+ return request.get(`/alertCenter/api/v1/alertRule/ruleList`)
297+ },
298+ { manual: true }
299+);
300+watch(ruleListRes, (ruleListRes: any) => {
301+ if (ruleListRes && ruleListRes.code === 200) {
302+ ruleTableDataList.value = ruleListRes.data
303+ } else {
304+ ruleTableDataList.value = []
305+ }
306+});
307+ 
308+const confirm = () => {
309+ if (activeName.value === 'template') {
310+ if (!currentId.value) {
311+ return
312+ }
313+ if (clusterNodeList0.value.length === 0) {
314+ ElMessage({
315+ message: t('app.selecteDataTip'),
316+ type: 'error'
317+ })
318+ return
319+ }
320+ let clusterNodeIds = clusterNodeList0.value.map(item => item.clusterNodeId).join(',')
321+ let param = {
322+ clusterNodeIds,
323+ templateId: currentId.value
324+ }
325+ request.post(`/alertCenter/api/v1/alertClusterNodeConf`, param).then((res: any) => {
326+ if (res && res.code === 200) {
327+ ElMessage({
328+ message: t('app.saveSuccess'),
329+ type: 'success'
330+ })
331+ emit("updateConfigSuccess")
332+ }
333+ })
334+ } else if (activeName.value === 'rule') {
335+ const rows = ruleTable.value.getSelectionRows() || []
336+ if (rows.length === 0) {
337+ ElMessage({
338+ message: t('app.selectDatasTip'),
339+ type: 'error'
340+ })
341+ return
342+ }
343+ visible.value = true
344+ templateName.value = t('alertTemplate.title') + rand()
345+ }
346+}
347+const cancel = () => {
348+ emit("cancelConfig")
349+}
350+ 
351+const editRule = (ruleRow: any) => {
352+ selectedRuleRows.value = ruleTable.value.getSelectionRows() || []
353+ // let selectedRuleIdArr = selectedRuleRows.value.map((item: any) => item.ruleId) || []
354+ // if (!selectedRuleIdArr.includes(ruleRow.ruleId)) {
355+ // ElMessage({
356+ // message: t('app.selectUpdateDataTip'),
357+ // type: 'warning'
358+ // })
359+ // return;
360+ // }
361+ editTemplateRuleId.value = ruleRow.templateRuleId
362+ editRuleId.value = ruleRow.ruleId
363+ titleList.value = [t('AlertClusterNodeConf.title'), t('AlertClusterNodeConf.detailTitle'), t('alertRule.editTitle')]
364+ showMain.value = false
365+}
366+const updateTemplateRuleSuccess = (templateRule: any) => {
367+ for (let i = 0; i < ruleTableDataList.value.length; i++) {
368+ if (ruleTableDataList.value[i].ruleId === templateRule.ruleId) {
369+ ruleTableDataList.value[i] = templateRule
370+ }
371+ }
372+ showMain.value = true
373+ nextTick(() => {
374+ for (let row of selectedRuleRows.value) {
375+ ruleTable.value.toggleRowSelection(row, true)
376+ }
377+ })
378+}
379+ 
380+const cancelUpdateTemplateRule = () => {
381+ showMain.value = true
382+ nextTick(() => {
383+ for (let row of selectedRuleRows.value) {
384+ ruleTable.value.toggleRowSelection(row, true)
385+ }
386+ })
387+}
388+ 
389+const saveTemplateAndConfig = () => {
390+ const rows = ruleTable.value.getSelectionRows()
391+ let templateRuleReqList = rows.map((item: any) => {
392+ return { templateRuleId: item.templateRuleId, ruleId: item.ruleId }
393+ })
394+ let clusterNodeIds = clusterNodeList0.value.map(item => item.clusterNodeId).join(',')
395+ let param = {
396+ templateName: templateName.value,
397+ templateRuleReqList,
398+ clusterNodeIds,
399+ }
400+ request.post(`/alertCenter/api/v1/alertClusterNodeConf/alertTemplate`, param).then((res: any) => {
401+ if (res && res.code === 200) {
402+ ElMessage({
403+ message: t('app.saveSuccess'),
404+ type: 'success'
405+ })
406+ visible.value = false
407+ activeName.value = 'template'
408+ emit("updateConfigSuccess")
409+ }
410+ })
411+}
412+ 
413+const rand = () => {
414+ return Math.floor(Math.random() * (9999 - 1000)) + 1000
415+}
416+ 
417+onMounted(() => {
418+ if (props.clusterNodeList && props.clusterNodeList.length === 1) {
419+ requestData(props.clusterNodeList[0].clusterNodeId)
420+ }
421+ clusterNodeList0.value = props.clusterNodeList
422+ requestTemplateData()
423+ requestRuleList()
424+})
425+ 
426+</script>
427+<style scoped lang='scss'>
428+.node-tabs>.el-tabs__content {
429+ padding: 32px;
430+ color: #6b778c;
431+ font-size: 32px;
432+ font-weight: 600;
433+}
434+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertClusterNodeConf/Index.vue+150-0
@@ -0,0 +1,150 @@
1+<template>
2+ <div v-if="showMain">
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('AlertClusterNodeConf.title') }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <div class="search-form">
8+ <div class="filter">
9+ <el-button type="primary" @click="addClusterNodeConf">{{ $t('AlertClusterNodeConf.confBtn') }}</el-button>
10+ <!-- <el-button type="primary" @click="setAlertConfig">{{ $t('AlertClusterNodeConf.setInterface') }}</el-button> -->
11+ </div>
12+ <div class="seperator"></div>
13+ <div class="search-form">
14+ <div class="filter">
15+ <el-input v-model="formData.nodeName" style="width: 200px"
16+ :placeholder="$t('AlertClusterNodeConf.nodeNamePlaceholder')">
17+ <template #suffix>
18+ <el-button :icon="Search" @click="search" />
19+ </template>
20+ </el-input>
21+ </div>
22+ </div>
23+ </div>
24+ <div>
25+ <el-table size="small" :data="tableDatas" ref="table" style="width: 100%" header-cell-class-name="grid-header" border>
26+ <el-table-column type="selection" width="55" />
27+ <el-table-column prop="nodeName" :label="$t('AlertClusterNodeConf.table[0]')" />
28+ <!-- <el-table-column prop="dbType" :label="$t('AlertClusterNodeConf.table[1]')" />
29+ <el-table-column prop="hostIp" :label="$t('AlertClusterNodeConf.table[2]')" /> -->
30+ <el-table-column prop="templateName" :label="$t('AlertClusterNodeConf.table[3]')" />
31+ <el-table-column :label="$t('AlertClusterNodeConf.table[4]')">
32+ <template #default="scope">
33+ <el-button link type="primary" size="small" @click="editClusterNodeConf(scope.row)">{{
34+ $t('AlertClusterNodeConf.confBtn') }}</el-button>
35+ </template>
36+ </el-table-column>
37+ </el-table>
38+ <!-- <div class="pagination">
39+ <el-pagination v-model:currentPage="page.currentPage" v-model:pageSize="page.pageSize" :total="page.total"
40+ :page-sizes="[10, 20, 30, 40]" layout="total,sizes,prev,pager,next" background small
41+ @size-change="handleSizeChange" @current-change="handleCurrentChange" />
42+ </div> -->
43+ </div>
44+ </div>
45+ <div v-else>
46+ <Detail :clusterNodeList="selectedDatas" @updateConfigSuccess="updateConfigSuccess" @cancelConfig="cancelConfig" />
47+ </div>
48+</template>
49+ 
50+<script setup lang='ts'>
51+import { Search } from "@element-plus/icons-vue";
52+import "element-plus/es/components/message-box/style/index";
53+import { useRequest } from "vue-request";
54+import request from "@/request";
55+import { i18n } from "@/i18n";
56+import { ElMessageBox, ElMessage } from "element-plus";
57+import { useI18n } from "vue-i18n";
58+import { ref, onMounted } from 'vue'
59+import Detail from "@/views/alert/AlertClusterNodeConf/Detail.vue"
60+import { cloneDeep } from "lodash";
61+const { t } = useI18n();
62+ 
63+ 
64+const showMain = ref<boolean>(true)
65+ 
66+const formData = ref<any>({
67+ nodeName: ''
68+})
69+const table = ref();
70+const srcTableDatas = ref<any[]>([])
71+const tableDatas = ref<any[]>([])
72+// const page = reactive({
73+// currentPage: 1,
74+// pageSize: 10,
75+// total: 0,
76+// })
77+const selectedDatas = ref<any[]>([])
78+ 
79+ 
80+const addClusterNodeConf = () => {
81+ const rows = table.value.getSelectionRows()
82+ if (rows.length === 0) {
83+ ElMessage({
84+ message: t('alertRecord.tableDataSelectTip'),
85+ type: 'warning',
86+ })
87+ } else {
88+ selectedDatas.value = rows;
89+ showMain.value = false
90+ }
91+}
92+const editClusterNodeConf = (row: any) => {
93+ selectedDatas.value = []
94+ selectedDatas.value.push(row)
95+ showMain.value = false
96+}
97+ 
98+const updateConfigSuccess = () => {
99+ showMain.value = true
100+ // page.pageSize = 10
101+ // page.currentPage = 1
102+ requestData()
103+}
104+const cancelConfig = () => {
105+ showMain.value = true
106+}
107+const { data: res, run: requestData } = useRequest(
108+ () => {
109+ let params = {
110+ }
111+ return request.get("/alertCenter/api/v1/alertClusterNodeConf", params)
112+ },
113+ { manual: true }
114+)
115+watch(res, (res: any) => {
116+ if (res && res.code === 200) {
117+ // tableDatas.value = res.rows || []
118+ // page.total = res.total
119+ tableDatas.value = srcTableDatas.value = res.data || []
120+ } else {
121+ // tableDatas.value = []
122+ // page.total = 0
123+ srcTableDatas.value = []
124+ tableDatas.value = []
125+ }
126+})
127+ 
128+const search = () => {
129+ if (!formData.value.nodeName) {
130+ tableDatas.value = cloneDeep(srcTableDatas.value)
131+ } else {
132+ tableDatas.value = srcTableDatas.value.filter(item => item.nodeName.indexOf(formData.value.nodeName) !== -1)
133+ }
134+}
135+ 
136+// const handleSizeChange = (val: any) => {
137+// page.pageSize = val
138+// requestData()
139+// }
140+// const handleCurrentChange = (val: any) => {
141+// page.currentPage = val
142+// requestData()
143+// }
144+ 
145+onMounted(() => {
146+ requestData()
147+})
148+</script>
149+<style scoped lang='scss'>
150+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertRecord/Index.vue+433-0
@@ -0,0 +1,433 @@
1+<template>
2+ <div>
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('alertRecord.title') }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <div class="search-form">
8+ <div class="filter">
9+ <el-icon color="#F56C6C">
10+ <Bell />
11+ </el-icon>
12+ <span>{{ $t(`alertRecord.alertTotal`) }}:</span><span>{{ statisticsData.totalNum }}</span>
13+ <span style="margin-left: 20px;">{{ $t(`alertRecord.unread`) }}:</span><span>{{ statisticsData.unReadNum
14+ }}</span>
15+ <span style="margin-left: 20px;">{{ $t(`alertRecord.read`) }}:</span><span>{{ statisticsData.readNum
16+ }}</span>
17+ </div>
18+ <div class="seperator"></div>
19+ <div class="filter">
20+ <span>{{ $t(`alertRecord.cluster`) }}:&nbsp;</span>
21+ <el-cascader v-model="formData.clusterNodeId" :options="clusterList" @change="changeClusterNode" clearable/>
22+ </div>
23+ <div class="filter">
24+ <span class="demonstration">{{ $t(`alertRecord.alertTimeRange`) }}:&nbsp;</span>
25+ <el-date-picker v-model="alertTimeRange" type="datetimerange" range-separator="~"
26+ format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
27+ :start-placeholder="t(`alertRecord.startTimePlaceholder`)"
28+ :end-placeholder="t(`alertRecord.endTimePlaceholder`)" @change="changeAlertTimeRange" />
29+ </div>
30+ </div>
31+ <el-row style="width: 100%;">
32+ <el-col :span="8">
33+ <RecordPid :datas="recordStatusData" :title="$t('alertRecord.recordStatusTip')"></RecordPid>
34+ </el-col>
35+ <el-col :span="8">
36+ <RecordPid :datas="alertStatusData" :title="$t('alertRecord.alertStatusTip')"></RecordPid>
37+ </el-col>
38+ <el-col :span="8">
39+ <RecordPid :datas="levelData" :title="$t('alertRecord.levelTip')"></RecordPid>
40+ </el-col>
41+ </el-row>
42+ <div class="record-table">
43+ <div class="search-form" style="margin-bottom: 5px !important;">
44+ <div class="filter">
45+ <el-button @click="markAsUnread">{{ t('alertRecord.markAsUnread') }}</el-button>
46+ <el-button @click="markAsRead">{{ t('alertRecord.markAsRead') }}</el-button>
47+ </div>
48+ <div class="seperator"></div>
49+ <div class="filter">
50+ <span>{{ $t('alertRecord.recordStatus') }}:&nbsp;</span>
51+ <el-checkbox-group v-model="formData.recordStatus" @change="changeRecordStatus">
52+ <el-checkbox label="0">{{ $t(`alertRecord.unread`) }}</el-checkbox>
53+ <el-checkbox label="1">{{ $t(`alertRecord.read`) }}</el-checkbox>
54+ </el-checkbox-group>
55+ <el-divider direction="vertical" />
56+ </div>
57+ <div class="filter">
58+ <span>{{ $t('alertRecord.alertStatus') }}:&nbsp;</span>
59+ <el-checkbox-group v-model="formData.alertStatus" @change="changeAlertStatus">
60+ <el-checkbox label="0">{{ $t(`alertRecord.alerting`) }}</el-checkbox>
61+ <el-checkbox label="1">{{ $t(`alertRecord.alerted`) }}</el-checkbox>
62+ </el-checkbox-group>
63+ <el-divider direction="vertical" />
64+ </div>
65+ <div class="filter">
66+ <span>{{ $t('alertRecord.level') }}:&nbsp;</span>
67+ <el-checkbox-group v-model="formData.alertLevel" @change="changeLevel">
68+ <el-checkbox label="serious">{{ $t(`alertRule.serious`) }}</el-checkbox>
69+ <el-checkbox label="warn">{{ $t(`alertRule.warn`) }}</el-checkbox>
70+ <el-checkbox label="info">{{ $t(`alertRule.info`) }}</el-checkbox>
71+ </el-checkbox-group>
72+ </div>
73+ </div>
74+ <el-table size="small" :data="tableDatas" style="width: 100%" ref="recordTable"
75+ header-cell-class-name="grid-header" border>
76+ <el-table-column type="selection" width="55" />
77+ <el-table-column prop="clusterNodeName" min-width="150" :label="$t('alertRecord.table[0]')" />
78+ <!-- <el-table-column prop="hostIp" :label="$t('alertRecord.table[1]')" />
79+ <el-table-column prop="dbType" :label="$t('alertRecord.table[2]')" /> -->
80+ <el-table-column prop="templateName" :label="$t('alertRecord.table[3]')" />
81+ <el-table-column prop="templateRuleName" :label="$t('alertRecord.table[4]')" />
82+ <el-table-column prop="templateRuleType" :label="$t('alertRecord.table[5]')">
83+ <template #default="scope">
84+ <span>{{ $t(`alertRule.${scope.row.templateRuleType}`) }}</span>
85+ </template>
86+ </el-table-column>
87+ <el-table-column prop="level" :label="$t('alertRecord.table[6]')">
88+ <template #default="scope">
89+ <svg-icon name="circle1" style="margin-right: 2px;" v-if="scope.row.level === 'info'" />
90+ <svg-icon name="circle2" style="margin-right: 2px;" v-if="scope.row.level === 'warn'" />
91+ <svg-icon name="circle3" style="margin-right: 2px;" v-if="scope.row.level === 'serious'" />
92+ <span>{{ $t(`alertRule.${scope.row.level}`) }}</span>
93+ </template>
94+ </el-table-column>
95+ <el-table-column prop="startTime" :label="$t('alertRecord.table[7]')" />
96+ <el-table-column prop="endTime" :label="$t('alertRecord.table[8]')" />
97+ <el-table-column prop="duration" :label="$t('alertRecord.table[9]')">
98+ <template #default="scope">
99+ <span>{{ durationFormat(scope.row.duration) }}</span>
100+ </template>
101+ </el-table-column>
102+ <el-table-column prop="alertStatus" :label="$t('alertRecord.table[10]')">
103+ <template #default="scope">
104+ <span v-if="scope.row.alertStatus === 0"><svg-icon name="circle3" style="margin-right: 2px;" />{{
105+ $t(`alertRecord.alerting`) }}</span>
106+ <span v-if="scope.row.alertStatus === 1"><svg-icon name="circle1" style="margin-right: 2px;" />{{
107+ $t(`alertRecord.alerted`) }}</span>
108+ </template>
109+ </el-table-column>
110+ <el-table-column prop="notifyWayName" :label="$t('alertRecord.table[11]')" />
111+ <el-table-column prop="recordStatus" :label="$t('alertRecord.table[12]')">
112+ <template #default="scope">
113+ <span v-if="scope.row.recordStatus === 0">{{ $t(`alertRecord.unread`) }}</span>
114+ <span v-if="scope.row.recordStatus === 1">{{ $t(`alertRecord.read`) }}</span>
115+ </template>
116+ </el-table-column>
117+ <el-table-column :label="$t('alertRecord.table[13]')" fixed="right">
118+ <template #default="scope">
119+ <el-button link type="primary" size="small" @click="showDetail(scope.row.id)">{{ $t('app.view')
120+ }}</el-button>
121+ </template>
122+ </el-table-column>
123+ </el-table>
124+ <div class="pagination">
125+ <el-pagination v-model:currentPage="page.currentPage" v-model:pageSize="page.pageSize" :total="page.total"
126+ :page-sizes="[10, 20, 30, 40]" layout="total,sizes,prev,pager,next" background small
127+ @size-change="handleSizeChange" @current-change="handleCurrentChange" />
128+ </div>
129+ </div>
130+ 
131+ </div>
132+</template>
133+ 
134+<script setup lang='ts'>
135+import { Bell } from "@element-plus/icons-vue";
136+import "element-plus/es/components/message-box/style/index";
137+import { useRequest } from "vue-request";
138+import request from "@/request";
139+import { i18n } from "@/i18n";
140+import { ElMessageBox, ElMessage } from "element-plus";
141+import { useI18n } from "vue-i18n";
142+import RecordPid from "@/views/alert/AlertRecord/components/RecordPie.vue"
143+const { t } = useI18n();
144+ 
145+const recordTable = ref()
146+const formData = ref<any>({
147+ clusterNodeId: [],
148+ startTime: '',
149+ endTime: '',
150+ recordStatus: [],
151+ alertStatus: [],
152+ alertLevel: []
153+})
154+const clusterList = ref<Array<any>[]>([])
155+const alertTimeRange = ref<Array<any>[]>([])
156+const statisticsData = ref<any>({})
157+const recordStatusData = ref<any[]>([])
158+const alertStatusData = ref<any[]>([])
159+const levelData = ref<any[]>([])
160+const tableDatas = ref<any[]>([])
161+const page = reactive({
162+ currentPage: 1,
163+ pageSize: 10,
164+ total: 0,
165+})
166+const { data: statisticsRes, run: requestStatisticsData } = useRequest(
167+ () => {
168+ return request.get("/alertCenter/api/v1/alertRecord/statistics")
169+ },
170+ { manual: true }
171+);
172+watch(statisticsRes, (statisticsRes: any) => {
173+ if (statisticsRes && statisticsRes.code === 200) {
174+ statisticsData.value = statisticsRes.data
175+ }
176+});
177+const { data: pieRes, run: requestPisData } = useRequest(
178+ () => {
179+ let param = {
180+ clusterNodeId: formData.value.clusterNodeId && formData.value.clusterNodeId.length > 1 ? formData.value.clusterNodeId[1] : '',
181+ startTime: formData.value.startTime,
182+ endTime: formData.value.endTime
183+ }
184+ return request.get("/alertCenter/api/v1/alertRecord/statistics", param)
185+ },
186+ { manual: true }
187+);
188+watch(pieRes, (pieRes: any) => {
189+ if (pieRes && pieRes.code === 200) {
190+ let data = pieRes.data
191+ recordStatusData.value = [{
192+ name: t(`alertRecord.unread`),
193+ value: data.unReadNum,
194+ itemStyle: {
195+ color: '#3DC7FF'
196+ }
197+ }, {
198+ name: t(`alertRecord.read`),
199+ value: data.readNum,
200+ itemStyle: {
201+ color: '#246CFF'
202+ }
203+ }]
204+ alertStatusData.value = [{
205+ name: t(`alertRecord.alerting`),
206+ value: data.firingNum,
207+ itemStyle: {
208+ color: '#246CFF'
209+ }
210+ }, {
211+ name: t(`alertRecord.alerted`),
212+ value: data.recoverNum,
213+ itemStyle: {
214+ color: '#3DC7FF'
215+ }
216+ }]
217+ levelData.value = [{
218+ name: t(`alertRule.serious`),
219+ value: data.seriousNum,
220+ itemStyle: {
221+ color: '#F53F3F'
222+ }
223+ }, {
224+ name: t(`alertRule.warn`),
225+ value: data.warnNum,
226+ itemStyle: {
227+ color: '#FF7D00'
228+ }
229+ }, {
230+ name: t(`alertRule.info`),
231+ value: data.infoNum,
232+ itemStyle: {
233+ color: '#246CFF'
234+ }
235+ }]
236+ }
237+});
238+const { data: res, run: requestData } = useRequest(
239+ () => {
240+ let params = {
241+ clusterNodeId: formData.value.clusterNodeId && formData.value.clusterNodeId.length > 1 ? formData.value.clusterNodeId[1] : '',
242+ startTime: formData.value.startTime,
243+ endTime: formData.value.endTime,
244+ recordStatus: formData.value.recordStatus && formData.value.recordStatus.length > 0 ? formData.value.recordStatus.join(',') : '',
245+ alertStatus: formData.value.alertStatus && formData.value.alertStatus.length > 0 ? formData.value.alertStatus.join(',') : '',
246+ alertLevel: formData.value.alertLevel && formData.value.alertLevel.length > 0 ? formData.value.alertLevel.join(',') : ''
247+ }
248+ params = Object.assign(params, { pageNum: page.currentPage, pageSize: page.pageSize })
249+ return request.get("/alertCenter/api/v1/alertRecord", params)
250+ },
251+ { manual: true }
252+);
253+watch(res, (res: any) => {
254+ if (res && res.code === 200) {
255+ tableDatas.value = res.rows
256+ page.total = res.total
257+ }
258+});
259+const durationFormat = (val: any) => {
260+ if (typeof val === 'number') {
261+ if (val <= 0) {
262+ return '00:00:00';
263+ } else {
264+ let hh = parseInt(val / 3600);
265+ let shh = val - hh * 3600;
266+ let ii = parseInt(shh / 60);
267+ let ss = shh - ii * 60;
268+ return (hh < 10 ? '0' + hh : hh) + ':' + (ii < 10 ? '0' + ii : ii) + ':' + (ss < 10 ? '0' + ss : ss);
269+ }
270+ } else {
271+ return '00:00:00';
272+ }
273+}
274+const { data: opsClusterData } = useRequest(() => request.get("/alertCenter/api/v1/environment/cluster"), { manual: false });
275+const treeTransform = (arr: any) => {
276+ let obj: any = [];
277+ if (arr instanceof Array) {
278+ arr.forEach((item) => {
279+ obj.push({
280+ label: item.clusterId ? item.clusterId : (item.azName ? item.azName + "_" : "") + item.publicIp + ":" + item.dbPort + (item.clusterRole ? "(" + item.clusterRole + ")" : ""),
281+ value: item.clusterId ? item.clusterId : item.nodeId,
282+ children: treeTransform(item.clusterNodes),
283+ });
284+ });
285+ }
286+ return obj;
287+};
288+watch(opsClusterData, (res: any) => {
289+ if (res && res.code === 200) {
290+ clusterList.value = treeTransform(res.data);
291+ }
292+});
293+ 
294+const changeAlertTimeRange = () => {
295+ if (alertTimeRange.value && alertTimeRange.value.length === 2) {
296+ formData.value.startTime = alertTimeRange.value[0]
297+ formData.value.endTime = alertTimeRange.value[1]
298+ } else {
299+ formData.value.startTime = ''
300+ formData.value.endTime = ''
301+ }
302+ requestPisData()
303+ requestData()
304+}
305+ 
306+const changeClusterNode = () => {
307+ requestPisData()
308+ requestData()
309+}
310+ 
311+const changeRecordStatus = () => {
312+ requestData()
313+}
314+const changeAlertStatus = () => {
315+ requestData()
316+}
317+const changeLevel = () => {
318+ requestData()
319+}
320+const markAsUnread = () => {
321+ const rows = recordTable.value.getSelectionRows();
322+ if (rows.length === 0) {
323+ ElMessage({
324+ message: t('alertRecord.tableDataSelectTip'),
325+ type: 'warning',
326+ })
327+ } else {
328+ const ids = rows.map((item: any) => item.id).join(',')
329+ request.post(`/alertCenter/api/v1/alertRecord/markAsUnread?ids=${ids}`).then((res: any) => {
330+ if (res && res.code === 200) {
331+ requestStatisticsData()
332+ requestPisData()
333+ requestData()
334+ }
335+ })
336+ }
337+}
338+const markAsRead = () => {
339+ const rows = recordTable.value.getSelectionRows();
340+ if (rows.length === 0) {
341+ ElMessage({
342+ message: t('alertRecord.tableDataSelectTip'),
343+ type: 'warning',
344+ })
345+ } else {
346+ const ids = rows.map((item: any) => item.id).join(',')
347+ request.post(`/alertCenter/api/v1/alertRecord/markAsRead?ids=${ids}`).then((res: any) => {
348+ if (res && res.code === 200) {
349+ requestStatisticsData()
350+ requestPisData()
351+ requestData()
352+ }
353+ })
354+ }
355+}
356+const router = useRouter();
357+ 
358+const handleSizeChange = (val: any) => {
359+ page.currentPage = val
360+ requestData()
361+}
362+const handleCurrentChange = (val: any) => {
363+ page.pageSize = val
364+ requestData()
365+}
366+ 
367+const showDetail = (id: number) => {
368+ if (process.env.mode === 'production') {
369+ window.$wujie?.props.methods.jump({
370+ name: `Static-pluginAlert-centerVemAlertRecordDetail`,
371+ query: {
372+ id,
373+ },
374+ });
375+ } else {
376+ router.push({
377+ path: `/vem/alert/recordDetail`,
378+ query: {
379+ id,
380+ },
381+ });
382+ }
383+}
384+ 
385+onMounted(() => {
386+ requestStatisticsData()
387+ requestPisData()
388+ requestData()
389+ 
390+ const wujie = window.$wujie;
391+ // Judge whether it is a plug-in environment or a local environment through wujie
392+ if (wujie) {
393+ // Monitoring platform language change
394+ wujie?.bus.$on('opengauss-locale-change', (val: string) => {
395+ console.log('log-search catch locale change');
396+ nextTick(() => {
397+ requestPisData()
398+ });
399+ });
400+ // wujie?.bus.$on('opengauss-theme-change', (val: string) => {
401+ // nextTick(() => {
402+ // requestPisData()
403+ // });
404+ // });
405+ }
406+})
407+</script>
408+<style scoped lang='scss'>
409+@use 'sass:math';
410+@use '@/assets/style/color.scss' as *;
411+@use 'sass:map';
412+ 
413+.record-table {
414+ margin-top: 17px;
415+ 
416+ // border: 1px solid #D9D9D9
417+ .pagination {
418+ margin-top: 5px;
419+ }
420+ 
421+ .search-form {
422+ .el-button {
423+ font-size: 14px !important;
424+ padding: 8px 16px !important;
425+ border: 1px solid var(--button-1-border) !important;
426+ border-radius: 2px;
427+ font-weight: 400 !important;
428+ color: var(--button-1-color) !important;
429+ background-color: var(--button-1-background) !important;
430+ }
431+ }
432+}
433+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertRecord/RecordDetail.vue+223-0
@@ -0,0 +1,223 @@
1+<template>
2+ <div>
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('alertRecord.title') }}</el-breadcrumb-item>
5+ <el-breadcrumb-item>{{ t('alertRecord.detailTitle') }}</el-breadcrumb-item>
6+ </el-breadcrumb>
7+ <el-divider />
8+ <el-row>
9+ <el-col :span="8">
10+ <div class="record-detail">
11+ <el-space wrap>
12+ <el-icon size="18" color="red">
13+ <Bell />
14+ </el-icon>
15+ <h5 class="title">{{ t('alertRecord.alertInstance') }}</h5>
16+ </el-space>
17+ <el-form label-position="right" label-width="120">
18+ <el-form-item :label="$t('alertRecord.clusterName') + ':'" style="margin-bottom: 5px !important">
19+ <span>{{ formData.clusterId }}</span>
20+ </el-form-item>
21+ <el-form-item :label="$t('alertRecord.IPAndPort') + ':'" style="margin-bottom: 5px !important">
22+ <span>{{ formData.hostIpAndPort }}</span>
23+ </el-form-item>
24+ <el-form-item :label="$t('alertRecord.nodeRole') + ':'" style="margin-bottom: 5px !important">
25+ <span>{{ formData.nodeRole }}</span>
26+ </el-form-item>
27+ </el-form>
28+ </div>
29+ </el-col>
30+ <el-col :span="8">
31+ <div class="record-detail">
32+ <el-space wrap>
33+ <el-icon size="18" color="red">
34+ <Bell />
35+ </el-icon>
36+ <h5 class="title">{{ t('alertRecord.alertRule') }}</h5>
37+ </el-space>
38+ <el-form label-position="right" label-width="120">
39+ <el-form-item :label="$t('alertRule.ruleName') + ':'" style="margin-bottom: 5px !important">
40+ <span>{{ formData.templateRuleName }}</span>
41+ </el-form-item>
42+ <el-form-item :label="$t('alertRecord.table[6]') + ':'" style="margin-bottom: 5px !important">
43+ <span>{{ $t(`alertRule.${formData.level}`) }}</span>
44+ </el-form-item>
45+ <el-form-item :label="$t('alertRecord.table[3]') + ':'" style="margin-bottom: 5px !important">
46+ <span>{{ formData.templateName }}</span>
47+ </el-form-item>
48+ <el-form-item :label="$t('alertRecord.table[11]') + ':'" style="margin-bottom: 5px !important">
49+ <span>{{ formData.notifyWayName }}</span>
50+ </el-form-item>
51+ <el-form-item :label="$t('alertRecord.alertContent') + ':'" style="margin-bottom: 5px !important">
52+ <span>{{ formData.alertContent }}</span>
53+ </el-form-item>
54+ </el-form>
55+ </div>
56+ </el-col>
57+ <el-col :span="8">
58+ <div class="record-detail">
59+ <el-space wrap>
60+ <el-icon size="18" color="red">
61+ <Bell />
62+ </el-icon>
63+ <h5 class="title">{{ t('alertRecord.alertStatus') }}</h5>
64+ </el-space>
65+ <el-form label-position="right" label-width="120">
66+ <el-form-item :label="$t('alertRecord.table[7]') + ':'" style="margin-bottom: 5px !important">
67+ <span>{{ formData.startTime }}</span>
68+ </el-form-item>
69+ <el-form-item :label="$t('alertRecord.table[8]') + ':'" style="margin-bottom: 5px !important">
70+ <span>{{ formData.endTime }}</span>
71+ </el-form-item>
72+ <el-form-item :label="$t('alertRecord.table[9]') + ':'" style="margin-bottom: 5px !important">
73+ <span>{{ durationFormat(formData.duration) }}</span>
74+ </el-form-item>
75+ <el-form-item :label="$t('alertRecord.table[12]') + ':'" style="margin-bottom: 5px !important">
76+ <el-switch v-model="formData.recordStatus" :active-value="1" :inactive-value="0"
77+ @change="markAs" />
78+ </el-form-item>
79+ </el-form>
80+ </div>
81+ </el-col>
82+ </el-row>
83+ <el-divider style="margin: 5px 0;" />
84+ <div style="min-height: 370px;" v-if="formData.templateRuleType === 'index'">
85+ <h5 class="title">{{ t('alertRecord.alertRelationView') }}</h5>
86+ <div v-for="(item, index) in relationDatas" style="width: 100%; height: 200px;margin-top: 20px;" :key="index">
87+ <RecordLine :title="item.name" :datas="item.datas" :unit="item.unit" />
88+ </div>
89+ </div>
90+ </div>
91+</template>
92+ 
93+<script setup lang='ts'>
94+import "element-plus/es/components/message-box/style/index";
95+import { useRequest } from "vue-request";
96+import request from "@/request";
97+import { Bell } from "@element-plus/icons-vue";
98+import { useI18n } from "vue-i18n";
99+import RecordLine from "@/views/alert/AlertRecord/components/RecordLine.vue"
100+const { t } = useI18n();
101+const theme = localStorage.getItem('theme');
102+const color = ref<string>(theme === 'dark' ? '#d4d4d4' : '#1d212a')
103+const background = ref<string>(theme === 'dark' ? '#303030' : '#F5F7FB')
104+ 
105+const offsetHeight = ref<number>()
106+const carHeight = ref<number>()
107+const formData = ref<any>({})
108+const relationDatas = ref<any[]>([])
109+ 
110+const { data: res, run: requestData } = useRequest(
111+ (id: number) => {
112+ return request.get(`/alertCenter/api/v1/alertRecord/${id}`)
113+ },
114+ { manual: true }
115+);
116+watch(res, (res: any) => {
117+ if (res && res.code === 200) {
118+ formData.value = res.data
119+ }
120+});
121+ 
122+const markAs = (val: any) => {
123+ if (formData.value.id) {
124+ if (val === 0) {
125+ request.post(`/alertCenter/api/v1/alertRecord/markAsUnread?ids=${formData.value.id}`).then((res: any) => {
126+ if (res && res.code === 200) {
127+ requestData(formData.value.id)
128+ }
129+ })
130+ }
131+ if (val === 1) {
132+ request.post(`/alertCenter/api/v1/alertRecord/markAsRead?ids=${formData.value.id}`).then((res: any) => {
133+ if (res && res.code === 200) {
134+ requestData(formData.value.id)
135+ }
136+ })
137+ }
138+ }
139+}
140+ 
141+const { data: relationRes, run: requestRelationData } = useRequest(
142+ (id: number) => {
143+ return request.get(`/alertCenter/api/v1/alertRecord/${id}/relation`)
144+ },
145+ { manual: true }
146+);
147+watch(relationRes, (relationRes: any) => {
148+ if (relationRes && relationRes.code === 200) {
149+ relationDatas.value = relationRes.data
150+ console.log(relationDatas.value)
151+ }
152+});
153+ 
154+const durationFormat = (val: any) => {
155+ if (typeof val === 'number') {
156+ if (val <= 0) {
157+ return '00:00:00';
158+ } else {
159+ let hh = parseInt(val / 3600);
160+ let shh = val - hh * 3600;
161+ let ii = parseInt(shh / 60);
162+ let ss = shh - ii * 60;
163+ return (hh < 10 ? '0' + hh : hh) + ':' + (ii < 10 ? '0' + ii : ii) + ':' + (ss < 10 ? '0' + ss : ss);
164+ }
165+ } else {
166+ return '00:00:00';
167+ }
168+}
169+ 
170+const router = useRouter();
171+onMounted(() => {
172+ offsetHeight.value = document.body.offsetHeight - 50
173+ carHeight.value = offsetHeight.value * 0.4
174+ let _id = null
175+ if (window.$wujie) {
176+ _id = window.$wujie?.props.data.id as number;
177+ } else {
178+ let param = router.currentRoute.value.query
179+ _id = param.id
180+ }
181+ if (_id) {
182+ requestData(_id)
183+ requestRelationData(_id)
184+ }
185+ 
186+ const wujie = window.$wujie;
187+ if (wujie) {
188+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
189+ nextTick(() => {
190+ color.value = theme === 'dark' ? '#d4d4d4' : '#1d212a'
191+ background.value = theme === 'dark' ? '#303030' : '#F5F7FB'
192+ });
193+ });
194+ }
195+})
196+</script>
197+<style scoped lang='scss'>
198+.card-header {
199+ display: flex;
200+ justify-content: space-between;
201+ align-items: center;
202+}
203+ 
204+.record-detail {
205+ background: v-bind(background);
206+ color: v-bind(color);
207+ border-radius: 2px;
208+ margin: 5px;
209+ height: 220px;
210+}
211+ 
212+.title {
213+ font-style: 'normal';
214+ font-weight: 'bold';
215+ font-size: 14px;
216+ line-height: 22px;
217+ margin: 5px
218+}
219+ 
220+.content {
221+ margin-top: 10px;
222+}
223+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertRecord/components/RecordLine.vue+90-0
@@ -0,0 +1,90 @@
1+<template>
2+ <div ref="main" style="width: 100%; height: 100%;"></div>
3+</template>
4+ 
5+<script setup lang='ts'>
6+import { ref } from 'vue'
7+import * as echarts from "echarts"
8+const main = ref()
9+const props = withDefaults(
10+ defineProps<{
11+ datas?: any[],
12+ title: string,
13+ unit: string,
14+ }>(),
15+ {
16+ title: '',
17+ unit: ''
18+ }
19+);
20+watch(() => props.datas, (datas) => {
21+ if (datas && datas.length > 0) {
22+ init()
23+ }
24+}, { deep: true })
25+ 
26+watch(() => props.title, (title) => {
27+
28+})
29+watch(() => props.unit, (unit) => {
30+
31+})
32+ 
33+const init = () => {
34+ const theme = localStorage.getItem('theme');
35+ let chart = echarts.init(main.value)
36+ const option = {
37+ title: {
38+ show: true,
39+ text: props.title,
40+ left: '40%',
41+ bottom: 'bottom',
42+ textStyle: {
43+ color: theme === 'dark' ? '#d4d4d4' : '#1d212a'
44+ }
45+ },
46+ tooltip: {
47+ trigger: 'item'
48+ },
49+ legend: {
50+ left: 'left',
51+ textStyle: {
52+ color: theme === 'dark' ? '#d4d4d4' : '#1d212a'
53+ }
54+ },
55+ xAxis: {
56+ type: 'time',
57+ splitLine: {
58+ show: true
59+ }
60+ },
61+ yAxis: {
62+ name: props.unit,
63+ type: 'value',
64+ splitLine: {
65+ show: true
66+ }
67+ },
68+ series: [
69+ {
70+ name: props.title,
71+ type: 'line',
72+ showSymbol: false,
73+ hoverAnimation: false,
74+ labelLine: {
75+ show: true,
76+ smooth: true
77+ },
78+ data: props.datas
79+ }
80+ ]
81+ }
82+ chart.setOption(option)
83+}
84+ 
85+onMounted(() => {
86+ init()
87+})
88+</script>
89+<style scoped lang='scss'>
90+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertRecord/components/RecordPie.vue+120-0
@@ -0,0 +1,120 @@
1+<template>
2+ <div ref="main" style="width: 100%; height: 125px;"></div>
3+</template>
4+ 
5+<script setup lang='ts'>
6+import { ref } from 'vue'
7+import * as echarts from "echarts"
8+const main = ref()
9+const props = withDefaults(
10+ defineProps<{
11+ datas?: any[],
12+ title: string
13+ }>(),
14+ {
15+ title: ''
16+ }
17+);
18+watch(() => props.datas, (datas) => {
19+ if (datas && datas.length > 0) {
20+ init()
21+ }
22+}, { deep: true })
23+ 
24+watch(() => props.title, (title) => {
25+
26+})
27+ 
28+const init = () => {
29+ const theme = localStorage.getItem('theme');
30+ let chart = echarts.init(main.value)
31+ const option = {
32+ title: {
33+ show: true,
34+ text: props.title,
35+ left: '34%',
36+ bottom: 'bottom',
37+ padding: 0,
38+ textStyle: {
39+ fontWeight: 'normal',
40+ color: theme === 'dark' ? '#d4d4d4' : '#1d212a',
41+ fontSize: 14
42+ }
43+ },
44+ tooltip: {
45+ trigger: 'item'
46+ },
47+ legend: {
48+ right: '18%',
49+ top: 'middle',
50+ orient: 'vertical',
51+ textStyle: {
52+ color: theme === 'dark' ? '#d4d4d4' : '#1d212a',
53+ fontSize: 11
54+ },
55+ formatter: function (name: string) {
56+ let datas = option.series[0].data
57+ if (datas.length === 0) {
58+ return `${name}: 0`
59+ }
60+ let val = ''
61+ for (let data of datas) {
62+ if (data.name === name) {
63+ val = data.value
64+ break;
65+ }
66+ }
67+ return `${name}: ${val}`
68+ }
69+ },
70+ series: [
71+ {
72+ name: props.title,
73+ top: '10px',
74+ type: 'pie',
75+ radius: ['40%', '60%'],
76+ center: ['45%', '50%'],
77+ avoidLabelOverlap: false,
78+ label: {
79+ show: false,
80+ position: 'center'
81+ },
82+ emphasis: {
83+ label: {
84+ show: true,
85+ fontSize: '14',
86+ fontWeight: 'bold'
87+ }
88+ },
89+ labelLine: {
90+ show: true,
91+ smooth: true
92+ },
93+ data: props.datas || []
94+ }
95+ ]
96+ }
97+ chart.setOption(option)
98+}
99+ 
100+onMounted(() => {
101+ const wujie = window.$wujie;
102+ // Judge whether it is a plug-in environment or a local environment through wujie
103+ if (wujie) {
104+ // Monitoring platform language change
105+ wujie?.bus.$on('opengauss-locale-change', (val: string) => {
106+ console.log('log-search catch locale change');
107+ nextTick(() => {
108+ init()
109+ });
110+ });
111+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
112+ nextTick(() => {
113+ init()
114+ });
115+ });
116+ }
117+})
118+ 
119+</script>
120+<style scoped lang='scss'></style>
Aplugins/alert-center/web-ui/src/views/alert/AlertRule/Index.vue+262-0
@@ -0,0 +1,262 @@
1+<template>
2+ <div v-if="showMain">
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item >{{ t('alertRule.title') }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <div class="search-form">
8+ <div class="filter">
9+ <span>{{ $t('alertRule.ruleType') }}&nbsp;</span>
10+ <el-select v-model="formData.ruleType" :placeholder="$t('alertRule.selectRuleType')" clearable
11+ @change="search">
12+ <el-option v-for="item in ruleTypeList" :key="item" :value="item" :label="$t(`alertRule.${item}`)" />
13+ </el-select>
14+ </div>
15+ <div class="filter">
16+ <span>{{ $t('alertRule.level') }}&nbsp;</span>
17+ <el-select v-model="formData.level" :placeholder="$t('alertRule.selectLevelType')" style="max-width: 100px"
18+ clearable @change="search">
19+ <el-option v-for="item in levelList" :key="item" :value="item" :label="$t(`alertRule.${item}`)" />
20+ </el-select>
21+ </div>
22+ <div class="filter">
23+ <el-input v-model="formData.ruleName" style="width: 150px"
24+ :placeholder="$t('alertRule.ruleNamePlaceholder')">
25+ <template #suffix>
26+ <el-button :icon="Search" @click="search" />
27+ </template>
28+ </el-input>
29+ </div>
30+ </div>
31+ <div class="alert-rule">
32+ <div class="alert-record-table">
33+ <el-table size="small" :data="tableDatas" style="width: 100%" header-cell-class-name="grid-header" border>
34+ <el-table-column :label="$t('alertRule.table[0]')" prop="ruleName" width="140">
35+ <!-- <template #default="scope">
36+ <el-link :underline="false" type="primary" @click="showDetail(scope.row.ruleId)">{{
37+ scope.row.ruleName }}</el-link>
38+ </template> -->
39+ </el-table-column>
40+ <el-table-column :label="$t('alertRule.table[1]')" prop="ruleType" width="100">
41+ <template #default="scope">
42+ <span>{{ $t(`alertRule.${scope.row.ruleType}`) }}</span>
43+ </template>
44+ </el-table-column>
45+ <el-table-column prop="level" :label="$t('alertRule.table[2]')" width="100">
46+ <template #default="scope">
47+ <span>{{ $t(`alertRule.${scope.row.level}`) }}</span>
48+ </template>
49+ </el-table-column>
50+ <el-table-column prop="ruleExpDesc" :label="$t('alertRule.table[8]')" min-width="200">
51+ <template #default="scope">
52+ <span v-html="scope.row.ruleExpDesc"></span>
53+ </template>
54+ </el-table-column>
55+ <el-table-column prop="ruleExpComb" :label="$t('alertRule.table[9]')">
56+ </el-table-column>
57+ <el-table-column prop="isRepeat" :label="$t('alertRule.table[3]')" width="100">
58+ <template #default="scope">
59+ <span>{{ scope.row.isRepeat === 1 ? $t('alertRule.isRepeat') :
60+ $t('alertRule.isNotRepeat') }}</span>
61+ </template>
62+ </el-table-column>
63+ <el-table-column prop="isSilence" :label="$t('alertRule.table[4]')" width="200">
64+ <template #default="scope">
65+ <span>{{ scope.row.isSilence === 1 ? scope.row.silenceStartTime + $t('alertRule.to') +
66+ scope.row.silenceEndTime : $t('alertRule.isNotSilence') }}</span>
67+ </template>
68+ </el-table-column>
69+ <el-table-column prop="alertNotify" :label="$t('alertRule.table[5]')" width="120">
70+ <template #default="scope">
71+ <span v-text="showAlertNotify(scope.row.alertNotify)"></span>
72+ </template>
73+ </el-table-column>
74+ <el-table-column prop="ruleContent" :label="$t('alertRule.table[6]')" min-width="200"/>
75+ <el-table-column :label="$t('alertRule.table[7]')" width="120" fixed="right">
76+ <template #default="scope">
77+ <el-button link type="primary" size="small" @click="showDetail(scope.row.ruleId)">{{ $t('app.view')
78+ }}</el-button>
79+ </template>
80+ </el-table-column>
81+ </el-table>
82+ <el-pagination v-model:currentPage="page.currentPage" v-model:pageSize="page.pageSize" :total="page.total"
83+ :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small
84+ @size-change="handleSizeChange" @current-change="handleCurrentChange" />
85+ </div>
86+ </div>
87+ </div>
88+ <div v-else>
89+ <RuleDetail :ruleId="curId" @updateRule="updateRule" @cancelRule="cancelRule" :state="state"/>
90+ </div>
91+</template>
92+ 
93+<script setup lang="ts">
94+import { Search, Refresh, Delete } from "@element-plus/icons-vue";
95+import "element-plus/es/components/message-box/style/index";
96+import { useRequest } from "vue-request";
97+import request from "@/request";
98+import { i18n } from "@/i18n";
99+import { ElMessageBox, ElMessage } from "element-plus";
100+import { useI18n } from "vue-i18n";
101+import RuleDetail from "@/views/alert/AlertRule/RuleDetail.vue";
102+const { t } = useI18n();
103+ 
104+const theme = localStorage.getItem('theme');
105+const color = ref<string>(theme === 'dark' ? '#d4d4d4' : '#1d212a')
106+const background = ref<string>(theme === 'dark' ? '#303030' : '#F5F7FB')
107+ 
108+const showMain = ref<boolean>(true)
109+const curId = ref<number>()
110+const state = ref<string>()
111+const tableDatas = ref<any[]>([]);
112+const page = reactive({
113+ currentPage: 1,
114+ pageSize: 10,
115+ total: 0,
116+});
117+const ruleTypeList = reactive(['index', 'log'])
118+const levelList = reactive(['serious', 'warn', 'info'])
119+const formData = ref<any>({
120+ ruleName: '',
121+ ruleType: '',
122+ level: ''
123+})
124+ 
125+const { data: ruleResult, run: requestData } = useRequest(
126+ () => {
127+ let params = {}
128+ params = Object.assign(params, formData.value, { pageNum: page.currentPage, pageSize: page.pageSize })
129+ return request.get("/alertCenter/api/v1/alertRule", params)
130+ },
131+ { manual: true }
132+);
133+watch(ruleResult, (ruleResult: any) => {
134+ if (ruleResult && ruleResult.code === 200) {
135+ tableDatas.value = ruleResult.rows || []
136+ page.total = ruleResult.total
137+ } else {
138+ tableDatas.value = []
139+ page.total = 0
140+ const msg = t("app.queryFail");
141+ ElMessage({
142+ showClose: true,
143+ message: msg,
144+ type: "error",
145+ });
146+ }
147+});
148+ 
149+const search = () => {
150+ page.currentPage = 1
151+ page.pageSize = 10
152+ requestData()
153+}
154+const handleSizeChange = (val: any) => {
155+ page.pageSize = val
156+ requestData()
157+}
158+const handleCurrentChange = (val: any) => {
159+ page.currentPage = val
160+ requestData()
161+}
162+const showAlertNotify = (val: string) => {
163+ if (!val) {
164+ return ''
165+ }
166+ let arr = val.split(',');
167+ let result = arr.map(item => t(`alertRule.${item}`)).join(',')
168+ return result
169+}
170+ 
171+const showDetail = (id: number) => {
172+ curId.value = id
173+ state.value = "detail"
174+ showMain.value = false
175+}
176+const updateRule = () => {
177+ formData.value = {
178+ ruleName: '',
179+ ruleType: '',
180+ level: ''
181+ }
182+ search()
183+ curId.value = undefined
184+ showMain.value = true
185+}
186+const cancelRule = () => {
187+ curId.value = undefined
188+ showMain.value = true
189+}
190+onMounted(() => {
191+ requestData()
192+ 
193+ const wujie = window.$wujie;
194+ if (wujie) {
195+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
196+ nextTick(() => {
197+ color.value = theme === 'dark' ? '#d4d4d4' : '#1d212a'
198+ background.value = theme === 'dark' ? '#303030' : '#F5F7FB'
199+ });
200+ });
201+ }
202+})
203+ 
204+</script>
205+<style lang='scss' scoped>
206+.search-form {
207+ .filter {
208+ :deep(.el-button--small) {
209+ background-color: v-bind(background) !important;
210+ color: v-bind(color) !important;
211+ }
212+ }
213+}
214+ 
215+.alert-rule {
216+ &-chart {
217+ height: 380px;
218+ display: flex;
219+ flex-direction: column;
220+ align-items: flex-end;
221+ 
222+ .chart-wrap {
223+ width: 100%;
224+ height: 100%;
225+ display: flex;
226+ flex-direction: column;
227+ justify-content: center;
228+ text-align: center;
229+ 
230+ .chart-content {
231+ width: 100%;
232+ height: 100%;
233+ }
234+ }
235+ 
236+ .noresult-wrap {
237+ width: 100%;
238+ height: 500px;
239+ 
240+ .noresult-img {
241+ width: 200px;
242+ display: block;
243+ margin: 20px auto;
244+ }
245+ 
246+ .noresult-text {
247+ text-align: center;
248+ color: #707070;
249+ }
250+ }
251+ 
252+ .noResult .barLine-chart {
253+ display: none;
254+ }
255+ }
256+ 
257+ :deep(.el-link) {
258+ color: #409EFF !important;
259+ }
260+ 
261+}
262+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertRule/RuleDetail.vue+273-0
@@ -0,0 +1,273 @@
1+<template>
2+ <div>
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('alertRule.title') }}</el-breadcrumb-item>
5+ <el-breadcrumb-item v-if="state === 'add'">{{ t('alertRule.addTitle') }}</el-breadcrumb-item>
6+ <el-breadcrumb-item v-if="state === 'edit'">{{ t('alertRule.editTitle') }}</el-breadcrumb-item>
7+ <el-breadcrumb-item v-if="state === 'detail'">{{ t('alertRule.detailTitle') }}</el-breadcrumb-item>
8+ </el-breadcrumb>
9+ <el-divider />
10+ <el-descriptions :title="$t('alertRule.alertTitle')"></el-descriptions>
11+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
12+ <el-form-item :label="$t('alertRule.ruleName')" prop="ruleName">
13+ <el-input v-model="formData.ruleName" :placeholder="$t('alertRule.ruleNamePlaceholder')"
14+ :disabled="disabled"></el-input>
15+ </el-form-item>
16+ <el-form-item :label="$t('alertRule.ruleType')" prop="ruleType">
17+ <el-radio-group v-model="formData.ruleType" :disabled="disabled">
18+ <el-radio v-for="item in ruleTypeList" :key="item" :label="item">{{ $t(`alertRule.${item}`)
19+ }}</el-radio>
20+ </el-radio-group>
21+ </el-form-item>
22+ <el-form-item :label="$t('alertRule.level')" prop="level">
23+ <el-radio-group v-model="formData.level" :disabled="disabled">
24+ <el-radio v-for="item in levelList" :key="item" :label="item">{{ $t(`alertRule.${item}`) }}</el-radio>
25+ </el-radio-group>
26+ </el-form-item>
27+ <el-form-item :label="$t('alertRule.ruleItem')" prop="">
28+ <el-card class="box-card" v-for="item in formData.alertRuleItemList" :key="item.id"
29+ body-style="padding: 5px">
30+ <span style="margin: 5px 0;">{{ $t('alertRule.ruleItemNum') }}:</span><el-input v-model="item.ruleMark"
31+ :disabled="disabled" style="width: 30px;margin: 5px;"></el-input>
32+ <span style="margin: 5px 0 10px 5px;">{{ $t('alertRule.ruleItemExp') }}:</span>
33+ <el-select v-model="item.ruleExpName" :disabled="disabled" style="width: 100px;margin: 5px 0 5px 5px;">
34+ <el-option v-for="item0 in ruleItemExpList" :key="item0.name" :value="item0.name"
35+ :label="item0.i18nName" />
36+ </el-select>
37+ <span v-if="item.itemParamList && item.itemParamList.length > 0">
38+ <el-input v-model="item0.paramValue" style="width: 80px;margin: 1px;" :disabled="disabled"
39+ v-for="item0 in item.itemParamList" :key="item0.id"></el-input>
40+ </span>
41+ <el-select v-model="item.action" :disabled="disabled" style="width: 100px;margin: 5px;">
42+ <el-option v-for="item0 in actionList" :key="item0.value" :value="item0.value"
43+ :label="item0.name" />
44+ </el-select>
45+ <el-input v-model="item.operate" style="width: 50px;margin: 5px;" :disabled="disabled"></el-input>
46+ <el-input v-model="item.limitValue" style="width: 100px;margin: 5px;" :disabled="disabled"></el-input>
47+ <span style="width: 50px;margin: 5px;">{{ item.unit }}</span>
48+ </el-card>
49+ </el-form-item>
50+ <el-form-item :label="$t('alertRule.ruleExpComb')" prop="ruleExpComb">
51+ <span v-for="item in ruleExpComb" :key="item">
52+ <el-select :value="item" v-if="showLogicSelect(item)" style="width: 50px;" :disabled="disabled">
53+ <el-option v-for="item in logicSymbolList" :key="item" :value="item"
54+ :label="$t(`alertRule.${item}`)" />
55+ </el-select>
56+ <el-input :value="item" style="width: 50px;" v-else :disabled="disabled"></el-input>
57+ </span>
58+ </el-form-item>
59+ <el-form-item :label="$t('alertRule.ruleContent')" prop="ruleContent">
60+ <el-input v-model="formData.ruleContent" :disabled="disabled"></el-input>
61+ </el-form-item>
62+ <el-form-item :label="$t('alertRule.notifyDuration')" prop="notifyDuration">
63+ <el-input v-model="formData.notifyDuration" style="width: 100px;margin-right: 5px;"
64+ :disabled="disabled"></el-input>
65+ <el-select v-model="formData.notifyDurationUnit" :disabled="disabled" style="width: 80px;">
66+ <el-option v-for="item in durationUnitList" :disabled="disabled" :key="item.name" :value="item.value"
67+ :label="$t(`alertRule.${item.name}`)" />
68+ </el-select>
69+ </el-form-item>
70+ <el-form-item :label="$t('alertRule.alertDesc')" prop="alertDesc">
71+ <el-input v-model="formData.alertDesc" :disabled="disabled"></el-input>
72+ </el-form-item>
73+ </el-form>
74+ <el-descriptions :title="$t('alertRule.notifyTitle')"></el-descriptions>
75+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
76+ <el-form-item :label="$t('alertRule.alertNotify')" prop="notifyDuration">
77+ <el-checkbox-group v-model="alertNotifyList" :disabled="disabled">
78+ <el-checkbox label="firing">{{ $t('alertRule.firing') }}</el-checkbox>
79+ <el-checkbox label="recover">{{ $t('alertRule.recover') }}</el-checkbox>
80+ </el-checkbox-group>
81+ </el-form-item>
82+ <el-form-item :label="$t('alertRule.repeat')" prop="isRepeat">
83+ <el-radio-group v-model="isRepeat" :disabled="disabled">
84+ <el-radio label="1">{{ $t('alertRule.isRepeat') }}</el-radio>
85+ <el-radio label="0">{{ $t('alertRule.isNotRepeat') }}</el-radio>
86+ </el-radio-group>
87+ </el-form-item>
88+ <el-form-item :label="$t('alertRule.silence')" prop="isSilence">
89+ <el-radio-group v-model="isSilence" :disabled="disabled">
90+ <el-radio label="1" key="1">{{ $t('alertRule.isSilence') }}</el-radio>
91+ <el-radio label="0" key="0">{{ $t('alertRule.isNotSilence') }}</el-radio>
92+ </el-radio-group>
93+ <span style="margin-left: 30px;" v-if="isSilence === 1 || isSilence === '1'">{{ $t('alertRule.silenceTime')
94+ }}</span>
95+ <span style="width: 250px;margin-left: 5px;">
96+ <el-date-picker :disabled="disabled" v-if="isSilence === 1 || isSilence === '1'" v-model="silenceTimes"
97+ type="datetimerange" range-separator="~" format="YYYY-MM-DD HH:mm:ss"
98+ value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t(`alertRecord.startTimePlaceholder`)"
99+ :end-placeholder="t(`alertRecord.endTimePlaceholder`)" />
100+ </span>
101+ </el-form-item>
102+ <el-form-item :label="$t('alertRule.notifyWay')">
103+ <el-select v-model="notifyWayIdArr" multiple :disabled="disabled">
104+ <el-option v-for="item in notifyWayList" :key="item.id" :value="item.id + ''" :label="item.name" />
105+ </el-select>
106+ </el-form-item>
107+ </el-form>
108+ <el-row style="margin-top: 10px;">
109+ <!-- <el-button type="primary" @click="confirm">{{ t('app.confirm') }}</el-button> -->
110+ <el-button type="primary" @click="cancel">{{ t('app.cancel') }}</el-button>
111+ </el-row>
112+ </div>
113+</template>
114+ 
115+<script setup lang='ts'>
116+import { Search, Refresh, Delete } from "@element-plus/icons-vue";
117+import "element-plus/es/components/message-box/style/index";
118+import { useRequest } from "vue-request";
119+import request from "@/request";
120+import { i18n } from "@/i18n";
121+import { ElMessageBox, ElMessage } from "element-plus";
122+import { useI18n } from "vue-i18n";
123+const { t } = useI18n();
124+ 
125+const props = withDefaults(
126+ defineProps<{
127+ ruleId: number,
128+ state: string,
129+ }>(),
130+ {
131+ state: 'detail'
132+ }
133+);
134+ 
135+const emit = defineEmits(["updateRule", "cancelRule"]);
136+ 
137+const disabled = ref<boolean>(true)
138+const ruleItemExpList = ref<any[]>()
139+const actionList = ref<any[]>([{
140+ name: t('alertRule.normalAction'), value: 'normal'
141+}, {
142+ name: t('alertRule.increaseAction'), value: 'increase'
143+}, {
144+ name: t('alertRule.decreaseAction'), value: 'decrease'
145+}])
146+const logicSymbolList = ref<string[]>(['and', 'or'])
147+const durationUnitList = ref<any[]>([{
148+ name: 'second',
149+ value: 's'
150+}, {
151+ name: 'minute',
152+ value: 'm'
153+}, {
154+ name: 'hour',
155+ value: 'h'
156+}, {
157+ name: 'day',
158+ value: 'd'
159+}])
160+const notifyWayIdArr = ref<string[]>([])
161+const notifyWayList = ref<string[]>([])
162+const formData = ref<any>({
163+ ruleName: '',
164+ ruleType: '',
165+ level: '',
166+ ruleExpComb: '',
167+ isRepeat: 1,
168+ isSilence: 0,
169+})
170+const ruleTypeList = reactive(['index', 'log'])
171+const levelList = reactive(['serious', 'warn', 'info'])
172+const ruleExpComb = ref<string[]>([])
173+const alertNotifyList = ref<string[]>([])
174+const isRepeat = ref<string>('')
175+const isSilence = ref<string>('')
176+const silenceTimes = ref<any[]>([])
177+const showLogicSelect = (logicSymbol: string) => {
178+ return logicSymbolList.value.includes(logicSymbol)
179+}
180+ 
181+const { data: ruleDetail, run: requestData } = useRequest(
182+ (id) => {
183+ return request.get(`/alertCenter/api/v1/alertRule/${id}`)
184+ },
185+ { manual: true }
186+)
187+watch(ruleDetail, (ruleDetail: any) => {
188+ if (ruleDetail && ruleDetail.code === 200) {
189+ formData.value = ruleDetail.data
190+ if (formData.value?.ruleExpComb) {
191+ ruleExpComb.value = formData.value?.ruleExpComb.split(',')
192+ }
193+ if (formData.value?.alertNotify) {
194+ alertNotifyList.value = formData.value?.alertNotify.split(',')
195+ }
196+ if (formData.value?.isRepeat) {
197+ isRepeat.value = formData.value?.isRepeat + ''
198+ }
199+ if (formData.value?.isSilence) {
200+ isSilence.value = formData.value?.isSilence + ''
201+ } else {
202+ isSilence.value = '0'
203+ }
204+ if (formData.value.notifyWayIds) {
205+ notifyWayIdArr.value = formData.value.notifyWayIds.split(',')
206+ }
207+ if (formData.value.silenceStartTime && formData.value.silenceEndTime) {
208+ silenceTimes.value = [formData.value.silenceStartTime, formData.value.silenceEndTime]
209+ }
210+ } else {
211+ const msg = t("app.queryFail");
212+ ElMessage({
213+ showClose: true,
214+ message: msg,
215+ type: "error",
216+ });
217+ }
218+});
219+ 
220+const cancel = () => {
221+ emit("cancelRule")
222+}
223+ 
224+const requestNotifyWayData = () => {
225+ request.get(`/alertCenter/api/v1/notifyWay/list`).then((res: any) => {
226+ if (res && res.code === 200) {
227+ notifyWayList.value = res.data
228+ }
229+ })
230+}
231+ 
232+const requestRuleItemList = () => {
233+ request.get(`/alertCenter/api/v1/alertRule/ruleItem/properties`).then((res: any) => {
234+ if (res && res.code === 200) {
235+ ruleItemExpList.value = res.data
236+ }
237+ })
238+}
239+onMounted(() => {
240+ requestNotifyWayData()
241+ requestData(props.ruleId)
242+ requestRuleItemList()
243+ if (props.state !== 'detail') {
244+ disabled.value = false
245+ } else {
246+ disabled.value = true
247+ }
248+
249+ const wujie = window.$wujie;
250+ // Judge whether it is a plug-in environment or a local environment through wujie
251+ if (wujie) {
252+ // Monitoring platform language change
253+ wujie?.bus.$on('opengauss-locale-change', (val: string) => {
254+ console.log('log-search catch locale change');
255+ nextTick(() => {
256+ actionList.value = [{
257+ name: t('alertRule.normalAction'), value: 'normal'
258+ }, {
259+ name: t('alertRule.increaseAction'), value: 'increase'
260+ }, {
261+ name: t('alertRule.decreaseAction'), value: 'decrease'
262+ }]
263+ });
264+ });
265+ // wujie?.bus.$on('opengauss-theme-change', (val: string) => {
266+ // nextTick(() => {
267+ // requestPisData()
268+ // });
269+ // });
270+ }
271+})
272+</script>
273+<style scoped lang='scss'></style>
Aplugins/alert-center/web-ui/src/views/alert/AlertTemplate/Index.vue+320-0
@@ -0,0 +1,320 @@
1+<template>
2+ <div v-if="showMain">
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('alertTemplate.title') }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <el-row>
8+ <el-col :span="8" class="container">
9+ <el-descriptions :title="t('alertTemplate.title')"></el-descriptions>
10+ <div class="search-form">
11+ <div class="filter">
12+ <el-button type="primary" @click="addTemplate">{{ $t('app.add') }}</el-button>
13+ </div>
14+ <div class="seperator"></div>
15+ <div class="filter search">
16+ <el-input v-model="formData.templateName" style="width: 150px"
17+ :placeholder="$t('alertTemplate.templateNamePlaceholder')">
18+ <template #suffix>
19+ <el-button :icon="Search" @click="search" />
20+ </template>
21+ </el-input>
22+ </div>
23+ </div>
24+ <div class="template-table">
25+ <el-table size="small" :data="tableDatas" style="width: 100%" header-cell-class-name="grid-header" border>
26+ <el-table-column width="50">
27+ <template #default="scope">
28+ <el-radio :label="scope.row.id" v-model="currentId"
29+ @change="getCurrentRow(scope.row.id)">&nbsp;</el-radio>
30+ </template>
31+ </el-table-column>
32+ <el-table-column prop="templateName" :label="$t('alertTemplate.table[0]')" />
33+ <el-table-column :label="$t('alertTemplate.table[1]')">
34+ <template #default="scope">
35+ <el-button link type="primary" size="small" @click="editTemplate(scope.row.id)">{{
36+ $t('app.edit') }}</el-button>
37+ <el-button link type="primary" size="small" @click="delTemplate(scope.row.id)">{{
38+ $t('app.delete') }}</el-button>
39+ </template>
40+ </el-table-column>
41+ </el-table>
42+ <div class="pagination">
43+ <el-pagination v-model:currentPage="page.currentPage" v-model:pageSize="page.pageSize"
44+ :total="page.total" :page-sizes="[10, 20, 30, 40]" layout="total,sizes,prev,pager,next"
45+ background small @size-change="handleSizeChange" @current-change="handleCurrentChange" />
46+ </div>
47+ </div>
48+ </el-col>
49+ <el-col :span="16" class="container">
50+ <el-descriptions :title="t('alertRule.title')"></el-descriptions>
51+ <div class="search-form">
52+ <div class="filter search">
53+ <el-input v-model="ruleFormData.ruleName" style="width: 150px"
54+ :placeholder="$t('alertRule.ruleNamePlaceholder')">
55+ <template #suffix>
56+ <el-button :icon="Search" @click="ruleSearch" />
57+ </template>
58+ </el-input>
59+ </div>
60+ </div>
61+ <div class="template-table">
62+ <el-table size="small" :data="ruleTableDatas" style="width: 100%" header-cell-class-name="grid-header"
63+ border>
64+ <el-table-column :label="$t('alertRule.table[0]')" prop="ruleName" width="140"></el-table-column>
65+ <el-table-column :label="$t('alertRule.table[1]')" prop="ruleType" width="100">
66+ <template #default="scope">
67+ <span>{{ $t(`alertRule.${scope.row.ruleType}`) }}</span>
68+ </template>
69+ </el-table-column>
70+ <el-table-column prop="level" :label="$t('alertRule.table[2]')" width="100">
71+ <template #default="scope">
72+ <span>{{ $t(`alertRule.${scope.row.level}`) }}</span>
73+ </template>
74+ </el-table-column>
75+ <el-table-column prop="ruleExpDesc" :label="$t('alertRule.table[8]')" min-width="200">
76+ <template #default="scope">
77+ <span v-html="scope.row.ruleExpDesc"></span>
78+ </template>
79+ </el-table-column>
80+ <el-table-column prop="ruleExpComb" :label="$t('alertRule.table[9]')">
81+ </el-table-column>
82+ <el-table-column prop="isRepeat" :label="$t('alertRule.table[3]')" width="100">
83+ <template #default="scope">
84+ <span>{{ scope.row.isRepeat === 1 ? $t('alertRule.isRepeat') :
85+ $t('alertRule.isNotRepeat') }}</span>
86+ </template>
87+ </el-table-column>
88+ <el-table-column prop="isSilence" :label="$t('alertRule.table[4]')" width="200">
89+ <template #default="scope">
90+ <span>{{ scope.row.isSilence === 1 ? scope.row.silenceStartTime + $t('alertRule.to') +
91+ scope.row.silenceEndTime : $t('alertRule.isNotSilence') }}</span>
92+ </template>
93+ </el-table-column>
94+ <el-table-column prop="alertNotify" :label="$t('alertRule.table[5]')" width="120">
95+ <template #default="scope">
96+ <span v-text="showAlertNotify(scope.row.alertNotify)"></span>
97+ </template>
98+ </el-table-column>
99+ <!-- <el-table-column prop="ruleContent" :label="$t('alertRule.table[6]')" /> -->
100+ </el-table>
101+ <div class="pagination">
102+ <el-pagination v-model:currentPage="rulePage.currentPage" v-model:pageSize="rulePage.pageSize"
103+ :total="rulePage.total" :page-sizes="[10, 20, 30, 40]" class="pagination"
104+ layout="total,sizes,prev,pager,next" background small @size-change="handleRuleSizeChange"
105+ @current-change="handleRuleCurrentChange" />
106+ </div>
107+ </div>
108+ </el-col>
109+ </el-row>
110+ </div>
111+ <div v-else>
112+ <TemplateDetail :templateId="curId" :state="state" @updateTemplate="updateTemplate"
113+ @cancelTemplate="cancelTemplate" />
114+ </div>
115+</template>
116+ 
117+<script setup lang='ts'>
118+import { Search, Refresh, Delete } from "@element-plus/icons-vue";
119+import "element-plus/es/components/message-box/style/index";
120+import { useRequest } from "vue-request";
121+import request from "@/request";
122+import { i18n } from "@/i18n";
123+import { ElMessageBox, ElMessage } from "element-plus";
124+import { useI18n } from "vue-i18n";
125+import TemplateDetail from "@/views/alert/AlertTemplate/TemplateDetail.vue";
126+const { t } = useI18n();
127+ 
128+const showMain = ref<boolean>(true)
129+const curId = ref<number>()
130+const state = ref<string>()
131+const offsetHeight = ref<number>()
132+const page = reactive({
133+ currentPage: 1,
134+ pageSize: 10,
135+ total: 0,
136+})
137+const formData = ref<any>({
138+ templateName: '',
139+})
140+const ruleFormData = ref<any>({
141+ ruleName: '',
142+})
143+const rulePage = reactive({
144+ currentPage: 1,
145+ pageSize: 10,
146+ total: 0,
147+})
148+ 
149+const currentId = ref<number>()
150+const tableDatas = ref<any[]>([])
151+const ruleTableDatas = ref<any[]>([])
152+const getCurrentRow = (id: number) => {
153+ if (id) {
154+ currentId.value = id
155+ requestRuleData()
156+ }
157+}
158+const { data: templateRes, run: requestTemplateData } = useRequest(
159+ () => {
160+ let params = {}
161+ params = Object.assign(params, formData.value, { pageNum: page.currentPage, pageSize: page.pageSize })
162+ return request.get("/alertCenter/api/v1/alertTemplate", params)
163+ },
164+ { manual: true }
165+);
166+const { data: ruleRes, run: requestRuleData } = useRequest(
167+ () => {
168+ let params = {}
169+ params = Object.assign(params, ruleFormData.value, { pageNum: rulePage.currentPage, pageSize: rulePage.pageSize })
170+ return request.get(`/alertCenter/api/v1/alertTemplate/${currentId.value}/rule`, params)
171+ },
172+ { manual: true }
173+);
174+watch(templateRes, (templateRes: any) => {
175+ if (templateRes && templateRes.code === 200) {
176+ tableDatas.value = templateRes.rows || []
177+ page.total = templateRes.total
178+ if (tableDatas.value.length > 0) {
179+ currentId.value = tableDatas.value[0].id
180+ requestRuleData()
181+ }
182+ } else {
183+ tableDatas.value = []
184+ page.total = 0
185+ const msg = t("app.queryFail");
186+ ElMessage({
187+ showClose: true,
188+ message: msg,
189+ type: "error",
190+ });
191+ }
192+});
193+watch(ruleRes, (ruleRes: any) => {
194+ if (ruleRes && ruleRes.code === 200) {
195+ ruleTableDatas.value = ruleRes.rows || []
196+ rulePage.total = ruleRes.total
197+ } else {
198+ ruleTableDatas.value = []
199+ rulePage.total = 0
200+ const msg = t("app.queryFail");
201+ ElMessage({
202+ showClose: true,
203+ message: msg,
204+ type: "error",
205+ });
206+ }
207+});
208+const search = () => {
209+ page.currentPage = 1
210+ page.pageSize = 10
211+ requestTemplateData()
212+}
213+const handleSizeChange = (val: any) => {
214+ page.pageSize = val
215+ requestTemplateData()
216+}
217+const handleCurrentChange = (val: any) => {
218+ page.currentPage = val
219+ requestTemplateData()
220+}
221+ 
222+const ruleSearch = () => {
223+ rulePage.currentPage = 1
224+ rulePage.pageSize = 10
225+ requestRuleData()
226+}
227+const handleRuleSizeChange = (val: any) => {
228+ rulePage.pageSize = val
229+ requestRuleData()
230+}
231+const handleRuleCurrentChange = (val: any) => {
232+ rulePage.currentPage = val
233+ requestRuleData()
234+}
235+const showAlertNotify = (val: string) => {
236+ if (!val) {
237+ return ''
238+ }
239+ let arr = val.split(',');
240+ let result = arr.map(item => t(`alertRule.${item}`)).join(',')
241+ return result
242+}
243+ 
244+const addTemplate = () => {
245+ state.value = 'add'
246+ showMain.value = false
247+}
248+const editTemplate = (id: number) => {
249+ state.value = 'edit'
250+ curId.value = id
251+ showMain.value = false
252+}
253+const updateTemplate = () => {
254+ formData.value = {
255+ ruleName: ''
256+ }
257+ search()
258+ curId.value = undefined
259+ showMain.value = true
260+}
261+const cancelTemplate = () => {
262+ curId.value = undefined
263+ showMain.value = true
264+}
265+const delTemplate = (id: number) => {
266+ ElMessageBox.confirm(
267+ t('app.deleteTip'),
268+ t('app.tip'),
269+ {
270+ confirmButtonText: t('app.confirm'),
271+ cancelButtonText: t('app.cancel'),
272+ type: 'warning',
273+ }
274+ ).then(() => {
275+ request.delete(`/alertCenter/api/v1/alertTemplate`, { id }).then((res: any) => {
276+ if (res && res.code === 200) {
277+ ElMessage({
278+ type: 'success',
279+ message: t('app.delSuccess'),
280+ })
281+ search()
282+ } else {
283+ ElMessage({
284+ type: 'error',
285+ message: t('app.delFail') + (res && res.msg ? "," + res.msg : ""),
286+ })
287+ }
288+ }).catch((res: any) => {
289+ ElMessage({
290+ type: 'error',
291+ message: t('app.delFail') + (res && res.msg ? "," + res.msg : ""),
292+ })
293+ })
294+ }).catch(() => {
295+ 
296+ })
297+}
298+onMounted(() => {
299+ offsetHeight.value = document.body.offsetHeight - 110
300+ requestTemplateData()
301+})
302+</script>
303+<style scoped lang='scss'>
304+.container {
305+ border: #ebeef5 1px solid;
306+ padding: 5px;
307+ min-height: 600px;
308+}
309+.template-table {
310+ :deep(.el-link) {
311+ color: #409EFF !important;
312+ }
313+}
314+ 
315+.pagination {
316+ margin-top: 5px;
317+ // bottom: 50px;
318+ // position: fixed;
319+}
320+</style>
Aplugins/alert-center/web-ui/src/views/alert/AlertTemplate/TemplateDetail.vue+293-0
@@ -0,0 +1,293 @@
1+<template>
2+ <div v-if="showMain">
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('alertTemplate.title') }}</el-breadcrumb-item>
5+ <el-breadcrumb-item>{{ title }}</el-breadcrumb-item>
6+ </el-breadcrumb>
7+ <el-divider />
8+ <!-- <el-descriptions :title="$t('alertRule.detailTitle')"></el-descriptions> -->
9+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
10+ <el-form-item :label="$t('alertTemplate.templateName')" prop="templateName">
11+ <el-input v-model="formData.templateName"
12+ :placeholder="$t('alertTemplate.templateNamePlaceholder')"></el-input>
13+ </el-form-item>
14+ <el-form-item :label="$t('alertTemplate.selectRule')">
15+ <el-table size="small" :data="tableDatas" ref="ruleTable" style="width: 100%"
16+ header-cell-class-name="grid-header" border>
17+ <el-table-column type="selection" width="55" />
18+ <el-table-column :label="$t('alertRule.table[0]')" prop="ruleName" width="140"></el-table-column>
19+ <el-table-column :label="$t('alertRule.table[1]')" prop="ruleType" width="100">
20+ <template #default="scope">
21+ <span>{{ $t(`alertRule.${scope.row.ruleType}`) }}</span>
22+ </template>
23+ </el-table-column>
24+ <el-table-column prop="level" :label="$t('alertRule.table[2]')" width="100">
25+ <template #default="scope">
26+ <span>{{ $t(`alertRule.${scope.row.level}`) }}</span>
27+ </template>
28+ </el-table-column>
29+ <el-table-column prop="ruleExpDesc" :label="$t('alertRule.table[8]')" min-width="200">
30+ <template #default="scope">
31+ <span v-html="scope.row.ruleExpDesc"></span>
32+ </template>
33+ </el-table-column>
34+ <el-table-column prop="ruleExpComb" :label="$t('alertRule.table[9]')">
35+ </el-table-column>
36+ <el-table-column prop="isRepeat" :label="$t('alertRule.table[3]')" width="100">
37+ <template #default="scope">
38+ <span>{{ scope.row.isRepeat === 1 ? $t('alertRule.isRepeat') :
39+ $t('alertRule.isNotRepeat') }}</span>
40+ </template>
41+ </el-table-column>
42+ <el-table-column prop="isSilence" :label="$t('alertRule.table[4]')" width="200">
43+ <template #default="scope">
44+ <span>{{ scope.row.isSilence === 1 ? scope.row.silenceStartTime + $t('alertRule.to') +
45+ scope.row.silenceEndTime : $t('alertRule.isNotSilence') }}</span>
46+ </template>
47+ </el-table-column>
48+ <el-table-column prop="alertNotify" :label="$t('alertRule.table[5]')" width="120">
49+ <template #default="scope">
50+ <span v-text="showAlertNotify(scope.row.alertNotify)"></span>
51+ </template>
52+ </el-table-column>
53+ <el-table-column :label="$t('alertRule.table[7]')" fixed="right">
54+ <template #default="scope">
55+ <el-button link type="primary" size="small"
56+ @click.prevent="edit(scope.row.templateRuleId, scope.row.ruleId)">
57+ {{ t('app.edit') }}
58+ </el-button>
59+ </template>
60+ </el-table-column>
61+ <!-- <el-table-column prop="ruleContent" :label="$t('alertRule.table[6]')" /> -->
62+ </el-table>
63+ </el-form-item>
64+ </el-form>
65+ <el-row>
66+ <el-button type="primary" @click="confirm">{{ t('app.confirm') }}</el-button>
67+ <el-button @click="cancel">{{ t('app.cancel') }}</el-button>
68+ </el-row>
69+ </div>
70+ <div v-else>
71+ <TemplateRuleDetail :ruleId="editRuleId" :templateRuleId="editTemplateRuleId" :titleList="titleList"
72+ @updateTemplateRuleSuccess="updateTemplateRuleSuccess" @cancelUpdateTemplateRule="cancelUpdateTemplateRule" />
73+ </div>
74+</template>
75+ 
76+<script setup lang='ts'>
77+// import { } from "@element-plus/icons-vue";
78+import "element-plus/es/components/message-box/style/index";
79+import { ref, nextTick } from 'vue'
80+import { useRequest } from "vue-request";
81+import request from "@/request";
82+import { i18n } from "@/i18n";
83+import { ElMessageBox, ElMessage } from "element-plus";
84+import { useI18n } from "vue-i18n";
85+import type { FormInstance, FormRules } from 'element-plus'
86+import TemplateRuleDetail from "@/views/alert/AlertTemplate/TemplateRuleDetail.vue";
87+const { t } = useI18n();
88+const props = withDefaults(
89+ defineProps<{
90+ templateId: number,
91+ state: string,
92+ }>(),
93+ {
94+ state: 'add'
95+ }
96+);
97+const emit = defineEmits(["updateTemplate", "cancelTemplate"]);
98+ 
99+const title = ref<string>()
100+const showMain = ref<boolean>(true)
101+const ruleTable = ref();
102+const formData = ref<any>({
103+ templateName: '',
104+ templateRuleDtoList: []
105+})
106+const tableDatas = ref<any[]>([])
107+ 
108+const selectedRuleRows = ref<any[]>([])
109+const editTemplateRuleId = ref<number>()
110+const editRuleId = ref<number>()
111+const titleList = ref<string[]>([])
112+ 
113+const formRef = ref<FormInstance>()
114+const formRules = reactive<FormRules>({
115+ templateName: [
116+ { required: true, message: t('alertTemplate.templateNamePlaceholder'), trigger: 'blur' },
117+ ]
118+})
119+ 
120+const showAlertNotify = (val: string) => {
121+ if (!val) {
122+ return ''
123+ }
124+ let arr = val.split(',');
125+ let result = arr.map(item => t(`alertRule.${item}`)).join(',')
126+ return result
127+}
128+const { data: ruleRes, run: requestRuleData } = useRequest(
129+ () => {
130+ return request.get(`/alertCenter/api/v1/alertRule/ruleList`)
131+ },
132+ { manual: true }
133+);
134+watch(ruleRes, (ruleRes: any) => {
135+ if (ruleRes && ruleRes.code === 200) {
136+ tableDatas.value = ruleRes.data
137+ let selectDatas = formData.value.templateRuleDtoList || [];
138+ if (selectDatas.length > 0) {
139+ tableDatas.value = tableDatas.value.map(item => {
140+ let datas = selectDatas.filter((item0: any) => item0.ruleId === item.ruleId) || []
141+ if (datas.length > 0) {
142+ nextTick(() => {
143+ ruleTable.value.toggleRowSelection(datas[0], true)
144+ })
145+ return datas[0]
146+ }
147+ return item;
148+ })
149+ }
150+ } else {
151+ tableDatas.value = []
152+ }
153+});
154+ 
155+const { data: templateRes, run: requestData } = useRequest(
156+ (id) => {
157+ return request.get(`/alertCenter/api/v1/alertTemplate/${id}`)
158+ },
159+ { manual: true }
160+);
161+watch(templateRes, (templateRes: any) => {
162+ if (templateRes && templateRes.code === 200) {
163+ formData.value = templateRes.data
164+ let selectDatas = formData.value.templateRuleDtoList || [];
165+ if (tableDatas.value.length > 0) {
166+ tableDatas.value = tableDatas.value.map(item => {
167+ let datas = selectDatas.filter((item0: any) => item0.ruleId === item.ruleId) || []
168+ if (datas.length > 0) {
169+ nextTick(() => {
170+ ruleTable.value.toggleRowSelection(datas[0], true)
171+ })
172+ return datas[0]
173+ }
174+ return item;
175+ })
176+ }
177+ } else {
178+ tableDatas.value = []
179+ }
180+});
181+ 
182+const confirm = () => {
183+ if (!formRef.value) return
184+ formRef.value?.validate((valid, fields) => {
185+ if (!valid) {
186+ return;
187+ }
188+ const rows = ruleTable.value.getSelectionRows() || []
189+ if (rows.length === 0) {
190+ ElMessage({
191+ message: t('app.selectDatasTip'),
192+ type: 'warning'
193+ })
194+ return;
195+ }
196+ let templateRuleReqList = rows.map((item: any) => {
197+ return { templateRuleId: item.templateRuleId, ruleId: item.ruleId }
198+ })
199+ let param = {
200+ id: formData.value.id,
201+ templateName: formData.value.templateName,
202+ templateRuleReqList
203+ }
204+ request.post(`/alertCenter/api/v1/alertTemplate`, param).then((res: any) => {
205+ if (res && res.code === 200) {
206+ ElMessage({
207+ message: t('app.saveSuccess'),
208+ type: 'success'
209+ })
210+ formData.value = {
211+ templateName: '',
212+ templateRuleDtoList: []
213+ }
214+ emit("updateTemplate")
215+ } else {
216+ ElMessage({
217+ message: t('app.saveFail'),
218+ type: 'error'
219+ })
220+ }
221+ }).catch(() => {
222+ ElMessage({
223+ message: t('app.saveFail'),
224+ type: 'error'
225+ })
226+ })
227+ })
228+}
229+const cancel = () => {
230+ formData.value = {
231+ templateName: '',
232+ templateRuleDtoList: []
233+ }
234+ emit("cancelTemplate")
235+}
236+ 
237+const edit = (templateRuleId: any, ruleId: any) => {
238+ selectedRuleRows.value = ruleTable.value.getSelectionRows() || []
239+ // let selectedRuleIdArr = selectedRuleRows.value.map((item: any) => item.ruleId) || []
240+ // if (!selectedRuleIdArr.includes(ruleId)) {
241+ // ElMessage({
242+ // message: t('app.selectUpdateDataTip'),
243+ // type: 'warning'
244+ // })
245+ // return;
246+ // }
247+ editTemplateRuleId.value = templateRuleId
248+ editRuleId.value = ruleId
249+ let curTitle = title.value || ''
250+ titleList.value = [t('alertTemplate.title'), curTitle, t('alertRule.editTitle')]
251+ showMain.value = false
252+}
253+ 
254+const updateTemplateRuleSuccess = (templateRule: any) => {
255+ for (let i = 0; i < tableDatas.value.length; i++) {
256+ if (tableDatas.value[i].ruleId === templateRule.ruleId) {
257+ tableDatas.value[i].templateRuleId = templateRule.templateRuleId
258+ tableDatas.value[i].level = templateRule.level
259+ tableDatas.value[i].ruleExpDesc = templateRule.ruleExpDesc
260+ tableDatas.value[i].ruleExpComb = templateRule.ruleExpComb
261+ tableDatas.value[i].isRepeat = templateRule.isRepeat
262+ tableDatas.value[i].isSilence = templateRule.isSilence
263+ tableDatas.value[i].isSilence = templateRule.silenceStartTime
264+ tableDatas.value[i].isSilence = templateRule.silenceEndTime
265+ }
266+ }
267+ showMain.value = true
268+ nextTick(() => {
269+ for (let row of selectedRuleRows.value) {
270+ ruleTable.value.toggleRowSelection(row, true)
271+ }
272+ })
273+}
274+ 
275+const cancelUpdateTemplateRule = () => {
276+ showMain.value = true
277+ nextTick(() => {
278+ for (let row of selectedRuleRows.value) {
279+ ruleTable.value.toggleRowSelection(row, true)
280+ }
281+ })
282+}
283+ 
284+onMounted(() => {
285+ title.value = props.state === 'add' ? t('alertTemplate.addTitle') : props.state === 'edit' ? t('alertTemplate.editTitle') : t('alertTemplate.detailTitle')
286+ requestRuleData()
287+ if (props.templateId) {
288+ requestData(props.templateId)
289+ }
290+})
291+ 
292+</script>
293+<style scoped lang='scss'></style>
Aplugins/alert-center/web-ui/src/views/alert/AlertTemplate/TemplateRuleDetail.vue+393-0
@@ -0,0 +1,393 @@
1+<template>
2+ <div>
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item v-for="item in titleList" :key="item">{{ item }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <el-descriptions :title="$t('alertRule.alertTitle')"></el-descriptions>
8+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
9+ <el-form-item :label="$t('alertRule.ruleName')" prop="ruleName">
10+ <el-input v-model="formData.ruleName" disabled
11+ :placeholder="$t('alertRule.ruleNamePlaceholder')"></el-input>
12+ </el-form-item>
13+ <el-form-item :label="$t('alertRule.ruleType')" prop="ruleType">
14+ <el-radio-group v-model="formData.ruleType" disabled>
15+ <el-radio v-for="item in ruleTypeList" :key="item" :label="item">{{ $t(`alertRule.${item}`)
16+ }}</el-radio>
17+ </el-radio-group>
18+ </el-form-item>
19+ <el-form-item :label="$t('alertRule.level')" prop="level">
20+ <el-radio-group v-model="formData.level">
21+ <el-radio v-for="item in levelList" :key="item" :label="item">{{ $t(`alertRule.${item}`) }}</el-radio>
22+ </el-radio-group>
23+ </el-form-item>
24+ <el-form-item :label="$t('alertRule.ruleItem')">
25+ <el-card class="box-card" v-for="item in formData.alertRuleItemList" :key="item.id"
26+ body-style="padding: 5px">
27+ <span style="margin: 5px 0;">{{ $t('alertRule.ruleItemNum') }}:</span><el-input v-model="item.ruleMark"
28+ disabled style="width: 30px;margin: 5px;"></el-input>
29+ <span style="margin: 5px 0 10px 5px;">{{ $t('alertRule.ruleItemExp') }}:</span>
30+ <el-select v-model="item.ruleExpName" disabled style="width: 100px;margin: 5px 0 5px 5px;">
31+ <el-option v-for="item0 in ruleItemExpList" :key="item0.name" :value="item0.name"
32+ :label="item0.i18nName" />
33+ </el-select>
34+ <span v-if="item.itemParamList && item.itemParamList.length > 0">
35+ <el-input v-model="item0.paramValue" style="width: 80px;margin: 1px;" disabled
36+ v-for="item0 in item.itemParamList" :key="item0.id"></el-input>
37+ </span>
38+ <el-select v-model="item.action" disabled style="width: 100px;margin: 5px;">
39+ <el-option v-for="item0 in actionList" :key="item0.value" :value="item0.value"
40+ :label="item0.name" />
41+ </el-select>
42+ <el-input v-model="item.operate" style="width: 50px;margin: 5px;" disabled></el-input>
43+ <el-input v-model="item.limitValue" style="width: 100px;margin: 5px;"></el-input>
44+ <span style="width: 50px;margin: 5px;">{{ item.unit }}</span>
45+ </el-card>
46+ </el-form-item>
47+ <el-form-item :label="$t('alertRule.ruleExpComb')" prop="ruleExpComb">
48+ <span v-for="item in ruleExpComb" :key="item">
49+ <el-select :value="item" v-if="showLogicSelect(item)" style="width: 50px;" disabled>
50+ <el-option v-for="item in logicSymbolList" :key="item" :value="item"
51+ :label="$t(`alertRule.${item}`)" />
52+ </el-select>
53+ <el-input :value="item" style="width: 50px;" v-else disabled></el-input>
54+ </span>
55+ </el-form-item>
56+ <el-row>
57+ <el-col :span="15">
58+ <el-form-item :label="$t('alertRule.ruleContent')" prop="ruleContent" style="margin-bottom: 0px;">
59+ <el-input v-model="formData.ruleContent" @blur="preview"></el-input>
60+ </el-form-item>
61+ </el-col>
62+ <el-col :span="1" style="margin-bottom: 0px;">
63+ <el-button @click="reset" style="display: flex;">{{ t('app.reset') }}</el-button>
64+ </el-col>
65+ <el-col :span="8" style="margin-bottom: 0px;">
66+ <div class="alert-param">
67+ <div class="title"><svg-icon name="tip" />{{ t('alertRule.alertContentTitle') }}</div>
68+ <div class="content">
69+ <span>{{ t('alertRule.alertContentTip') }}</span>
70+ <el-row style="margin-top: 10px;">
71+ <el-col :span="12" v-for="item in paramNameList" :key="item">{{ `${item}:
72+ ${alertContentParam[item]['name']}` }}</el-col>
73+ </el-row>
74+ </div>
75+ </div>
76+ </el-col>
77+ <el-col :span="15">
78+ <el-form-item>
79+ <span>{{ previewContent }}</span>
80+ </el-form-item>
81+ </el-col>
82+ </el-row>
83+ <el-form-item :label="$t('alertRule.notifyDuration')" prop="notifyDuration">
84+ <el-input v-model="formData.notifyDuration" style="width: 100px;margin-right: 5px;" disabled></el-input>
85+ <el-select v-model="formData.notifyDurationUnit" disabled style="width: 80px;">
86+ <el-option v-for="item in durationUnitList" :key="item.name" :value="item.value"
87+ :label="$t(`alertRule.${item.name}`)" />
88+ </el-select>
89+ </el-form-item>
90+ <el-form-item :label="$t('alertRule.alertDesc')" prop="alertDesc">
91+ <el-input v-model="formData.alertDesc" style="width: 50%;"></el-input>
92+ </el-form-item>
93+ </el-form>
94+ <el-descriptions :title="$t('alertRule.notifyTitle')"></el-descriptions>
95+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
96+ <el-form-item :label="$t('alertRule.alertNotify')" prop="notifyDuration">
97+ <el-checkbox-group v-model="alertNotifyList">
98+ <el-checkbox label="firing">{{ $t('alertRule.firing') }}</el-checkbox>
99+ <el-checkbox label="recover">{{ $t('alertRule.recover') }}</el-checkbox>
100+ </el-checkbox-group>
101+ </el-form-item>
102+ <el-form-item :label="$t('alertRule.repeat')" prop="isRepeat">
103+ <el-radio-group v-model="formData.isRepeat">
104+ <el-radio :label="1">{{ $t('alertRule.isRepeat') }}</el-radio>
105+ <el-radio :label="0">{{ $t('alertRule.isNotRepeat') }}</el-radio>
106+ </el-radio-group>
107+ </el-form-item>
108+ <el-form-item :label="$t('alertRule.silence')" prop="isSilence">
109+ <el-radio-group v-model="formData.isSilence">
110+ <el-radio :label="1" key="1">{{ $t('alertRule.isSilence') }}</el-radio>
111+ <el-radio :label="0" key="0">{{ $t('alertRule.isNotSilence') }}</el-radio>
112+ </el-radio-group>
113+ <span style="margin-left: 30px;" v-if="formData.isSilence === 1 || formData.isSilence === '1'">{{ $t('alertRule.silenceTime')
114+ }}</span>
115+ <span style="width: 250px;margin-left: 5px;">
116+ <el-date-picker v-if="formData.isSilence === 1 || formData.isSilence === '1'" v-model="silenceTimes" type="datetimerange"
117+ range-separator="~" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
118+ :start-placeholder="t(`alertRecord.startTimePlaceholder`)"
119+ :end-placeholder="t(`alertRecord.endTimePlaceholder`)" />
120+ </span>
121+ </el-form-item>
122+ <el-form-item :label="$t('alertRule.notifyWay')">
123+ <el-select v-model="notifyWayIdArr" multiple>
124+ <el-option v-for="item in notifyWayList" :key="item.id" :value="item.id + ''" :label="item.name" />
125+ </el-select>
126+ </el-form-item>
127+ </el-form>
128+ <el-row>
129+ <el-button type="primary" @click="confirm">{{ t('app.confirm') }}</el-button>
130+ <el-button @click="cancel">{{ t('app.cancel') }}</el-button>
131+ </el-row>
132+ </div>
133+</template>
134+ 
135+<script setup lang='ts'>
136+import "element-plus/es/components/message-box/style/index";
137+import { useRequest } from "vue-request";
138+import request from "@/request";
139+import { i18n } from "@/i18n";
140+import { ElMessageBox, ElMessage } from "element-plus";
141+import { useI18n } from "vue-i18n";
142+import SvgIcon from "@/components/SvgIcon.vue";
143+import { parseContent } from "@/utils/commonUtil"
144+const { t } = useI18n();
145+ 
146+const alertContentParam = ref<any>()
147+const paramNameList = ref<any>()
148+const previewContent = ref<string>()
149+ 
150+const props = withDefaults(
151+ defineProps<{
152+ ruleId: number,
153+ templateRuleId: number,
154+ titleList: string[]
155+ }>(),
156+ {
157+ titleList: () => []
158+ }
159+);
160+const emit = defineEmits(["updateTemplateRuleSuccess", "cancelUpdateTemplateRule"]);
161+ 
162+const ruleItemExpList = ref<any[]>()
163+const actionList = ref<any[]>([{
164+ name: t('alertRule.normalAction'), value: 'normal'
165+}, {
166+ name: t('alertRule.increaseAction'), value: 'increase'
167+}, {
168+ name: t('alertRule.decreaseAction'), value: 'decrease'
169+}])
170+const durationUnitList = ref<any[]>([{
171+ name: 'second',
172+ value: 's'
173+}, {
174+ name: 'minute',
175+ value: 'm'
176+}, {
177+ name: 'hour',
178+ value: 'h'
179+}, {
180+ name: 'day',
181+ value: 'd'
182+}])
183+const notifyWayIdArr = ref<string[]>([])
184+const notifyWayList = ref<string[]>([])
185+const logicSymbolList = ref<string[]>(['and', 'or'])
186+const formData = ref<any>({
187+ ruleName: '',
188+ ruleType: '',
189+ level: '',
190+ ruleExpComb: '',
191+ isRepeat: 1,
192+ isSilence: 0,
193+ alertRuleItemList: []
194+})
195+const ruleTypeList = reactive(['index', 'log'])
196+const levelList = reactive(['serious', 'warn', 'info'])
197+const ruleExpComb = ref<string[]>([])
198+const alertNotifyList = ref<string[]>([])
199+// const isRepeat = ref<string>('')
200+// const isSilence = ref<string>('')
201+const silenceTimes = ref<any[]>([])
202+const showLogicSelect = (logicSymbol: string) => {
203+ return logicSymbolList.value.includes(logicSymbol)
204+}
205+ 
206+const ruleContent = ref<string>('')
207+const reset = () => {
208+ formData.value.ruleContent = ruleContent.value
209+ preview()
210+}
211+ 
212+const { data: ruleDetail, run: requestData } = useRequest(
213+ (templateRuleId, ruleId) => {
214+ if (templateRuleId) {
215+ return request.get(`/alertCenter/api/v1/alertTemplate/ruleList/${templateRuleId}`)
216+ } else {
217+ return request.get(`/alertCenter/api/v1/alertRule/${ruleId}`)
218+ }
219+ },
220+ { manual: true }
221+)
222+watch(ruleDetail, (ruleDetail: any) => {
223+ if (ruleDetail && ruleDetail.code === 200) {
224+ formData.value = ruleDetail.data
225+ ruleContent.value = formData.value?.ruleContent
226+ if (formData.value?.ruleExpComb) {
227+ ruleExpComb.value = formData.value.ruleExpComb.split(',')
228+ }
229+ if (formData.value?.alertNotify) {
230+ alertNotifyList.value = formData.value.alertNotify.split(',')
231+ }
232+ if (formData.value.notifyWayIds) {
233+ notifyWayIdArr.value = formData.value.notifyWayIds.split(',')
234+ }
235+ if (formData.value.silenceStartTime && formData.value.silenceEndTime) {
236+ silenceTimes.value = [formData.value.silenceStartTime, formData.value.silenceEndTime]
237+ }
238+ if (alertContentParam.value && formData.value.ruleContent) {
239+ let param = {}
240+ let keys = Object.keys(alertContentParam.value);
241+ for (let key of keys) {
242+ param[key] = alertContentParam.value[key]['preVal']
243+ }
244+ previewContent.value = parseContent(formData.value.ruleContent, param)
245+ }
246+ } else {
247+ const msg = t("app.queryFail");
248+ ElMessage({
249+ showClose: true,
250+ message: msg,
251+ type: "error",
252+ });
253+ }
254+});
255+ 
256+const requestNotifyWayData = () => {
257+ request.get(`/alertCenter/api/v1/notifyWay/list`).then((res: any) => {
258+ if (res && res.code === 200) {
259+ notifyWayList.value = res.data
260+ }
261+ })
262+}
263+const requestRuleItemList = () => {
264+ request.get(`/alertCenter/api/v1/alertRule/ruleItem/properties`).then((res: any) => {
265+ if (res && res.code === 200) {
266+ ruleItemExpList.value = res.data
267+ }
268+ })
269+}
270+ 
271+const confirm = () => {
272+ formData.value.ruleExpComb = ruleExpComb.value.join(',')
273+ formData.value.alertNotify = alertNotifyList.value.join(',')
274+ formData.value.notifyWayIds = notifyWayIdArr.value.join(',')
275+ if (silenceTimes.value.length > 0) {
276+ formData.value.silenceStartTime = silenceTimes.value[0]
277+ formData.value.silenceEndTime = silenceTimes.value[1]
278+ }
279+ if (formData.value.alertRuleItemList && formData.value.alertRuleItemList.length > 0) {
280+ for (let row of formData.value.alertRuleItemList) {
281+ if (row.ruleId) {
282+ row.ruleId = undefined
283+ }
284+ if (row.itemParamList && row.itemParamList.length > 0) {
285+ for (let row0 of row.itemParamList) {
286+ row0.ruleItemParamId = undefined
287+ row0.itemId = undefined
288+ }
289+ }
290+ }
291+ }
292+ request.post(`/alertCenter/api/v1/alertTemplate/templateRule`, formData.value).then((res: any) => {
293+ if (res && res.code === 200) {
294+ let data = res.data
295+ emit("updateTemplateRuleSuccess", data)
296+ }
297+ })
298+}
299+const cancel = () => {
300+ emit("cancelUpdateTemplateRule")
301+}
302+ 
303+const requestAlertContentParam = () => {
304+ request.get(`/alertCenter/api/v1/environment/alertContentParam`, { type: 'alert' }).then((res: any) => {
305+ if (res && res.code === 200) {
306+ alertContentParam.value = res.data
307+ paramNameList.value = Object.keys(alertContentParam.value)
308+ if (alertContentParam.value && formData.value.ruleContent) {
309+ let param = {}
310+ let keys = Object.keys(alertContentParam.value);
311+ for (let key of keys) {
312+ param[key] = alertContentParam.value[key]['preVal']
313+ }
314+ previewContent.value = parseContent(formData.value.ruleContent, param)
315+ }
316+ }
317+ })
318+}
319+ 
320+const preview = () => {
321+ if (alertContentParam.value && formData.value.ruleContent) {
322+ let param = {}
323+ let keys = Object.keys(alertContentParam.value);
324+ for (let key of keys) {
325+ param[key] = alertContentParam.value[key]['preVal']
326+ }
327+ previewContent.value = parseContent(formData.value.ruleContent, param)
328+ }
329+}
330+ 
331+onMounted(() => {
332+ requestNotifyWayData()
333+ requestRuleItemList()
334+ requestAlertContentParam();
335+ if (props.templateRuleId || props.ruleId) {
336+ requestData(props.templateRuleId, props.ruleId)
337+ }
338+ 
339+ const wujie = window.$wujie;
340+ // Judge whether it is a plug-in environment or a local environment through wujie
341+ if (wujie) {
342+ // Monitoring platform language change
343+ wujie?.bus.$on('opengauss-locale-change', (val: string) => {
344+ console.log('log-search catch locale change');
345+ nextTick(() => {
346+ actionList.value = [{
347+ name: t('alertRule.normalAction'), value: 'normal'
348+ }, {
349+ name: t('alertRule.increaseAction'), value: 'increase'
350+ }, {
351+ name: t('alertRule.decreaseAction'), value: 'decrease'
352+ }]
353+ });
354+ });
355+ // wujie?.bus.$on('opengauss-theme-change', (val: string) => {
356+ // nextTick(() => {
357+ // requestPisData()
358+ // });
359+ // });
360+ }
361+})
362+ 
363+</script>
364+<style scoped lang='scss'>
365+.alert-param {
366+ box-sizing: border-box;
367+ display: flex;
368+ flex-direction: column;
369+ position: absolute;
370+ align-items: flex-start;
371+ padding: 9px 16px;
372+ gap: 6px;
373+ // background: #F4FAFF;
374+ border: 1px solid #94BFFF;
375+ border-radius: 2px;
376+ 
377+ .title {
378+ font-style: normal;
379+ font-weight: 800;
380+ font-size: 14px;
381+ line-height: 24px;
382+ // color: #1D2129;
383+ }
384+ 
385+ .content {
386+ font-style: normal;
387+ font-weight: 800;
388+ font-size: 12px;
389+ line-height: 18px;
390+ // color: #4E5969;
391+ }
392+}
393+</style>
Aplugins/alert-center/web-ui/src/views/notify/NotifyTemplate/Index.vue+251-0
@@ -0,0 +1,251 @@
1+<template>
2+ <div v-if="showMain">
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('notifyTemplate.title') }}</el-breadcrumb-item>
5+ </el-breadcrumb>
6+ <el-divider />
7+ <div class="search-form">
8+ <div class="filter">
9+ <el-button type="primary" @click="addTemplate">{{ $t('app.add') }}</el-button>
10+ </div>
11+ <div class="seperator"></div>
12+ <div class="filter">
13+ <span>{{ $t('notifyTemplate.templateType') }}</span>
14+ <el-select v-model="formData.notifyTemplateType" style="width: 100px;margin: 5px;" @change="changeTemplateType" clearable>
15+ <el-option v-for="item in templateTypeList" :key="item.value" :value="item.value" :label="item.name" />
16+ </el-select>
17+ </div>
18+ <div class="filter search">
19+ <el-input v-model="formData.notifyTemplateName" style="width: 200px"
20+ :placeholder="$t('notifyTemplate.templateNamePlaceholder')">
21+ <template #suffix>
22+ <el-button :icon="Search" @click="search" />
23+ </template>
24+ </el-input>
25+ </div>
26+ </div>
27+ <div>
28+ <div>
29+ <el-table size="small" :data="tableDatas" style="width: 100%" header-cell-class-name="grid-header" border>
30+ <el-table-column :label="$t('notifyTemplate.templateName')" prop="notifyTemplateName" width="100">
31+ </el-table-column>
32+ <el-table-column :label="$t('notifyTemplate.templateType')" prop="notifyTemplateType" width="100">
33+ <template #default="scope">
34+ <span>{{ $t(`notifyTemplate.${scope.row.notifyTemplateType}`) }}</span>
35+ </template>
36+ </el-table-column>
37+ <el-table-column :label="$t('notifyTemplate.notifyTitle')" prop="notifyTitle" width="100">
38+ </el-table-column>
39+ <el-table-column :label="$t('notifyTemplate.notifyContent')" prop="notifyContent" min-width="200">
40+ <template #default="scope">
41+ <!-- <span style="white-space: pre-line;">{{ scope.row.notifyContent }}</span> -->
42+ <el-popover trigger="hover" :content="scope.row.notifyContent"
43+ popper-style="min-width: 200px;white-space: pre-line;" popper-class="sql-popover-tip">
44+ <template #reference>
45+ <div style="overflow: hidden;text-overflow: ellipsis;white-space: pre-line;">
46+ {{ showSubWord(scope.row.notifyContent) }}</div>
47+ </template>
48+ </el-popover>
49+ </template>
50+ </el-table-column>
51+ <el-table-column :label="$t('notifyTemplate.templateDesc')" prop="notifyTemplateDesc" min-width="200">
52+ </el-table-column>
53+ <el-table-column :label="$t('app.operate')" width="120" fixed="right">
54+ <template #default="scope">
55+ <!-- <el-button link type="primary" size="small" @click="showDetail(scope.row.id)">{{
56+ $t('app.view')
57+ }}</el-button> -->
58+ <el-button link type="primary" size="small" @click="editTemplate(scope.row.id)">{{
59+ $t('app.edit')
60+ }}</el-button>
61+ <el-button link type="primary" size="small" @click="delTemplate(scope.row.id)">{{
62+ $t('app.delete')
63+ }}</el-button>
64+ </template>
65+ </el-table-column>
66+ </el-table>
67+ <el-pagination v-model:currentPage="page.currentPage" v-model:pageSize="page.pageSize" :total="page.total"
68+ :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small
69+ @size-change="handleSizeChange" @current-change="handleCurrentChange" />
70+ </div>
71+ </div>
72+ </div>
73+ <div v-else>
74+ <NotifyTemplateDetail :id="curId" :state="state" @updateTemplate="updateTemplate"
75+ @cancelTemplate="cancelTemplate" />
76+ </div>
77+</template>
78+ 
79+<script setup lang='ts'>
80+import { Search } from "@element-plus/icons-vue";
81+import "element-plus/es/components/message-box/style/index";
82+import { useRequest } from "vue-request";
83+import request from "@/request";
84+import { ElMessageBox, ElMessage } from "element-plus";
85+import { useI18n } from "vue-i18n";
86+import { cloneDeep } from 'lodash-es';
87+import NotifyTemplateDetail from "@/views/notify/NotifyTemplate/NotifyTemplateDetail.vue";
88+ 
89+const theme = localStorage.getItem('theme');
90+const color = ref<string>(theme === 'dark' ? '#d4d4d4' : '#1d212a')
91+const background = ref<string>(theme === 'dark' ? '#303030' : '#F5F7FB')
92+ 
93+const { t } = useI18n();
94+const initFormData = {
95+ notifyTemplateName: '',
96+ notifyTemplateType: ''
97+}
98+ 
99+const showMain = ref<boolean>(true)
100+const formData = ref<any>(cloneDeep(initFormData));
101+const tableDatas = ref<any[]>([]);
102+const page = reactive({
103+ currentPage: 1,
104+ pageSize: 10,
105+ total: 0,
106+});
107+const templateTypeList = ref<any[]>([{
108+ name: t('notifyTemplate.email'), value: 'email'
109+}, {
110+ name: t('notifyTemplate.WeCom'), value: 'WeCom'
111+}, {
112+ name: t('notifyTemplate.DingTalk'), value: 'DingTalk'
113+}])
114+const curId = ref<number>()
115+const state = ref<string>()
116+ 
117+const { data: res, run: requestData } = useRequest(
118+ () => {
119+ let params = {}
120+ params = Object.assign(params, formData.value, { pageNum: page.currentPage, pageSize: page.pageSize })
121+ return request.get("/alertCenter/api/v1/notifyTemplate", params)
122+ },
123+ { manual: true }
124+);
125+watch(res, (res: any) => {
126+ if (res && res.code === 200) {
127+ tableDatas.value = res.rows || []
128+ page.total = res.total
129+ } else {
130+ tableDatas.value = []
131+ page.total = 0
132+ const msg = t("app.queryFail");
133+ ElMessage({
134+ showClose: true,
135+ message: msg,
136+ type: "error",
137+ });
138+ }
139+});
140+const search = () => {
141+ page.currentPage = 1
142+ page.pageSize = 10
143+ requestData()
144+}
145+const changeTemplateType = () => {
146+ search()
147+}
148+const handleSizeChange = (val: number) => {
149+ page.pageSize = val
150+ requestData()
151+}
152+const handleCurrentChange = (val: number) => {
153+ page.currentPage = val
154+ requestData()
155+}
156+const addTemplate = () => {
157+ state.value = 'add'
158+ showMain.value = false
159+}
160+// const showDetail = (id: number) => {
161+// state.value = 'detail'
162+// curId.value = id
163+// showMain.value = false
164+// }
165+const editTemplate = (id: number) => {
166+ state.value = 'edit'
167+ curId.value = id
168+ showMain.value = false
169+}
170+const delTemplate = (id: number) => {
171+ ElMessageBox.confirm(
172+ t('app.deleteTip'),
173+ t('app.tip'),
174+ {
175+ confirmButtonText: t('app.confirm'),
176+ cancelButtonText: t('app.cancel'),
177+ type: 'warning',
178+ }
179+ ).then(() => {
180+ request.delete(`/alertCenter/api/v1/notifyTemplate/${id}`).then((res: any) => {
181+ if (res && res.code === 200) {
182+ ElMessage({
183+ type: 'success',
184+ message: t('app.delSuccess'),
185+ })
186+ formData.value = cloneDeep(initFormData)
187+ search()
188+ } else {
189+ ElMessage({
190+ type: 'error',
191+ message: t('app.delFail') + (res && res.msg ? ',' + res.msg : ''),
192+ })
193+ }
194+ }).catch((res: any) => {
195+ ElMessage({
196+ type: 'error',
197+ message: t('app.delFail') + (res && res.msg ? ',' + res.msg : ''),
198+ })
199+ })
200+ }).catch(() => {
201+ 
202+ })
203+}
204+const updateTemplate = () => {
205+ formData.value = cloneDeep(initFormData)
206+ search()
207+ curId.value = undefined
208+ showMain.value = true
209+}
210+const cancelTemplate = () => {
211+ curId.value = undefined
212+ showMain.value = true
213+}
214+ 
215+const showSubWord = (content: string) => {
216+ if (!content) {
217+ return ''
218+ }
219+ let contentArr = content.split('\n')
220+ if (contentArr.length > 1) {
221+ return contentArr[0] + '……'
222+ } else {
223+ return content;
224+ }
225+}
226+ 
227+onMounted(() => {
228+ requestData()
229+ 
230+ const wujie = window.$wujie;
231+ if (wujie) {
232+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
233+ nextTick(() => {
234+ color.value = theme === 'dark' ? '#d4d4d4' : '#1d212a'
235+ background.value = theme === 'dark' ? '#303030' : '#F5F7FB'
236+ });
237+ });
238+ }
239+})
240+ 
241+</script>
242+<style scoped lang='scss'>
243+.search-form {
244+ .search {
245+ :deep(.el-button--small) {
246+ background-color: v-bind(background) !important;
247+ color: v-bind(color) !important;
248+ }
249+ }
250+}
251+</style>
Aplugins/alert-center/web-ui/src/views/notify/NotifyTemplate/NotifyTemplateDetail.vue+275-0
@@ -0,0 +1,275 @@
1+<template>
2+ <div>
3+ <el-breadcrumb separator="/">
4+ <el-breadcrumb-item>{{ t('notifyTemplate.title') }}</el-breadcrumb-item>
5+ <el-breadcrumb-item v-if="state === 'add'">{{ t('notifyTemplate.addTitle') }}</el-breadcrumb-item>
6+ <el-breadcrumb-item v-if="state === 'edit'">{{ t('notifyTemplate.editTitle') }}</el-breadcrumb-item>
7+ <el-breadcrumb-item v-if="state === 'detail'">{{ t('notifyTemplate.detailTitle') }}</el-breadcrumb-item>
8+ </el-breadcrumb>
9+ <el-divider />
10+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
11+ <el-form-item :label="$t('notifyTemplate.templateName')" prop="notifyTemplateName">
12+ <el-input v-model="formData.notifyTemplateName" :placeholder="$t('notifyTemplate.templateNamePlaceholder')"
13+ :disabled="disabled"></el-input>
14+ </el-form-item>
15+ <el-form-item :label="$t('notifyTemplate.templateType')" prop="notifyTemplateType">
16+ <el-select v-model="formData.notifyTemplateType" :disabled="disabled" style="width: 100px;margin: 5px;">
17+ <el-option v-for="item in templateTypeList" :key="item.value" :value="item.value" :label="item.name" />
18+ </el-select>
19+ </el-form-item>
20+ <el-form-item :label="$t('notifyTemplate.templateDesc')" prop="notifyTemplateDesc">
21+ <el-input v-model="formData.notifyTemplateDesc" :placeholder="$t('notifyTemplate.templateDescPlaceholder')"
22+ :disabled="disabled"></el-input>
23+ </el-form-item>
24+ <el-form-item :label="$t('notifyTemplate.notifyTitle')" prop="notifyTitle">
25+ <el-input v-model="formData.notifyTitle" :placeholder="$t('notifyTemplate.notifyTitlePlaceholder')"
26+ :disabled="disabled"></el-input>
27+ </el-form-item>
28+ <!-- <el-form-item :label="$t('notifyTemplate.notifyContent')" prop="notifyContent">
29+ <el-input v-model="formData.notifyContent" type="textarea" :rows="10"
30+ :placeholder="$t('notifyTemplate.notifyContentPlaceholder')" :disabled="disabled"></el-input>
31+ </el-form-item> -->
32+ <el-row>
33+ <el-col :span="15">
34+ <el-form-item :label="$t('notifyTemplate.notifyContent')" prop="notifyContent">
35+ <el-input v-model="formData.notifyContent" type="textarea" :rows="10"
36+ :placeholder="$t('notifyTemplate.notifyContentPlaceholder')" :disabled="disabled"></el-input>
37+ </el-form-item>
38+ </el-col>
39+ <el-col :span="1">
40+ <div style="margin: 5px 0;">
41+ <el-button @click="reset">{{ t('app.reset') }}</el-button>
42+ </div>
43+ <div style="margin: 5px 0;">
44+ <el-button @click="preview">{{ t('app.preview') }}</el-button>
45+ </div>
46+ </el-col>
47+ <el-col :span="8">
48+ <div class="alert-param">
49+ <div class="title"><svg-icon name="tip" />{{ t('notifyTemplate.notifyContentTitle') }}</div>
50+ <div class="content">
51+ <span>{{ t('notifyTemplate.notifyContentTip') }}</span>
52+ <el-row style="margin-top: 10px;">
53+ <el-col :span="12" v-for="item in paramNameList" :key="item">{{ `${item}:
54+ ${alertContentParam[item]['name']}` }}</el-col>
55+ </el-row>
56+ </div>
57+ </div>
58+ </el-col>
59+ </el-row>
60+ <el-row>
61+ <el-col :span="15">
62+ <el-form-item :label="$t('notifyTemplate.previewContent')">
63+ <el-input v-model="previewContent" type="textarea" :rows="10"
64+ :placeholder="$t('notifyTemplate.notifyContentPlaceholder')" disabled></el-input>
65+ </el-form-item>
66+ </el-col>
67+ </el-row>
68+ </el-form>
69+ <el-row style="margin-top: 10px;">
70+ <el-button type="primary" @click="confirm" v-if="state !== 'detail'">{{ t('app.confirm') }}</el-button>
71+ <el-button @click="cancel">{{ t('app.cancel') }}</el-button>
72+ </el-row>
73+ </div>
74+</template>
75+ 
76+<script setup lang='ts'>
77+import "element-plus/es/components/message-box/style/index";
78+import { useRequest } from "vue-request";
79+import request from "@/request";
80+import { ElMessage } from "element-plus";
81+import { useI18n } from "vue-i18n";
82+import { cloneDeep } from 'lodash-es';
83+import type { FormInstance, FormRules } from 'element-plus'
84+import SvgIcon from "@/components/SvgIcon.vue";
85+import { parseContent } from "@/utils/commonUtil"
86+ 
87+const alertContentParam = ref<any>()
88+const paramNameList = ref<any>()
89+const previewContent = ref<string>()
90+const notifyContent = ref<string>('')
91+ 
92+const { t } = useI18n();
93+const props = withDefaults(
94+ defineProps<{
95+ id: number,
96+ state: string,
97+ }>(),
98+ {
99+ state: 'detail'
100+ }
101+)
102+ 
103+const emit = defineEmits(["updateTemplate", "cancelTemplate"])
104+ 
105+const disabled = ref<boolean>(true)
106+const initFormData = {
107+ id: '',
108+ notifyTemplateName: '',
109+ notifyTemplateType: '',
110+ notifyTemplateDesc: '',
111+ notifyTitle: '',
112+ notifyContent: ''
113+}
114+const formData = ref<any>(cloneDeep(initFormData));
115+const templateTypeList = ref<any[]>([{
116+ name: t('notifyTemplate.email'), value: 'email'
117+}, {
118+ name: t('notifyTemplate.WeCom'), value: 'WeCom'
119+}, {
120+ name: t('notifyTemplate.DingTalk'), value: 'DingTalk'
121+}])
122+ 
123+const formRef = ref<FormInstance>()
124+const formRules = reactive<FormRules>({
125+ notifyTemplateName: [
126+ { required: true, message: t('notifyTemplate.templateNamePlaceholder'), trigger: 'blur' },
127+ ],
128+ notifyTemplateType: [
129+ { required: true, message: t('notifyTemplate.templateTypePlaceholder'), trigger: 'blur' },
130+ ],
131+ notifyTitle: [
132+ { required: true, message: t('notifyTemplate.notifyTitlePlaceholder'), trigger: 'blur' },
133+ ],
134+ notifyContent: [
135+ { required: true, message: t('notifyTemplate.notifyContentPlaceholder'), trigger: 'blur' },
136+ ],
137+ 
138+})
139+ 
140+const { data: res, run: requestData } = useRequest(
141+ (id) => {
142+ return request.get(`/alertCenter/api/v1/notifyTemplate/${id}`)
143+ },
144+ { manual: true }
145+)
146+watch(res, (res: any) => {
147+ if (res && res.code === 200) {
148+ formData.value = res.data
149+ notifyContent.value = formData.value.notifyContent
150+ if (alertContentParam.value && formData.value.notifyContent) {
151+ let param = {}
152+ let keys = Object.keys(alertContentParam.value);
153+ for (let key of keys) {
154+ param[key] = alertContentParam.value[key]['preVal']
155+ }
156+ previewContent.value = parseContent(formData.value.notifyContent, param)
157+ }
158+ } else {
159+ const msg = t("app.queryFail");
160+ ElMessage({
161+ showClose: true,
162+ message: msg,
163+ type: "error",
164+ });
165+ }
166+});
167+ 
168+const confirm = () => {
169+ if (!formRef) return
170+ formRef.value?.validate((valid, fields) => {
171+ if (valid) {
172+ request.post(`/alertCenter/api/v1/notifyTemplate`, formData.value).then((res: any) => {
173+ if (res && res.code === 200) {
174+ ElMessage({
175+ message: t('app.saveSuccess'),
176+ type: 'success'
177+ })
178+ formData.value = {}
179+ formData.value = cloneDeep(initFormData)
180+ emit("updateTemplate")
181+ } else {
182+ ElMessage({
183+ message: t('app.saveFail'),
184+ type: 'error'
185+ })
186+ }
187+ }).catch(() => {
188+ ElMessage({
189+ message: t('app.saveFail'),
190+ type: 'error'
191+ })
192+ })
193+ }
194+ })
195+}
196+const cancel = () => {
197+ formData.value = {}
198+ formData.value = cloneDeep(initFormData)
199+ emit("cancelTemplate")
200+}
201+const requestNotifyContentParam = () => {
202+ request.get(`/alertCenter/api/v1/environment/alertContentParam`, { type: 'notify' }).then((res: any) => {
203+ if (res && res.code === 200) {
204+ alertContentParam.value = res.data
205+ paramNameList.value = Object.keys(alertContentParam.value)
206+ if (alertContentParam.value && formData.value.notifyContent) {
207+ let param = {}
208+ let keys = Object.keys(alertContentParam.value);
209+ for (let key of keys) {
210+ param[key] = alertContentParam.value[key]['preVal']
211+ }
212+ previewContent.value = parseContent(formData.value.notifyContent, param)
213+ }
214+ }
215+ })
216+}
217+const reset = () => {
218+ formData.value.notifyContent = notifyContent.value
219+ preview()
220+}
221+const preview = () => {
222+ if (alertContentParam.value && formData.value.notifyContent) {
223+ let param = {}
224+ let keys = Object.keys(alertContentParam.value);
225+ for (let key of keys) {
226+ param[key] = alertContentParam.value[key]['preVal']
227+ }
228+ previewContent.value = parseContent(formData.value.notifyContent, param)
229+ } else {
230+ previewContent.value = ''
231+ }
232+}
233+ 
234+onMounted(() => {
235+ requestNotifyContentParam()
236+ if (props.id) {
237+ requestData(props.id)
238+ }
239+ if (props.state !== 'detail') {
240+ disabled.value = false
241+ } else {
242+ disabled.value = true
243+ }
244+})
245+</script>
246+<style scoped lang='scss'>
247+.alert-param {
248+ box-sizing: border-box;
249+ display: flex;
250+ flex-direction: column;
251+ position: absolute;
252+ align-items: flex-start;
253+ padding: 9px 16px;
254+ gap: 6px;
255+ // background: #F4FAFF;
256+ border: 1px solid #94BFFF;
257+ border-radius: 2px;
258+ 
259+ .title {
260+ font-style: normal;
261+ font-weight: 800;
262+ font-size: 14px;
263+ line-height: 24px;
264+ // color: #1D2129;
265+ }
266+ 
267+ .content {
268+ font-style: normal;
269+ font-weight: 400;
270+ font-size: 12px;
271+ line-height: 18px;
272+ // color: #4E5969;
273+ }
274+}
275+</style>
Aplugins/alert-center/web-ui/src/views/notify/NotifyWay/Index.vue+45-0
@@ -0,0 +1,45 @@
1+<template>
2+ <el-breadcrumb separator="/">
3+ <el-breadcrumb-item v-if="activeName === 'notifyWay'">{{ $t('notifyWay.title') }}</el-breadcrumb-item>
4+ <el-breadcrumb-item v-if="activeName === 'notifyConfig'">{{ $t('notifyConfig.title') }}</el-breadcrumb-item>
5+ <el-breadcrumb-item v-if="state === 'addNotifyWay'">{{ $t('notifyWay.addTitle') }}</el-breadcrumb-item>
6+ <el-breadcrumb-item v-if="state === 'editNotifyWay'">{{ $t('notifyWay.editTitle') }}</el-breadcrumb-item>
7+ <el-breadcrumb-item v-if="state === 'detailNotifyWay'">{{ $t('notifyWay.detailTitle') }}</el-breadcrumb-item>
8+ </el-breadcrumb>
9+ <el-divider />
10+ <el-tabs v-model="activeName" @tab-click="handleClick">
11+ <el-tab-pane :label="$t('notifyWay.title')" name="notifyWay">
12+ <NotifyWay @updateState="updateState"/>
13+ </el-tab-pane>
14+ <el-tab-pane :label="$t('notifyConfig.title')" name="notifyConfig">
15+ <NotifyConfig />
16+ </el-tab-pane>
17+ </el-tabs>
18+</template>
19+ 
20+<script setup lang='ts'>
21+import "element-plus/es/components/message-box/style/index";
22+import { useI18n } from "vue-i18n";
23+import NotifyWay from "@/views/notify/NotifyWay/NotifyWay.vue";
24+import NotifyConfig from "@/views/notify/NotifyWay/NotifyConfig.vue";
25+ 
26+const { t } = useI18n();
27+ 
28+const activeName = ref<string>('notifyWay')
29+const state = ref<string>('')
30+ 
31+const updateState = (val: string) => {
32+ state.value = val
33+}
34+ 
35+const handleClick = () => {
36+ if (activeName.value === 'notifyConfig') {
37+ state.value = ''
38+ }
39+}
40+ 
41+onMounted(() => {
42+})
43+ 
44+</script>
45+<style scoped lang='scss'></style>
Aplugins/alert-center/web-ui/src/views/notify/NotifyWay/NotifyConfig.vue+375-0
@@ -0,0 +1,375 @@
1+<template>
2+ <div>
3+ <el-row>
4+ <el-col :span="2">
5+ <el-checkbox v-model="emailConfig.enable" :true-label="1" :false-label="0">{{ $t('notifyConfig.emailConfig')
6+ }}</el-checkbox>
7+ </el-col>
8+ <el-col :span="20" class="form-inline">
9+ <el-form :inline="true" :model="emailConfig" ref="emailForm" :rules="emailRules" label-position="right" label-width="100px">
10+ <el-form-item :label="$t('notifyConfig.senderEmail')" prop="email">
11+ <el-input v-model="emailConfig.email" :disabled="emailConfig.enable === 0"
12+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.senderEmail')" />
13+ </el-form-item>
14+ <el-form-item :label="$t('notifyConfig.sender')" prop="sender">
15+ <el-input v-model="emailConfig.sender" :disabled="emailConfig.enable === 0"
16+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.sender')" />
17+ </el-form-item>
18+ <el-form-item :label="$t('notifyConfig.sever')" prop="sever">
19+ <el-input v-model="emailConfig.sever" :disabled="emailConfig.enable === 0"
20+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.sever')" />
21+ </el-form-item>
22+ <el-form-item :label="$t('notifyConfig.port')" prop="port">
23+ <el-input v-model="emailConfig.port" :disabled="emailConfig.enable === 0"
24+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.port')" />
25+ </el-form-item>
26+ <el-form-item :label="$t('notifyConfig.account')" prop="account">
27+ <el-input v-model="emailConfig.account" :disabled="emailConfig.enable === 0"
28+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.account')" />
29+ </el-form-item>
30+ <el-form-item :label="$t('notifyConfig.passwd')" prop="passwd">
31+ <el-input v-model="emailConfig.passwd" :disabled="emailConfig.enable === 0"
32+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.passwd')" />
33+ </el-form-item>
34+ </el-form>
35+ </el-col>
36+ <el-col :span="2">
37+ <el-button style="margin: 5px 10px" type="info" @click="testNotify('email')">{{ t('notifyConfig.test')
38+ }}</el-button>
39+ </el-col>
40+ </el-row>
41+ <el-row style="margin-top: 10px;">
42+ <el-col :span="2">
43+ <el-checkbox v-model="weComConfig.enable" :true-label="1" :false-label="0">{{ t('notifyConfig.weComConfig')
44+ }}</el-checkbox>
45+ </el-col>
46+ <el-col :span="20" class="form-inline">
47+ <el-form :inline="true" :model="weComConfig" ref="weComForm" :rules="weComRules" label-position="right" label-width="100px">
48+ <el-form-item :label="$t('notifyConfig.weComAppKey')" prop="appKey">
49+ <el-input v-model="weComConfig.appKey" :disabled="weComConfig.enable === 0"
50+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.weComAppKey')" />
51+ </el-form-item>
52+ <el-form-item :label="$t('notifyConfig.agentId')" prop="agentId">
53+ <el-input v-model="weComConfig.agentId" :disabled="weComConfig.enable === 0"
54+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.agentId')" />
55+ </el-form-item>
56+ <el-form-item :label="$t('notifyConfig.weComSecret')" prop="secret">
57+ <el-input v-model="weComConfig.secret" :disabled="weComConfig.enable === 0"
58+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.weComSecret')" />
59+ </el-form-item>
60+ </el-form>
61+ </el-col>
62+ <el-col :span="2">
63+ <el-button style="margin: 5px 10px" type="info">{{ t('notifyConfig.test') }}</el-button>
64+ </el-col>
65+ </el-row>
66+ <el-row style="margin-top: 10px;">
67+ <el-col :span="2">
68+ <el-checkbox v-model="dingTalkConfig.enable" :true-label="1" :false-label="0">{{
69+ t('notifyConfig.dingTalkConfig') }}</el-checkbox>
70+ </el-col>
71+ <el-col :span="20" class="form-inline">
72+ <el-form :inline="true" :model="dingTalkConfig" ref="dingTalkForm" :rules="dingTalkRules" label-position="right" label-width="100px">
73+ <el-form-item :label="$t('notifyConfig.appKey')" prop="appKey">
74+ <el-input v-model="dingTalkConfig.appKey" :disabled="dingTalkConfig.enable === 0"
75+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.appKey')" />
76+ </el-form-item>
77+ <el-form-item :label="$t('notifyConfig.agentId')" prop="agentId">
78+ <el-input v-model="dingTalkConfig.agentId" :disabled="dingTalkConfig.enable === 0"
79+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.agentId')" />
80+ </el-form-item>
81+ <el-form-item :label="$t('notifyConfig.dingSecret')" prop="secret">
82+ <el-input v-model="dingTalkConfig.secret" :disabled="dingTalkConfig.enable === 0"
83+ :placeholder="$t('notifyConfig.inputTip') + $t('notifyConfig.dingSecret')" />
84+ </el-form-item>
85+ </el-form>
86+ </el-col>
87+ <el-col :span="2">
88+ <el-button style="margin: 5px 10px" type="info">{{ t('notifyConfig.test') }}</el-button>
89+ </el-col>
90+ </el-row>
91+ <el-row style="margin-top: 10px;">
92+ <el-button type="primary" @click="confirm">{{ t('app.confirm') }}</el-button>
93+ </el-row>
94+ <el-dialog v-model="dialogVisible" width="30%" :title="t('notifyConfig.test')" :before-close="handleClose">
95+ <el-form-item :label="$t('alertRule.notifyWay')">
96+ <el-select v-model="notifyWayId">
97+ <el-option v-for="item in notifyWayList" :key="item.id" :value="item.id" :label="item.name" />
98+ </el-select>
99+ </el-form-item>
100+ <template #footer>
101+ <span class="dialog-footer">
102+ <el-button @click="handleClose">{{ t('app.cancel') }}</el-button>
103+ <el-button type="primary" @click="testNotifyConfig">
104+ {{ t('app.confirm') }}
105+ </el-button>
106+ </span>
107+ </template>
108+ </el-dialog>
109+ </div>
110+</template>
111+ 
112+<script setup lang='ts'>
113+import { ElLoading } from 'element-plus'
114+import "element-plus/es/components/message-box/style/index";
115+import { useRequest } from "vue-request";
116+import request from "@/request";
117+import { ElMessageBox, ElMessage } from "element-plus";
118+import { useI18n } from "vue-i18n";
119+import { cloneDeep } from 'lodash-es';
120+import type { FormInstance, FormRules } from 'element-plus'
121+const { t } = useI18n();
122+ 
123+const theme = localStorage.getItem('theme');
124+const color = ref<string>(theme === 'dark' ? '#d4d4d4' : '#1d212a')
125+const background = ref<string>(theme === 'dark' ? '#303030' : '#F5F7FB')
126+ 
127+const baseConfig = {
128+ id: null,
129+ type: '',
130+ email: '',
131+ sender: '',
132+ sever: '',
133+ port: null,
134+ account: '',
135+ passwd: '',
136+ agentId: '',
137+ appKey: '',
138+ secret: '',
139+ enable: 0
140+}
141+const emailConfig = ref<any>(Object.assign(cloneDeep(baseConfig), { type: 'email' }))
142+const weComConfig = ref<any>(Object.assign(cloneDeep(baseConfig), { type: 'WeCom' }))
143+const dingTalkConfig = ref<any>(Object.assign(cloneDeep(baseConfig), { type: 'DingTalk' }))
144+ 
145+const dialogVisible = ref<boolean>(false)
146+const notifyWayId = ref<any>();
147+const notifyWayList = ref<any[]>([])
148+const curType = ref<any>();
149+ 
150+const emailForm = ref<FormInstance>()
151+const emailRules = reactive<FormRules>({
152+ email: [
153+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.email'), trigger: 'blur' },
154+ ],
155+ sender: [
156+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.sender'), trigger: 'blur' },
157+ ],
158+ sever: [
159+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.sever'), trigger: 'blur' },
160+ ],
161+ port: [
162+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.port'), trigger: 'blur' },
163+ ],
164+ account: [
165+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.account'), trigger: 'blur' },
166+ ],
167+ passwd: [
168+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.passwd'), trigger: 'blur' },
169+ ]
170+})
171+const weComForm = ref<FormInstance>()
172+const weComRules = reactive<FormRules>({
173+ appKey: [
174+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.weComAppKey'), trigger: 'blur' },
175+ ],
176+ agentId: [
177+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.agentId'), trigger: 'blur' },
178+ ],
179+ secret: [
180+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.weComSecret'), trigger: 'blur' },
181+ ]
182+})
183+const dingTalkForm = ref<FormInstance>()
184+const dingTalkRules = reactive<FormRules>({
185+ appKey: [
186+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.appKey'), trigger: 'blur' },
187+ ],
188+ agentId: [
189+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.agentId'), trigger: 'blur' },
190+ ],
191+ secret: [
192+ { required: true, message: t('notifyConfig.inputTip') + t('notifyConfig.dingSecret'), trigger: 'blur' },
193+ ]
194+})
195+ 
196+const { data: res, run: requestData } = useRequest(
197+ () => {
198+ return request.get("/alertCenter/api/v1/notifyConfig/list")
199+ },
200+ { manual: true }
201+);
202+watch(res, (res: any) => {
203+ if (res && res.code === 200) {
204+ let list = res.data || []
205+ let emailList = list.filter((item: any) => item.type === 'email') || []
206+ if (emailList.length > 0) {
207+ emailConfig.value = emailList[0]
208+ }
209+ let weComList = list.filter((item: any) => item.type === 'WeCom') || []
210+ if (weComList.length > 0) {
211+ weComConfig.value = weComList[0]
212+ }
213+ let dingTalkList = list.filter((item: any) => item.type === 'DingTalk') || []
214+ if (dingTalkList.length > 0) {
215+ dingTalkConfig.value = dingTalkList[0]
216+ }
217+ } else {
218+ const msg = t("app.queryFail");
219+ ElMessage({
220+ showClose: true,
221+ message: msg,
222+ type: "error",
223+ });
224+ }
225+});
226+const requestNotifyWayData = (notifyType: string) => {
227+ request.get(`/alertCenter/api/v1/notifyWay/list`, { notifyType }).then((res: any) => {
228+ if (res && res.code === 200) {
229+ notifyWayList.value = res.data
230+ }
231+ })
232+}
233+const testNotify = (type: string) => {
234+ dialogVisible.value = true
235+ notifyWayId.value = null
236+ curType.value = type
237+ requestNotifyWayData(type)
238+}
239+const loading = ref(false) // loading
240+const openLoading = () => {
241+ loading.value = ElLoading.service({
242+ lock: true,
243+ text: 'Loading',
244+ background: 'rgba(0, 0, 0, 0.7)'
245+ })
246+}
247+const closeLoading = () => {
248+ loading.value.close()
249+}
250+const testNotifyConfig = () => {
251+ let param = {}
252+ if (curType.value === 'email') {
253+ param = Object.assign(param, emailConfig.value)
254+ } else if (curType.value === 'WeCom') {
255+ param = Object.assign(param, weComConfig.value)
256+ } else if (curType.value === 'DingTalk') {
257+ param = Object.assign(param, dingTalkConfig.value)
258+ }
259+ param = Object.assign(param, { notifyWayId: notifyWayId.value })
260+ openLoading()
261+ request.post(`/alertCenter/api/v1/notifyConfig/testConfig`, param).then((res: any) => {
262+ if (res && res.code === 200) {
263+ ElMessage({
264+ message: t('app.testSuccess'),
265+ type: 'success'
266+ })
267+ } else {
268+ ElMessage({
269+ message: t('app.testFail'),
270+ type: 'error'
271+ })
272+ }
273+ closeLoading()
274+ }).catch(() => {
275+ ElMessage({
276+ message: t('app.testFail'),
277+ type: 'error'
278+ })
279+ closeLoading()
280+ })
281+}
282+const handleClose = () => {
283+ dialogVisible.value = false
284+ notifyWayId.value = null
285+ curType.value = null
286+}
287+ 
288+const confirm = async() => {
289+ if (emailConfig.value.enable === 1) {
290+ let valid = await emailForm.value?.validate(async(valid, fields) => {
291+ return valid
292+ })
293+ if (!valid) {
294+ return
295+ }
296+ }
297+ if (weComConfig.value.enable === 1) {
298+ let valid = await weComForm.value?.validate(async(valid, fields) => {
299+ return valid
300+ })
301+ if (!valid) {
302+ return
303+ }
304+ }
305+ if (dingTalkConfig.value.enable === 1) {
306+ let valid = await dingTalkForm.value?.validate(async(valid, fields) => {
307+ return valid
308+ })
309+ if (!valid) {
310+ return
311+ }
312+ }
313+ let params = [emailConfig.value, weComConfig.value, dingTalkConfig.value]
314+ request.post(`/alertCenter/api/v1/notifyConfig/list`, params).then((res: any) => {
315+ if (res && res.code === 200) {
316+ ElMessage({
317+ message: t('app.saveSuccess'),
318+ type: 'success'
319+ })
320+ requestData()
321+ } else {
322+ ElMessage({
323+ message: t('app.saveFail'),
324+ type: 'error'
325+ })
326+ }
327+ }).catch(() => {
328+ ElMessage({
329+ message: t('app.saveFail'),
330+ type: 'error'
331+ })
332+ })
333+}
334+onMounted(() => {
335+ requestData()
336+ 
337+ const wujie = window.$wujie;
338+ if (wujie) {
339+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
340+ nextTick(() => {
341+ color.value = theme === 'dark' ? '#d4d4d4' : '#1d212a'
342+ background.value = theme === 'dark' ? '#303030' : '#F5F7FB'
343+ });
344+ });
345+ }
346+})
347+</script>
348+<style scoped lang='scss'>
349+.form-inline {
350+ box-sizing: border-box;
351+ color: v-bind(color);
352+ background: v-bind(background);
353+ // background: #FBFBFB;
354+ 
355+ border: 1px solid #EAEBEE;
356+ border-radius: 2px;
357+}
358+ 
359+.el-form-item--small {
360+ margin: 10px;
361+}
362+ 
363+.testBtn {
364+ box-sizing: border-box;
365+ justify-content: center;
366+ align-items: center;
367+ width: 60px;
368+ height: 32px;
369+ background: #FFFFFF;
370+ /* 边框/配置项按钮边框 */
371+ border: 1px solid #D9D9D9;
372+ border-radius: 2px;
373+ margin: 5px 16px;
374+}
375+</style>
Aplugins/alert-center/web-ui/src/views/notify/NotifyWay/NotifyWay.vue+237-0
@@ -0,0 +1,237 @@
1+<template>
2+ <div v-if="showMain">
3+ <div class="search-form">
4+ <div class="filter">
5+ <el-button type="primary" @click="addTemplate">{{ $t('app.add') }}</el-button>
6+ </div>
7+ <div class="seperator"></div>
8+ <div class="filter">
9+ <span>{{ $t('notifyWay.notifyType') }}</span>
10+ <el-select v-model="formData.notifyType" style="width: 100px;margin: 5px;"
11+ @change="changeNotifyType" clearable>
12+ <el-option v-for="item in templateTypeList" :key="item.value" :value="item.value" :label="item.name" />
13+ </el-select>
14+ </div>
15+ <div class="filter search">
16+ <el-input v-model="formData.name" style="width: 200px"
17+ :placeholder="$t('notifyWay.namePlaceholder')">
18+ <template #suffix>
19+ <el-button :icon="Search" @click="search" />
20+ </template>
21+ </el-input>
22+ </div>
23+ </div>
24+ <div>
25+ <div>
26+ <el-table size="small" :data="tableDatas" style="width: 100%" header-cell-class-name="grid-header" border>
27+ <el-table-column :label="$t('notifyWay.name')" prop="name">
28+ </el-table-column>
29+ <el-table-column :label="$t('notifyWay.notifyType')" prop="notifyType">
30+ <template #default="scope">
31+ <span>{{ $t(`notifyTemplate.${scope.row.notifyType}`) }}</span>
32+ </template>
33+ </el-table-column>
34+ <el-table-column :label="$t('notifyWay.notifyTarget')" min-width="200">
35+ <template #default="scope">
36+ <div v-if="scope.row.notifyType === 'email'">{{ scope.row.email }}</div>
37+ <div v-if="scope.row.notifyType === 'WeCom' || scope.row.notifyType === 'DingTalk'">
38+ <div v-if="scope.row.personId">{{ $t('notifyWay.personId') + ': ' + scope.row.personId }}</div>
39+ <div v-if="scope.row.deptId">{{ $t('notifyWay.deptId') + ': ' + scope.row.deptId }}</div>
40+ </div>
41+ </template>
42+ </el-table-column>
43+ <el-table-column :label="$t('notifyTemplate.templateName')" prop="notifyTemplateName">
44+ </el-table-column>
45+ <el-table-column :label="$t('app.operate')" width="120" fixed="right">
46+ <template #default="scope">
47+ <!-- <el-button link type="primary" size="small" @click="showDetail(scope.row.id)">{{
48+ $t('app.view')
49+ }}</el-button> -->
50+ <el-button link type="primary" size="small" @click="editTemplate(scope.row.id)">{{
51+ $t('app.edit')
52+ }}</el-button>
53+ <el-button link type="primary" size="small" @click="delTemplate(scope.row.id)">{{
54+ $t('app.delete')
55+ }}</el-button>
56+ </template>
57+ </el-table-column>
58+ </el-table>
59+ <el-pagination v-model:currentPage="page.currentPage" v-model:pageSize="page.pageSize" :total="page.total"
60+ :page-sizes="[10, 20, 30, 40]" class="pagination" layout="total,sizes,prev,pager,next" background small
61+ @size-change="handleSizeChange" @current-change="handleCurrentChange" />
62+ </div>
63+ </div>
64+ </div>
65+ <div v-else>
66+ <NotifyWayDetail :id="curId" :state="state" @updateNotifyWay="updateNotifyWay"
67+ @cancelNotifyWay="cancelNotifyWay" />
68+ </div>
69+</template>
70+ 
71+<script setup lang='ts'>
72+import { Search } from "@element-plus/icons-vue";
73+import "element-plus/es/components/message-box/style/index";
74+import { useRequest } from "vue-request";
75+import request from "@/request";
76+import { ElMessageBox, ElMessage } from "element-plus";
77+import { useI18n } from "vue-i18n";
78+import { cloneDeep } from 'lodash-es';
79+import NotifyWayDetail from "@/views/notify/NotifyWay/NotifyWayDetail.vue";
80+ 
81+const emit = defineEmits(["updateState"])
82+ 
83+const theme = localStorage.getItem('theme');
84+const color = ref<string>(theme === 'dark' ? '#d4d4d4' : '#1d212a')
85+const background = ref<string>(theme === 'dark' ? '#303030' : '#F5F7FB')
86+ 
87+const { t } = useI18n();
88+const initFormData = {
89+ name: '',
90+ notifyType: ''
91+}
92+ 
93+const showMain = ref<boolean>(true)
94+const formData = ref<any>(cloneDeep(initFormData));
95+const tableDatas = ref<any[]>([]);
96+const page = reactive({
97+ currentPage: 1,
98+ pageSize: 10,
99+ total: 0,
100+});
101+const templateTypeList = ref<any[]>([{
102+ name: t('notifyTemplate.email'), value: 'email'
103+}, {
104+ name: t('notifyTemplate.WeCom'), value: 'WeCom'
105+}, {
106+ name: t('notifyTemplate.DingTalk'), value: 'DingTalk'
107+}])
108+const curId = ref<number>()
109+const state = ref<string>()
110+ 
111+const { data: res, run: requestData } = useRequest(
112+ () => {
113+ let params = {}
114+ params = Object.assign(params, formData.value, { pageNum: page.currentPage, pageSize: page.pageSize })
115+ return request.get("/alertCenter/api/v1/notifyWay", params)
116+ },
117+ { manual: true }
118+);
119+watch(res, (res: any) => {
120+ if (res && res.code === 200) {
121+ tableDatas.value = res.rows || []
122+ page.total = res.total
123+ } else {
124+ tableDatas.value = []
125+ page.total = 0
126+ const msg = t("app.queryFail");
127+ ElMessage({
128+ showClose: true,
129+ message: msg,
130+ type: "error",
131+ });
132+ }
133+});
134+const search = () => {
135+ page.currentPage = 1
136+ page.pageSize = 10
137+ requestData()
138+}
139+const changeNotifyType = () => {
140+ search()
141+}
142+const handleSizeChange = (val: number) => {
143+ page.pageSize = val
144+ requestData()
145+}
146+const handleCurrentChange = (val: number) => {
147+ page.currentPage = val
148+ requestData()
149+}
150+const addTemplate = () => {
151+ emit('updateState', 'addNotifyWay')
152+ state.value = 'add'
153+ showMain.value = false
154+}
155+// const showDetail = (id: number) => {
156+// state.value = 'detail'
157+// curId.value = id
158+// showMain.value = false
159+// }
160+const editTemplate = (id: number) => {
161+ emit('updateState', 'editNotifyWay')
162+ curId.value = id
163+ state.value = 'edit'
164+ showMain.value = false
165+}
166+const delTemplate = (id: number) => {
167+ ElMessageBox.confirm(
168+ t('app.deleteTip'),
169+ t('app.tip'),
170+ {
171+ confirmButtonText: t('app.confirm'),
172+ cancelButtonText: t('app.cancel'),
173+ type: 'warning',
174+ }
175+ ).then(() => {
176+ request.delete(`/alertCenter/api/v1/notifyWay/${id}`).then((res: any) => {
177+ if (res && res.code === 200) {
178+ ElMessage({
179+ type: 'success',
180+ message: t('app.delSuccess'),
181+ })
182+ formData.value = cloneDeep(initFormData)
183+ search()
184+ } else {
185+ ElMessage({
186+ type: 'error',
187+ message: t('app.delFail') + (res && res.msg ? ',' + res.msg : ''),
188+ })
189+ }
190+ }).catch((res: any) => {
191+ ElMessage({
192+ type: 'error',
193+ message: t('app.delFail') + (res && res.msg ? ',' + res.msg : ''),
194+ })
195+ })
196+ }).catch(() => {
197+ 
198+ })
199+}
200+const updateNotifyWay = () => {
201+ formData.value = cloneDeep(initFormData)
202+ search()
203+ curId.value = undefined
204+ emit('updateState', '')
205+ showMain.value = true
206+}
207+const cancelNotifyWay = () => {
208+ curId.value = undefined
209+ emit('updateState', '')
210+ showMain.value = true
211+}
212+ 
213+onMounted(() => {
214+ requestData()
215+ 
216+ const wujie = window.$wujie;
217+ if (wujie) {
218+ wujie?.bus.$on('opengauss-theme-change', (val: string) => {
219+ nextTick(() => {
220+ color.value = theme === 'dark' ? '#d4d4d4' : '#1d212a'
221+ background.value = theme === 'dark' ? '#303030' : '#F5F7FB'
222+ });
223+ });
224+ }
225+})
226+ 
227+</script>
228+<style scoped lang='scss'>
229+.search-form {
230+ .search {
231+ :deep(.el-button--small) {
232+ background-color: v-bind(background) !important;
233+ color: v-bind(color) !important;
234+ }
235+ }
236+}
237+</style>
Aplugins/alert-center/web-ui/src/views/notify/NotifyWay/NotifyWayDetail.vue+218-0
@@ -0,0 +1,218 @@
1+<template>
2+ <div>
3+ <el-form :model="formData" :rules="formRules" ref="formRef" label-position="right" label-width="100px">
4+ <el-form-item :label="$t('notifyWay.name')" prop="name">
5+ <el-input v-model="formData.name" :placeholder="$t('notifyWay.namePlaceholder')"
6+ :disabled="disabled"></el-input>
7+ </el-form-item>
8+ <el-form-item :label="$t('notifyWay.notifyType')" prop="notifyType">
9+ <el-select v-model="formData.notifyType" :disabled="disabled" style="margin: 5px;"
10+ @change="notifyTypeChange">
11+ <el-option v-for="item in templateTypeList" :key="item.value" :value="item.value" :label="item.name" />
12+ </el-select>
13+ </el-form-item>
14+ <el-form-item :label="$t('notifyTemplate.email')" prop="email" v-if="formData.notifyType === 'email'"
15+ :required="true">
16+ <el-input v-model="formData.email" :placeholder="$t('notifyWay.emailPlaceholder')"
17+ :disabled="disabled"></el-input>
18+ </el-form-item>
19+ <el-form-item :label="$t('notifyWay.personId')"
20+ v-if="formData.notifyType === 'WeCom' || formData.notifyType === 'DingTalk'">
21+ <el-input v-model="formData.personId" :placeholder="$t('notifyWay.personIdPlaceholder')"
22+ :disabled="disabled"></el-input>
23+ </el-form-item>
24+ <el-form-item :label="$t('notifyWay.deptId')"
25+ v-if="formData.notifyType === 'WeCom' || formData.notifyType === 'DingTalk'">
26+ <el-input v-model="formData.deptId" :placeholder="$t('notifyWay.deptIdPlaceholder')"
27+ :disabled="disabled"></el-input>
28+ </el-form-item>
29+ <el-form-item
30+ :label="formData.notifyType === 'email' ? $t('notifyWay.emailTemplate') : formData.notifyType === 'WeCom' ? $t('notifyWay.WeComTemplate') : formData.notifyType === 'DingTalk' ? $t('notifyWay.DingTalkTemplate') : ''"
31+ prop="notifyTemplateId" v-if="formData.notifyType">
32+ <el-select v-model="formData.notifyTemplateId" :disabled="disabled" style="margin: 5px;">
33+ <el-option v-for="item in templateList" :key="item.id" :value="item.id"
34+ :label="item.notifyTemplateName" />
35+ </el-select>
36+ </el-form-item>
37+ </el-form>
38+ <el-row style="margin-top: 10px;">
39+ <el-button type="primary" @click="confirm" v-if="state !== 'detail'">{{ t('app.confirm') }}</el-button>
40+ <el-button @click="cancel">{{ t('app.cancel') }}</el-button>
41+ </el-row>
42+ </div>
43+</template>
44+ 
45+<script setup lang='ts'>
46+import "element-plus/es/components/message-box/style/index";
47+import { useRequest } from "vue-request";
48+import request from "@/request";
49+import { ElMessage } from "element-plus";
50+import { useI18n } from "vue-i18n";
51+import { cloneDeep } from 'lodash-es';
52+import type { FormInstance, FormRules } from 'element-plus'
53+ 
54+const validateEmailNotNull = (rule: any, value: any, callback: any) => {
55+ if (formData.value.notifyType === 'email') {
56+ if (!value) {
57+ callback(new Error(t('notifyWay.emailPlaceholder')))
58+ }
59+ }
60+ callback()
61+}
62+const { t } = useI18n();
63+const props = withDefaults(
64+ defineProps<{
65+ id: number,
66+ state: string,
67+ }>(),
68+ {
69+ state: 'detail',
70+ }
71+)
72+ 
73+const emit = defineEmits(["updateNotifyWay", "cancelNotifyWay"])
74+ 
75+const disabled = ref<boolean>(true)
76+const initFormData = {
77+ id: '',
78+ name: '',
79+ notifyType: '',
80+ email: '',
81+ personId: '',
82+ deptId: '',
83+ notifyTemplateId: null
84+}
85+const formData = ref<any>(cloneDeep(initFormData));
86+const templateTypeList = ref<any[]>([{
87+ name: t('notifyTemplate.email'), value: 'email'
88+}, {
89+ name: t('notifyTemplate.WeCom'), value: 'WeCom'
90+}, {
91+ name: t('notifyTemplate.DingTalk'), value: 'DingTalk'
92+}])
93+const templateList = ref<any[]>([])
94+ 
95+const formRef = ref<FormInstance>()
96+const formRules = reactive<FormRules>({
97+ name: [
98+ { required: true, message: t('notifyWay.namePlaceholder'), trigger: 'blur' },
99+ ],
100+ notifyType: [
101+ { required: true, message: t('notifyWay.notifyTypePlaceholder'), trigger: 'blur' },
102+ ],
103+ email: [
104+ { validator: validateEmailNotNull, message: t('notifyWay.emailPlaceholder'), trigger: 'blur' },
105+ ],
106+ notifyTemplateId: [
107+ { required: true, message: t('notifyWay.templatePlaceholder'), trigger: 'blur' },
108+ ],
109+ 
110+})
111+ 
112+const { data: res, run: requestData } = useRequest(
113+ (id) => {
114+ return request.get(`/alertCenter/api/v1/notifyWay/${id}`)
115+ },
116+ { manual: true }
117+)
118+watch(res, (res: any) => {
119+ if (res && res.code === 200) {
120+ formData.value = res.data
121+ if (formData.value.notifyType) {
122+ requestTemplateData(formData.value.notifyType)
123+ }
124+ } else {
125+ const msg = t("app.queryFail");
126+ ElMessage({
127+ showClose: true,
128+ message: msg,
129+ type: "error",
130+ });
131+ }
132+});
133+ 
134+const { data: templateRes, run: requestTemplateData } = useRequest(
135+ (notifyTemplateType) => {
136+ return request.get(`/alertCenter/api/v1/notifyTemplate/list`, { notifyTemplateType })
137+ },
138+ { manual: true }
139+)
140+watch(templateRes, (templateRes: any) => {
141+ if (templateRes && templateRes.code === 200) {
142+ templateList.value = templateRes.data || []
143+ } else {
144+ const msg = t("app.queryFail");
145+ ElMessage({
146+ showClose: true,
147+ message: msg,
148+ type: "error",
149+ });
150+ }
151+});
152+ 
153+const notifyTypeChange = () => {
154+ formData.notifyTemplateId = null
155+ if (!formData.value.notifyType) {
156+ templateList.value = []
157+ return
158+ }
159+ formData.value.email = ''
160+ formData.value.personId = ''
161+ formData.value.deptId = ''
162+ requestTemplateData(formData.value.notifyType)
163+}
164+ 
165+const confirm = () => {
166+ if (!formRef) return
167+ formRef.value?.validate((valid, fields) => {
168+ if (valid) {
169+ if ((formData.value.notifyType === 'WeCom' || formData.value.notifyType === 'DingTalk') && !formData.value.personId && !formData.value.deptId) {
170+ const msg = t("notifyWay.existOneOfTargetTip");
171+ ElMessage({
172+ showClose: true,
173+ message: msg,
174+ type: "error",
175+ });
176+ return
177+ }
178+ request.post(`/alertCenter/api/v1/notifyWay`, formData.value).then((res: any) => {
179+ if (res && res.code === 200) {
180+ ElMessage({
181+ message: t('app.saveSuccess'),
182+ type: 'success'
183+ })
184+ formData.value = {}
185+ formData.value = cloneDeep(initFormData)
186+ emit("updateNotifyWay")
187+ } else {
188+ ElMessage({
189+ message: t('app.saveFail'),
190+ type: 'error'
191+ })
192+ }
193+ }).catch(() => {
194+ ElMessage({
195+ message: t('app.saveFail'),
196+ type: 'error'
197+ })
198+ })
199+ }
200+ })
201+}
202+const cancel = () => {
203+ formData.value = {}
204+ formData.value = cloneDeep(initFormData)
205+ emit("cancelNotifyWay")
206+}
207+onMounted(() => {
208+ if (props.id) {
209+ requestData(props.id)
210+ }
211+ if (props.state !== 'detail') {
212+ disabled.value = false
213+ } else {
214+ disabled.value = true
215+ }
216+})
217+</script>
218+<style scoped lang='scss'></style>
Aplugins/alert-center/web-ui/src/vite-env.d.ts+8-0
@@ -0,0 +1,8 @@
1+/// <reference types="vite/client" />
2+ 
3+declare module "*.vue" {
4+ import type { DefineComponent } from "vue";
5+ const component: DefineComponent<{}, {}, any>;
6+ export default component;
7+}
8+declare module 'jsencrypt'
Aplugins/alert-center/web-ui/tsconfig.json+19-0
@@ -0,0 +1,19 @@
1+{
2+ "compilerOptions": {
3+ "target": "ESNext",
4+ "useDefineForClassFields": true,
5+ "module": "ESNext",
6+ "moduleResolution": "Node",
7+ "strict": true,
8+ "jsx": "preserve",
9+ "sourceMap": true,
10+ "resolveJsonModule": true,
11+ "isolatedModules": false,
12+ "esModuleInterop": true,
13+ "lib": ["ESNext", "DOM"],
14+ "skipLibCheck": true,
15+ "allowSyntheticDefaultImports": true
16+ },
17+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
18+ "references": [{ "path": "./tsconfig.node.json" }]
19+}
Aplugins/alert-center/web-ui/tsconfig.node.json+9-0
@@ -0,0 +1,9 @@
1+{
2+ "compilerOptions": {
3+ "composite": true,
4+ "module": "ESNext",
5+ "moduleResolution": "Node",
6+ "allowSyntheticDefaultImports": true
7+ },
8+ "include": ["vite.config.ts"]
9+}
Aplugins/alert-center/web-ui/vite.config.ts+65-0
@@ -0,0 +1,65 @@
1+import { defineConfig, loadEnv } from "vite";
2+import vue from "@vitejs/plugin-vue";
3+import AutoImport from "unplugin-auto-import/vite";
4+import Components from "unplugin-vue-components/vite";
5+import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
6+import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
7+import { resolve } from "path";
8+ 
9+// https://vitejs.dev/config/
10+export default defineConfig(({ command, mode }) => {
11+ loadEnv(mode, process.cwd());
12+ return {
13+ base: mode === "production" ? "/static-plugin/alert-center/" : "/",
14+ plugins: [
15+ vue(),
16+ AutoImport({
17+ resolvers: [ElementPlusResolver()],
18+ imports: ["vue", "vue-router"],
19+ dts: "src/auto-imports.d.ts",
20+ eslintrc: {
21+ enabled: false,
22+ filepath: "./.eslintrc-auto-import.json",
23+ globalsPropValue: true,
24+ },
25+ }),
26+ Components({
27+ resolvers: [
28+ ElementPlusResolver({
29+ importStyle: "sass",
30+ }),
31+ ],
32+ dts: "src/components.d.ts",
33+ dirs: ["src/components", "src/layout"],
34+ }),
35+ createSvgIconsPlugin({
36+ iconDirs: [resolve(process.cwd(), "src/assets/svg")],
37+ symbolId: "icon-[dir]-[name]",
38+ inject: "body-first",
39+ }),
40+ ],
41+ define: {
42+ "process.env": {
43+ mode,
44+ },
45+ },
46+ resolve: {
47+ alias: {
48+ "@": resolve(__dirname, "./src/"),
49+ },
50+ },
51+ css: {
52+ preprocessorOptions: {
53+ scss: {
54+ additionalData: `@use "@/assets/style/theme.scss" as *;@use "@/assets/style/color.scss" as *;`,
55+ },
56+ },
57+ },
58+ server: {
59+ proxy: {
60+ // '^/alertCenter': 'http://192.168.110.32:9494/plugins/alert-center'
61+ '^/alertCenter': 'http://127.0.0.1:8080'
62+ },
63+ },
64+ };
65+});
Aplugins/observability-instance/HEADER+1-0
@@ -0,0 +1 @@
1+Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
Aplugins/observability-instance/InstanceExporter/.gitignore+1-0
@@ -0,0 +1 @@
1+/bin/
Aplugins/observability-instance/InstanceExporter/pom.xml+129-0
@@ -0,0 +1,129 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!--
3+ 
4+ Copyright (c) 2023 Huawei Technologies Co.,Ltd.
5+ 
6+-->
7+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
8+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
9+ <modelVersion>4.0.0</modelVersion>
10+ <parent>
11+ <groupId>org.springframework.boot</groupId>
12+ <artifactId>spring-boot-starter-parent</artifactId>
13+ <version>2.7.11</version>
14+ <relativePath />
15+ </parent>
16+ <groupId>com.nctigba</groupId>
17+ <artifactId>instance-exporter</artifactId>
18+ <version>5.0.0</version>
19+ <properties>
20+ <java.version>11</java.version>
21+ </properties>
22+ <dependencies>
23+ <dependency>
24+ <groupId>org.springframework.boot</groupId>
25+ <artifactId>spring-boot-starter-web</artifactId>
26+ </dependency>
27+ <dependency>
28+ <groupId>org.projectlombok</groupId>
29+ <artifactId>lombok</artifactId>
30+ <scope>provided</scope>
31+ <optional>true</optional>
32+ </dependency>
33+ <dependency>
34+ <groupId>cn.hutool</groupId>
35+ <artifactId>hutool-all</artifactId>
36+ <version>5.0.3</version>
37+ </dependency>
38+ <!-- https://mvnrepository.com/artifact/io.prometheus/simpleclient_httpserver -->
39+ <dependency>
40+ <groupId>io.prometheus</groupId>
41+ <artifactId>simpleclient_httpserver</artifactId>
42+ </dependency>
43+ <!-- https://mvnrepository.com/artifact/org.apache.sshd/sshd-scp -->
44+ <dependency>
45+ <groupId>org.apache.sshd</groupId>
46+ <artifactId>sshd-sftp</artifactId>
47+ <version>2.9.2</version>
48+ </dependency>
49+ <dependency>
50+ <groupId>org.opengauss</groupId>
51+ <artifactId>opengauss-jdbc</artifactId>
52+ <version>3.0.0</version>
53+ </dependency>
54+ </dependencies>
55+ <build>
56+ <plugins>
57+ <plugin>
58+ <groupId>org.springframework.boot</groupId>
59+ <artifactId>spring-boot-maven-plugin</artifactId>
60+ <configuration>
61+ <excludes>
62+ <exclude>
63+ <groupId>org.projectlombok</groupId>
64+ <artifactId>lombok</artifactId>
65+ </exclude>
66+ </excludes>
67+ </configuration>
68+ </plugin>
69+ <plugin>
70+ <artifactId>maven-dependency-plugin</artifactId>
71+ <executions>
72+ <execution>
73+ <phase>package</phase>
74+ <goals>
75+ <goal>copy</goal>
76+ </goals>
77+ <configuration>
78+ <artifactItems>
79+ <artifactItem>
80+ <groupId>${project.groupId}</groupId>
81+ <artifactId>${project.artifactId}</artifactId>
82+ <version>${project.version}</version>
83+ </artifactItem>
84+ </artifactItems>
85+ <outputDirectory>../src/main/resources</outputDirectory>
86+ <stripClassifier>true</stripClassifier>
87+ <stripVersion>true</stripVersion>
88+ </configuration>
89+ </execution>
90+ </executions>
91+ </plugin>
92+ <plugin>
93+ <groupId>com.mycila</groupId>
94+ <artifactId>license-maven-plugin</artifactId>
95+ <version>4.2</version>
96+ <executions>
97+ <execution>
98+ <phase>generate-sources</phase>
99+ <goals>
100+ <goal>format</goal>
101+ </goals>
102+ </execution>
103+ </executions>
104+ <configuration>
105+ <quiet>true</quiet>
106+ <header>../HEADER</header>
107+ <excludes>
108+ <exclude>**/*.properties</exclude>
109+ <exclude>*.sh</exclude>
110+ <exclude>*.yml</exclude>
111+ <exclude>.editorconfig</exclude>
112+ <exclude>.gitignore</exclude>
113+ <exclude>*.md</exclude>
114+ <exclude>*.xml</exclude>
115+ <exclude>*.html</exclude>
116+ <exclude>*.ts</exclude>
117+ <exclude>*.vue</exclude>
118+ <exclude>*.yaml</exclude>
119+ <exclude>*.scss</exclude>
120+ </excludes>
121+ <strictCheck>true</strictCheck>
122+ <mapping>
123+ <java>SLASHSTAR_STYLE</java>
124+ </mapping>
125+ </configuration>
126+ </plugin>
127+ </plugins>
128+ </build>
129+</project>
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/Application.java+18-0
@@ -0,0 +1,18 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent;
5+ 
6+import java.io.IOException;
7+ 
8+import org.springframework.boot.SpringApplication;
9+import org.springframework.boot.autoconfigure.SpringBootApplication;
10+import org.springframework.scheduling.annotation.EnableScheduling;
11+ 
12+@SpringBootApplication
13+@EnableScheduling
14+public class Application {
15+ public static void main(String[] args) throws IOException {
16+ SpringApplication.run(Application.class, args);
17+ }
18+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/controller/Command.java+98-0
@@ -0,0 +1,98 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.controller;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.ArrayList;
9+import java.util.Arrays;
10+import java.util.Collections;
11+import java.util.Comparator;
12+import java.util.HashMap;
13+import java.util.HashSet;
14+import java.util.List;
15+import java.util.Map;
16+import java.util.Set;
17+ 
18+import org.opengauss.plugin.agent.server.HostMetric;
19+import org.opengauss.plugin.agent.util.CmdUtil;
20+import org.opengauss.plugin.agent.util.StringUtil;
21+import org.springframework.web.bind.annotation.GetMapping;
22+import org.springframework.web.bind.annotation.RequestMapping;
23+import org.springframework.web.bind.annotation.RequestParam;
24+import org.springframework.web.bind.annotation.RestController;
25+ 
26+import cn.hutool.core.util.StrUtil;
27+import lombok.AllArgsConstructor;
28+ 
29+@RestController
30+@RequestMapping("/cmd")
31+@AllArgsConstructor
32+public class Command {
33+ private static final String TOP = "top -b -n 1";
34+ private static final String TOP_DB_PID = "netstat -nap|grep :'|grep LISTEN";
35+ private static final String TOP_DB_THREAD = "top -H -bn1 -p ";
36+ private final HostMetric hostMetric;
37+ 
38+ @GetMapping("/top")
39+ public Object top(@RequestParam(required = false) String sort) throws FileNotFoundException, IOException {
40+ List<String> header = new ArrayList<>();
41+ List<Map<String, String>> top = new ArrayList<>();
42+ CmdUtil.readFromCmd(TOP, (i, line) -> {
43+ if (i < 6)
44+ return;
45+ String[] part = StringUtil.splitByBlank(line);
46+ if (i == 6) {
47+ Collections.addAll(header, part);
48+ return;
49+ }
50+ Map<String, String> obj = new HashMap<String, String>();
51+ for (int j = 0; j < header.size(); j++) {
52+ obj.put(header.get(j), part[j]);
53+ }
54+ top.add(obj);
55+ });
56+ Set<String> pids = new HashSet<>();
57+ CmdUtil.readFromCmd(TOP_DB_PID.replaceAll("'", hostMetric.getDbport().toString()), line -> {
58+ var part = StringUtil.splitByBlank(line);
59+ var pid = part[part.length - 1];
60+ pid = pid.split("/")[0];
61+ pids.add(pid);
62+ });
63+ List<Map<String, String>> db_top = new ArrayList<>();
64+ if (pids.size() != 0) {
65+ var pid = pids.iterator().next();
66+ CmdUtil.readFromCmd(TOP_DB_THREAD + pid, (i, line) -> {
67+ if (i < 6)
68+ return;
69+ String[] part = StringUtil.splitByBlank(line);
70+ if (i == 6) {
71+ header.clear();
72+ Collections.addAll(header, part);
73+ return;
74+ }
75+ Map<String, String> obj = new HashMap<String, String>();
76+ for (int j = 0; j < header.size(); j++) {
77+ obj.put(header.get(j), part[j]);
78+ }
79+ db_top.add(obj);
80+ });
81+ }
82+ if (StrUtil.isNotBlank(sort)) {
83+ var comparator = new Comparator<Map<String, String>>() {
84+ @Override
85+ public int compare(Map<String, String> o1, Map<String, String> o2) {
86+ try {
87+ return -Double.compare(Double.parseDouble(o1.get(sort)), Double.parseDouble(o2.get(sort)));
88+ } catch (NumberFormatException e) {
89+ return -o1.get(sort).compareTo(o2.get(sort));
90+ }
91+ }
92+ };
93+ Collections.sort(top, comparator);
94+ Collections.sort(db_top, comparator);
95+ }
96+ return Arrays.asList(top, db_top);
97+ }
98+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/controller/Config.java+50-0
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.controller;
5+ 
6+import java.io.File;
7+import java.io.FileInputStream;
8+import java.io.FileWriter;
9+import java.io.IOException;
10+import java.io.InputStream;
11+import java.util.HashMap;
12+import java.util.Map;
13+ 
14+import org.opengauss.plugin.agent.server.HostMetric;
15+import org.springframework.web.bind.annotation.GetMapping;
16+import org.springframework.web.bind.annotation.RequestMapping;
17+import org.springframework.web.bind.annotation.RestController;
18+import org.yaml.snakeyaml.Yaml;
19+ 
20+import lombok.RequiredArgsConstructor;
21+ 
22+@RestController
23+@RequestMapping("/config")
24+@RequiredArgsConstructor
25+public class Config {
26+ private final HostMetric hostMetric;
27+ 
28+ @GetMapping("/set")
29+ public void set(String hostId, String nodeId, Integer dbport, String username, String password) throws IOException {
30+ File application = new File(".", "application.yml");
31+ if (!application.exists())
32+ application.createNewFile();
33+ try (InputStream is = new FileInputStream(application);) {
34+ Yaml yaml = new Yaml();
35+ Map<String, Object> map = yaml.load(is);
36+ if (map == null)
37+ map = new HashMap<>();
38+ map.put("conf", Map.of("hostId", hostId, "node",
39+ Map.of("nodeId", nodeId, "dbport", dbport, "username", username, "password", password)));
40+ // refresh curr config
41+ hostMetric.setHostId(hostId).setNodeId(nodeId).setDbport(dbport).setDbUsername(username)
42+ .setDbPassword(password);
43+ try (var writer = new FileWriter(application)) {
44+ writer.write(yaml.dumpAsMap(map));
45+ }
46+ } catch (IOException e) {
47+ throw e;
48+ }
49+ }
50+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/Ametric.java+23-0
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Map;
9+ 
10+/**
11+ * used by {@code com.nctigba.server.HttpServer}
12+ */
13+public interface Ametric {
14+ /**
15+ * generate metric
16+ *
17+ * @param dbPort TODO
18+ * @return
19+ * @throws FileNotFoundException
20+ * @throws IOException
21+ */
22+ Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException;
23+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/DBmetric.java+7-0
@@ -0,0 +1,7 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric;
5+ 
6+public interface DBmetric extends Ametric {
7+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/Metric.java+58-0
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric;
5+ 
6+import java.util.ArrayList;
7+import java.util.Collections;
8+import java.util.List;
9+ 
10+import cn.hutool.core.util.StrUtil;
11+import io.prometheus.client.Collector;
12+import lombok.AllArgsConstructor;
13+import lombok.Data;
14+import lombok.NoArgsConstructor;
15+ 
16+@Data
17+@NoArgsConstructor
18+public class Metric {
19+ Collector.Type type;
20+ String help;
21+ List<String> labelNames = new ArrayList<>();
22+ List<Metric.values> values = new ArrayList<>();
23+ 
24+ public Metric addValue(List<String> labelValues, String value) {
25+ return addValue(labelValues, StrUtil.isBlank(value) ? 0 : Double.parseDouble(value));
26+ }
27+ 
28+ public Metric addValue(List<String> labelValues, double value) {
29+ values.add(new values(labelValues, value));
30+ return this;
31+ }
32+ 
33+ public Metric(Collector.Type type, List<String> labelNames) {
34+ this.type = type;
35+ this.labelNames = labelNames;
36+ }
37+ 
38+ public Metric(Collector.Type type, String help, List<String> labelNames) {
39+ this.type = type;
40+ this.help = help;
41+ this.labelNames = labelNames;
42+ }
43+ 
44+ public List<String> getLabelNames() {
45+ return labelNames == null ? Collections.emptyList() : labelNames;
46+ }
47+ 
48+ @Data
49+ @AllArgsConstructor
50+ public static class values {
51+ private List<String> labelValues;
52+ private Double value;
53+ 
54+ public List<String> getLabelValues() {
55+ return labelValues == null ? Collections.emptyList() : labelValues;
56+ }
57+ }
58+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/OSmetric.java+7-0
@@ -0,0 +1,7 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric;
5+ 
6+public interface OSmetric extends Ametric {
7+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/opengauss/OpengaussExporter.java+217-0
@@ -0,0 +1,217 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.opengauss;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.sql.Timestamp;
9+import java.util.ArrayList;
10+import java.util.Collections;
11+import java.util.HashMap;
12+import java.util.List;
13+import java.util.Map;
14+import java.util.Map.Entry;
15+import java.util.concurrent.TimeUnit;
16+ 
17+import org.opengauss.plugin.agent.metric.Metric;
18+import org.opengauss.plugin.agent.util.DbUtil;
19+import org.springframework.beans.factory.InitializingBean;
20+import org.springframework.core.io.ClassPathResource;
21+import org.springframework.scheduling.annotation.Scheduled;
22+import org.springframework.stereotype.Service;
23+import org.yaml.snakeyaml.Yaml;
24+ 
25+import org.opengauss.plugin.agent.metric.DBmetric;
26+import org.opengauss.plugin.agent.metric.opengauss.OpengaussExporter.queryInstance.metricInfo.usage;
27+import org.opengauss.plugin.agent.metric.opengauss.OpengaussExporter.queryInstance.query;
28+import org.opengauss.plugin.agent.metric.opengauss.OpengaussExporter.queryInstance.query.state;
29+ 
30+import cn.hutool.core.bean.BeanUtil;
31+import cn.hutool.core.thread.ThreadUtil;
32+import cn.hutool.core.util.NumberUtil;
33+import io.prometheus.client.Collector.Type;
34+import lombok.AllArgsConstructor;
35+import lombok.Data;
36+import lombok.Getter;
37+import lombok.extern.log4j.Log4j2;
38+ 
39+/**
40+ * table pg_stat_activity
41+ */
42+@Service
43+@Log4j2
44+@AllArgsConstructor
45+public class OpengaussExporter implements DBmetric, InitializingBean {
46+ private static final Map<String, queryInstance> CONFIG = new HashMap<>();
47+ private static Map<String, Metric> metricData = new HashMap<>();
48+
49+ private final DbUtil dbUtil;
50+ 
51+ /**
52+ * load open Gauss exporter config file
53+ */
54+ @SuppressWarnings("unchecked")
55+ @Override
56+ public void afterPropertiesSet() throws Exception {
57+ var in = new ClassPathResource("og_exporter.yml").getInputStream();
58+ Map<String, Object> map = new Yaml().loadAs(in, Map.class);
59+ map.entrySet().forEach((entry) -> {
60+ if (entry.getValue() instanceof Map) {
61+ var b = BeanUtil.mapToBean((Map<?, ?>) entry.getValue(), queryInstance.class, false);
62+ CONFIG.put(entry.getKey(), b);
63+ 
64+ }
65+ });
66+ }
67+ 
68+ @Override
69+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
70+ return metricData;
71+ }
72+ 
73+ @Scheduled(fixedRate = 15000, initialDelay = 1000)
74+ public void catchMetric() {
75+ if (CONFIG.size() == 0) {
76+ return;
77+ }
78+ Map<String, Metric> all = new HashMap<>();
79+ var countDown = ThreadUtil.newCountDownLatch(CONFIG.size());
80+ CONFIG.entrySet().forEach(conf -> {
81+ ThreadUtil.execute(() -> {
82+ try {
83+ all.putAll(metric(conf));
84+ } finally {
85+ countDown.countDown();
86+ }
87+ });
88+ });
89+ try {
90+ countDown.await(10, TimeUnit.SECONDS);
91+ } catch (InterruptedException e) {
92+ throw new RuntimeException(e);
93+ }
94+ metricData = all;
95+ }
96+ 
97+ private Map<String, Metric> metric(Entry<String, queryInstance> conf) {
98+ String sql = null;
99+ for (query q : conf.getValue().getQuery()) {
100+ if (q.getStatus() == state.enable) {
101+ sql = q.getSql();
102+ break;
103+ }
104+ }
105+ if (sql == null) {
106+ return Collections.emptyMap();
107+ }
108+ // generate label names
109+ List<String> labelNames = conf.getValue().generateLabelNames();
110+ // generate metric family
111+ Map<String, Metric> result = conf.getValue().generateMetricFamily(conf.getKey(), labelNames);
112+ // add data
113+ List<Map<String, Object>> r = dbUtil.query(sql);
114+ for (Map<String, Object> row : r) {
115+ // label values
116+ List<String> labelValues = new ArrayList<>();
117+ for (String name : labelNames) {
118+ try {
119+ labelValues.add(row.get(name).toString());
120+ } catch (Exception e) {
121+ log.debug("【labelNames】:【name】{}", name);
122+ log.error("labelName not found from sql:{}", name);
123+ }
124+ }
125+ // metric values
126+ conf.getValue().getMetrics().forEach(metric -> {
127+ if (metric.getUsage().getType() != null) {
128+ double val = 0;
129+ Object value = row.get(metric.getName());
130+ if (value == null) {
131+ log.error("metric error:{}", metric.getName());
132+ val = 0;
133+ } else if (NumberUtil.isNumber(value.toString())) {
134+ val = Double.parseDouble(value.toString());
135+ } else {
136+ if (value instanceof Timestamp)
137+ val = ((Timestamp) value).getTime();
138+ }
139+ result.get(conf.getKey() + "_" + metric.getName()).addValue(labelValues, val);
140+ }
141+ });
142+ }
143+ return result;
144+ }
145+ 
146+ @Data
147+ public static class queryInstance {
148+ private String name;
149+ private String desc;
150+ private List<query> query;
151+ private List<metricInfo> metrics;
152+ private String status;
153+ 
154+ public List<String> generateLabelNames() {
155+ List<String> labelNames = new ArrayList<>();
156+ getMetrics().forEach(metric -> {
157+ if (metric.getUsage() == usage.LABEL) {
158+ labelNames.add(metric.getName());
159+ return;
160+ }
161+ });
162+ return labelNames;
163+ }
164+ 
165+ public Map<String, Metric> generateMetricFamily(String base, List<String> labelNames) {
166+ Map<String, Metric> result = new HashMap<>();
167+ getMetrics().forEach(metric -> {
168+ if (metric.getUsage().getType() != null) {
169+ result.put(base + "_" + metric.getName(), new Metric(metric.getUsage().type, labelNames));
170+ }
171+ });
172+ return result;
173+ }
174+ 
175+ @Data
176+ public static class query {
177+ private String name;
178+ private String desc;
179+ private String sql;
180+ private String version;
181+ private String tags;
182+ private Integer timeout;
183+ private Integer ttl;
184+ private state status;
185+ private String dbRole;
186+ 
187+ public enum state {
188+ enable,
189+ disable
190+ }
191+ }
192+ 
193+ @Data
194+ public static class metricInfo {
195+ private String name;
196+ private String description;
197+ private usage usage;
198+ 
199+ @Getter
200+ public enum usage {
201+ COUNTER(Type.COUNTER),
202+ GAUGE(Type.GAUGE),
203+ LABEL,
204+ DISCARD;
205+ 
206+ private Type type;
207+ 
208+ private usage() {
209+ }
210+ 
211+ private usage(Type type) {
212+ this.type = type;
213+ }
214+ }
215+ }
216+ }
217+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/opengauss/Top.java+51-0
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.opengauss;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Collections;
9+import java.util.HashMap;
10+import java.util.HashSet;
11+import java.util.Map;
12+import java.util.Set;
13+ 
14+import org.opengauss.plugin.agent.metric.Metric;
15+import org.opengauss.plugin.agent.util.CmdUtil;
16+import org.opengauss.plugin.agent.util.StringUtil;
17+import org.springframework.stereotype.Service;
18+ 
19+import org.opengauss.plugin.agent.metric.DBmetric;
20+ 
21+import io.prometheus.client.Collector.Type;
22+ 
23+@Service
24+public class Top implements DBmetric {
25+ private static final String COMMAND_PORT_TO_PID = "netstat -nap|grep :'|grep LISTEN";
26+ private static final String COMMAND_TOP_ = "top -b -n 1|grep ";
27+ 
28+ @Override
29+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
30+ if (dbPort == null)
31+ return Collections.emptyMap();
32+ Set<String> pids = new HashSet<>();
33+ CmdUtil.readFromCmd(COMMAND_PORT_TO_PID.replaceAll("'", dbPort.toString()), line -> {
34+ var part = StringUtil.splitByBlank(line);
35+ var pid = part[part.length - 1];
36+ pid = pid.split("/")[0];
37+ pids.add(pid);
38+ });
39+ Map<String, Metric> map = new HashMap<String, Metric>();
40+ if (pids.size() == 0)
41+ return map;
42+ CmdUtil.readFromCmd(COMMAND_TOP_ + pids.iterator().next(), line -> {
43+ // PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44+ // 163616 omm 20 0 3133820 525420 171716 S 0.0 3.3 507:05.43 gaussdb
45+ var part = StringUtil.splitByBlank(line);
46+ map.put("top_db_cpu", new Metric(Type.GAUGE, null).addValue(null, part[8]));
47+ map.put("top_db_mem", new Metric(Type.GAUGE, null).addValue(null, part[9]));
48+ });
49+ return map;
50+ }
51+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/VmStat.java+56-0
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.HashMap;
9+import java.util.Map;
10+ 
11+import org.opengauss.plugin.agent.metric.Metric;
12+import org.opengauss.plugin.agent.metric.OSmetric;
13+import org.opengauss.plugin.agent.util.CmdUtil;
14+import org.opengauss.plugin.agent.util.StringUtil;
15+import org.springframework.stereotype.Service;
16+ 
17+import io.prometheus.client.Collector.Type;
18+ 
19+/**
20+ * @see <a href=
21+ * "https://zhuanlan.zhihu.com/p/364383421">zhuanlan.zhihu.com/p/364383421</a>
22+ */
23+@Service("vmstat_")
24+public class VmStat implements OSmetric {
25+ private static final String[] KEYS = {
26+ "r",
27+ "b",
28+ "swpd",
29+ "free",
30+ "buff",
31+ "cache",
32+ "si",
33+ "so",
34+ "bi",
35+ "bo",
36+ "in",
37+ "cs",
38+ "us",
39+ "sy",
40+ "wa",
41+ "st" };
42+ 
43+ @Override
44+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
45+ Map<String, Metric> map = new HashMap<>();
46+ CmdUtil.readFromCmd(CmdUtil.cmd("vmstat"), (index, line) -> {
47+ if (index < 2)
48+ return;
49+ var part = StringUtil.splitByBlank(line);
50+ for (int i = 0; i < part.length && i < KEYS.length; i++) {
51+ map.put(KEYS[i], new Metric(Type.GAUGE, null).addValue(null, part[i]));
52+ }
53+ });
54+ return map;
55+ }
56+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/cpu/LoadAvg.java+34-0
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.cpu;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Map;
9+ 
10+import org.opengauss.plugin.agent.metric.Metric;
11+import org.opengauss.plugin.agent.util.FileUtil;
12+import org.opengauss.plugin.agent.util.StringUtil;
13+import org.springframework.stereotype.Service;
14+ 
15+import org.opengauss.plugin.agent.metric.OSmetric;
16+ 
17+import io.prometheus.client.Collector.Type;
18+ 
19+@Service("load")
20+public class LoadAvg implements OSmetric {
21+ @Override
22+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
23+ var load1 = new Metric(Type.GAUGE, "1m load average.", null);
24+ var load5 = new Metric(Type.GAUGE, "5m load average.", null);
25+ var load15 = new Metric(Type.GAUGE, "15m load average.", null);
26+ FileUtil.readFileLine("/proc/loadavg", s -> {
27+ var part = StringUtil.splitByBlank(s);
28+ load1.addValue(null, part[0]);
29+ load5.addValue(null, part[1]);
30+ load15.addValue(null, part[2]);
31+ });
32+ return Map.of("1", load1, "5", load5, "15", load15);
33+ }
34+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/cpu/ProcStat.java+57-0
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.cpu;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Arrays;
9+import java.util.Map;
10+ 
11+import org.opengauss.plugin.agent.metric.Metric;
12+import org.opengauss.plugin.agent.util.FileUtil;
13+import org.opengauss.plugin.agent.util.StringUtil;
14+import org.springframework.stereotype.Service;
15+ 
16+import org.opengauss.plugin.agent.metric.OSmetric;
17+ 
18+import io.prometheus.client.Collector.Type;
19+ 
20+/**
21+ * Linux file <code>/proc/stat</code>
22+ */
23+@Service("cpu")
24+public class ProcStat implements OSmetric {
25+ @Override
26+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
27+ var seconds_total = new Metric(Type.COUNTER, Arrays.asList("cpu", "mode"));
28+ FileUtil.readFileLine("/proc/stat", s -> {
29+ var part = StringUtil.splitByBlank(s);
30+ if (part[0].equalsIgnoreCase("cpu"))
31+ return;
32+ if (part[0].startsWith("cpu")) {
33+ String cpuIndex = part[0].replaceAll("cpu", "");
34+ for (cpuStat stat : cpuStat.values()) {
35+ int index = stat.ordinal() + 1;
36+ if (index < part.length) {
37+ var value = Double.parseDouble(part[index]);
38+ value /= 100;
39+ seconds_total.addValue(Arrays.asList(cpuIndex, stat.name()), value);
40+ }
41+ }
42+ }
43+ });
44+ return Map.of("_seconds_total", seconds_total);
45+ }
46+ 
47+ public enum cpuStat {
48+ user,
49+ nice,
50+ system,
51+ idle,
52+ iowait,
53+ irq,
54+ softirq,
55+ steal,
56+ }
57+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/disk/DiskStats.java+82-0
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.disk;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Arrays;
9+import java.util.HashMap;
10+import java.util.Map;
11+ 
12+import org.opengauss.plugin.agent.metric.Metric;
13+import org.opengauss.plugin.agent.util.FileUtil;
14+import org.opengauss.plugin.agent.util.StringUtil;
15+import org.springframework.stereotype.Service;
16+ 
17+import org.opengauss.plugin.agent.metric.OSmetric;
18+ 
19+import cn.hutool.core.util.ReUtil;
20+import io.prometheus.client.Collector.Type;
21+ 
22+/**
23+ * Linux 2.6 file {@code /proc/diskstats}<br/>
24+ * Linux 2.4 file {@code /proc/partitions} TODO
25+ *
26+ * @see <a href=
27+ * "https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats">
28+ * kernel.org/doc/procfs-diskstats</a>
29+ * @see <a href=
30+ * "https://blog.csdn.net/qu1993/article/details/105702991">blog.csdn.net/qu1993/article/details/105702991</a>
31+ */
32+@Service("disk_")
33+public class DiskStats implements OSmetric {
34+ private static final String[] KEYS = {
35+ "major number",
36+ "minor mumber",
37+ "device name",
38+ "rd_ios", // reads completed
39+ "rd_merges", // reads merged
40+ // https://unix.stackexchange.com/questions/111968/how-to-get-total-disk-read-write-in-bytes-per-hdd-device-from-proc
41+ "rd_sectors", // sectors read
42+ "rd_ticks", // time spent reading(ms)
43+ "wr_ios", // writes completed
44+ "wr_merges", // writes merged
45+ "wr_sectors", // sectors written
46+ "wr_ticks", // time spent writing(ms)
47+ "io_pgr", // io_now
48+ // io_ticks, https://zhuanlan.zhihu.com/p/604547780
49+ "tot_ticks", // io_time_ms
50+ "rq_ticks", // weighted time spent doing IOs(ms)
51+ // Kernel 4.18+
52+ "discards completed successfully",
53+ "discards merged",
54+ "discarded_sectors",
55+ "time spent discarding",
56+ // Kernel 5.5+
57+ "flush requests completed successfully",
58+ "time spent flushing", };
59+ 
60+ private static final String IGNORE = "^(ram|loop|fd|(h|s|v|xv)d[a-z]|nvme\\d+n\\d+p)\\d+$";
61+ 
62+ @Override
63+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
64+ Map<String, Metric> map = new HashMap<>();
65+ FileUtil.readFileLine("/proc/diskstats", line -> {
66+ var part = StringUtil.splitByBlank(line);
67+ if (ReUtil.contains(IGNORE, part[2]))
68+ return;
69+ if (map.isEmpty()) {
70+ var labelNames = Arrays.asList("major", "minor", "device");
71+ for (int i = 3; i < KEYS.length; i++) {
72+ map.put(KEYS[i], new Metric(Type.COUNTER, labelNames));
73+ }
74+ }
75+ var labelValues = Arrays.asList(part[0], part[1], part[2]);
76+ for (int i = 3; i < KEYS.length; i++) {
77+ map.get(KEYS[i]).addValue(labelValues, i < part.length ? part[i] : null);
78+ }
79+ });
80+ return map;
81+ }
82+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/memory/Free.java+43-0
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.memory;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.HashMap;
9+import java.util.Map;
10+ 
11+import org.opengauss.plugin.agent.metric.Metric;
12+import org.opengauss.plugin.agent.util.CmdUtil;
13+import org.opengauss.plugin.agent.util.StringUtil;
14+import org.springframework.stereotype.Service;
15+ 
16+import org.opengauss.plugin.agent.metric.OSmetric;
17+ 
18+import io.prometheus.client.Collector.Type;
19+ 
20+@Service("free_")
21+public class Free implements OSmetric {
22+ private static final String[] KEYS = { "type", "total", "used", "free", "shared", "cache", "available" };
23+ 
24+ @Override
25+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
26+ Map<String, Metric> map = new HashMap<>();
27+ CmdUtil.readFromCmd(CmdUtil.cmd("free"), (index, line) -> {
28+ if (index == 0) {
29+ // total used free shared buff/cache available
30+ return;
31+ }
32+ var part = StringUtil.splitByBlank(line);
33+ // Mem: 15896504 9520972 496816 1156408 5878716 4745916
34+ // Swap: 8278012 209664 8068348
35+ for (int i = 1; i < part.length; i++) {
36+ map.put(StringUtil.removeLast(part[0]) + "_" + KEYS[i] + "_bytes",
37+ new Metric(Type.GAUGE, null).addValue(null, Double.parseDouble(part[i]) * 1024));
38+ }
39+ return;
40+ });
41+ return map;
42+ }
43+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/memory/ProcMeminfo.java+41-0
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.memory;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.HashMap;
9+import java.util.Map;
10+ 
11+import org.opengauss.plugin.agent.metric.Metric;
12+import org.opengauss.plugin.agent.util.FileUtil;
13+import org.opengauss.plugin.agent.util.StringUtil;
14+import org.springframework.stereotype.Service;
15+ 
16+import org.opengauss.plugin.agent.metric.OSmetric;
17+ 
18+import io.prometheus.client.Collector.Type;
19+ 
20+/**
21+ * Linux file <code>/proc/meminfo</code>
22+ */
23+@Service("memory")
24+public class ProcMeminfo implements OSmetric {
25+ @Override
26+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
27+ var map = new HashMap<String, Metric>();
28+ FileUtil.readFileLine("/proc/meminfo", line -> {
29+ var part = StringUtil.splitByBlank(line);
30+ double value = Double.parseDouble(part[1]);
31+ var key = StringUtil.replaceParenthesis(line);
32+ if (part.length == 3) {
33+ value *= 1024;
34+ key += "_bytes";
35+ }
36+ map.put("_" + key, new Metric(Type.GAUGE, null).addValue(null, value));
37+ });
38+ return map;
39+ }
40+ 
41+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/network/NetDev.java+111-0
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.network;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Arrays;
9+import java.util.HashMap;
10+import java.util.Map;
11+ 
12+import org.opengauss.plugin.agent.metric.Metric;
13+import org.opengauss.plugin.agent.util.FileUtil;
14+import org.opengauss.plugin.agent.util.StringUtil;
15+import org.springframework.stereotype.Service;
16+ 
17+import org.opengauss.plugin.agent.metric.OSmetric;
18+ 
19+import cn.hutool.core.util.ReUtil;
20+import cn.hutool.core.util.StrUtil;
21+import io.prometheus.client.Collector.Type;
22+ 
23+/**
24+ * Linux file /proc/net/dev
25+ * <p>
26+ * <strong>bytes</strong> The total number of bytes of data transmitted or
27+ * received by the interface.
28+ * </p>
29+ * <p>
30+ * <strong>packets</strong> The total number of packets of data transmitted or
31+ * received by the interface.
32+ * </p>
33+ * <p>
34+ * <strong>errs</strong> The total number of transmit or receive errors detected
35+ * by the device driver.
36+ * </p>
37+ * <p>
38+ * <strong>drop</strong> The total number of packets dropped by the device
39+ * driver.
40+ * </p>
41+ * <p>
42+ * <strong>fifo</strong> The number of FIFO buffer errors.
43+ * </p>
44+ * <p>
45+ * <strong>frame</strong> The number of packet framing errors.
46+ * </p>
47+ * <p>
48+ * <strong>colls</strong> The number of collisions detected on the interface.
49+ * </p>
50+ * <p>
51+ * <strong>compressed</strong> The number of compressed packets transmitted or
52+ * received by the device driver. (This appears to be unused in the 2.2.15
53+ * kernel.)
54+ * </p>
55+ * <p>
56+ * <strong>carrier</strong> The number of carrier losses detected by the device
57+ * driver.
58+ * </p>
59+ * <p>
60+ * <strong>multicast</strong> The number of multicast frames transmitted or
61+ * received by the device driver.
62+ * </p>
63+ *
64+ * @see <a href=
65+ * "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/core/net-procfs.c">git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/core/net-procfs.c</a>
66+ */
67+@Service("network_")
68+public class NetDev implements OSmetric {
69+ private static final String[] KEYS = {
70+ "device_name",
71+ "receive_bytes",
72+ "receive_packets",
73+ "receive_errors",
74+ "receive_dropped",
75+ "receive_fifo",
76+ "receive_frame",
77+ "receive_compressed",
78+ "receive_multicast",
79+ "transmit_bytes",
80+ "transmit_packets",
81+ "transmit_errors",
82+ "transmit_dropped",
83+ "transmit_fifo",
84+ "transmit_colls",
85+ "transmit_carrier",
86+ "transmit_compressed", };
87+ private static final String IGNORE = "";
88+ 
89+ @Override
90+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
91+ Map<String, Metric> map = new HashMap<>();
92+ FileUtil.readFileLine("/proc/net/dev", (index, line) -> {
93+ if (line.contains("|"))
94+ return;
95+ var part = StringUtil.splitByBlank(line);
96+ if (StrUtil.isNotBlank(IGNORE) && ReUtil.contains(IGNORE, part[0]))
97+ return;
98+ if (map.isEmpty()) {
99+ var labelNames = Arrays.asList("device");
100+ for (int i = 1; i < KEYS.length; i++) {
101+ map.put(KEYS[i], new Metric(Type.COUNTER, labelNames));
102+ }
103+ }
104+ var labelValues = Arrays.asList(StringUtil.removeLast(part[0]));
105+ for (int i = 1; i < KEYS.length; i++) {
106+ map.get(KEYS[i]).addValue(labelValues, i < part.length ? part[i] : null);
107+ }
108+ });
109+ return map;
110+ }
111+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/network/Netstat.java+93-0
@@ -0,0 +1,93 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.network;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.ArrayList;
9+import java.util.Collections;
10+import java.util.HashMap;
11+import java.util.List;
12+import java.util.Map;
13+import java.util.regex.Pattern;
14+ 
15+import org.opengauss.plugin.agent.metric.Metric;
16+import org.opengauss.plugin.agent.util.FileUtil;
17+import org.opengauss.plugin.agent.util.StringUtil;
18+import org.springframework.stereotype.Service;
19+ 
20+import org.opengauss.plugin.agent.metric.OSmetric;
21+ 
22+import cn.hutool.core.util.NumberUtil;
23+import cn.hutool.core.util.ReUtil;
24+import io.prometheus.client.Collector;
25+import lombok.extern.log4j.Log4j2;
26+ 
27+@Service("netstat_")
28+@Log4j2
29+public class Netstat implements OSmetric {
30+ private static final Pattern PATTERN = Pattern.compile(
31+ "^(.*_(InErrors|InErrs)|Ip_Forwarding|Ip(6|Ext)_(InOctets|OutOctets)|Icmp6?_(InMsgs|OutMsgs)|TcpExt_(Listen.*|Syncookies.*|TCPSynRetrans|TCPTimeouts)|Tcp_(ActiveOpens|InSegs|OutSegs|OutRsts|PassiveOpens|RetransSegs|CurrEstab)|Udp6?_(InDatagrams|OutDatagrams|NoPorts|RcvbufErrors|SndbufErrors))$");
32+ 
33+ @Override
34+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
35+ var netStats = getNetStats("/proc/net/netstat");
36+ var snmp = getNetStats("/proc/net/snmp");
37+ var snmp6Stats = getSNMP6Stats("/proc/net/snmp6");
38+ netStats.putAll(snmp);
39+ netStats.putAll(snmp6Stats);
40+ var result = new HashMap<String, Metric>();
41+ netStats.forEach((key, map) -> {
42+ map.forEach((name, value) -> {
43+ String metricName = key + "_" + name;
44+ if (ReUtil.contains(PATTERN, metricName))
45+ result.put(metricName, new Metric(Collector.Type.UNKNOWN, null).addValue(null, value));
46+ });
47+ });
48+ return result;
49+ }
50+ 
51+ private Map<String, Map<String, Long>> getNetStats(String fileName) throws FileNotFoundException, IOException {
52+ Map<String, Map<String, Long>> values = new HashMap<String, Map<String, Long>>();
53+ List<String> keys = new ArrayList<String>();
54+ FileUtil.readFileLine(fileName, (i, line) -> {
55+ var part = StringUtil.splitByBlank(line);
56+ if (keys.isEmpty()) {
57+ Collections.addAll(keys, part);
58+ return;
59+ }
60+ try {
61+ var map = new HashMap<String, Long>();
62+ for (int j = 1; j < part.length; j++) {
63+ map.put(keys.get(j), NumberUtil.parseLong(part[j]));
64+ }
65+ String key = keys.get(0);
66+ values.put(key.substring(0, key.length() - 1), map);
67+ } catch (Exception e) {
68+ log.error("{} error:{}", fileName, i);
69+ } finally {
70+ keys.clear();
71+ }
72+ });
73+ return values;
74+ }
75+ 
76+ private Map<String, Map<String, Long>> getSNMP6Stats(String fileName) throws FileNotFoundException, IOException {
77+ Map<String, Map<String, Long>> values = new HashMap<String, Map<String, Long>>();
78+ FileUtil.readFileLine(fileName, line -> {
79+ // Expect to have "6" in metric name, skip line otherwise
80+ if (!line.contains("6"))
81+ return;
82+ var part = StringUtil.splitByBlank(line);
83+ if (part.length < 2)
84+ return;
85+ var keys = part[0].split("6");
86+ var protocol = keys[0] + 6;
87+ if (!values.containsKey(protocol))
88+ values.put(protocol, new HashMap<String, Long>());
89+ values.get(protocol).put(keys[1], NumberUtil.parseLong(part[1]));
90+ });
91+ return values;
92+ }
93+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/network/SockStat.java+34-0
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.network;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.HashMap;
9+import java.util.Map;
10+ 
11+import org.opengauss.plugin.agent.metric.Metric;
12+import org.opengauss.plugin.agent.util.FileUtil;
13+import org.opengauss.plugin.agent.util.StringUtil;
14+import org.springframework.stereotype.Service;
15+ 
16+import org.opengauss.plugin.agent.metric.OSmetric;
17+ 
18+import io.prometheus.client.Collector.Type;
19+ 
20+@Service("sockstat_")
21+public class SockStat implements OSmetric {
22+ @Override
23+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
24+ Map<String, Metric> map = new HashMap<>();
25+ FileUtil.readFileLine("/proc/net/sockstat", line -> {
26+ var part = StringUtil.splitByBlank(line);
27+ var key = StringUtil.replaceParenthesis(part[0]) + "_";
28+ for (int i = 1; i * 2 <= part.length; i++) {
29+ map.put(key + part[2 * i - 1], new Metric(Type.GAUGE, null).addValue(null, part[2 * i]));
30+ }
31+ });
32+ return map;
33+ }
34+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/metric/system/network/Socket.java+54-0
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.metric.system.network;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.Arrays;
9+import java.util.HashMap;
10+import java.util.Map;
11+ 
12+import org.opengauss.plugin.agent.metric.Metric;
13+import org.opengauss.plugin.agent.util.CmdUtil;
14+import org.opengauss.plugin.agent.util.StringUtil;
15+import org.springframework.stereotype.Service;
16+ 
17+import org.opengauss.plugin.agent.metric.OSmetric;
18+ 
19+import io.prometheus.client.Collector.Type;
20+ 
21+@Service("network_socket")
22+public class Socket implements OSmetric {
23+ private static final String SPLIT = ",";
24+ 
25+ @Override
26+ public Map<String, Metric> getMetric(Integer dbPort) throws FileNotFoundException, IOException {
27+ // TODO
28+// try {
29+// CmdUtil.readFromCmd(CmdUtil.cmd("ss -e"), line -> {
30+//
31+// });
32+// } catch (IOException e) {
33+ Map<String, Integer> counter = new HashMap<>();
34+ CmdUtil.readFromCmd(CmdUtil.cmd("netstat"), (index, line) -> {
35+ if (index < 2)
36+ return;
37+ var part = StringUtil.splitByBlank(line);
38+ if (part.length < 5)
39+ return;
40+ var proto = part[0];
41+ if (proto.startsWith("tcp") || proto.startsWith("udp")) {
42+ var key = proto + SPLIT + part[5];
43+ counter.put(key, counter.getOrDefault(key, 0) + 1);
44+ }
45+ });
46+// }
47+ var metric = new Metric(Type.GAUGE, Arrays.asList("proto", "state"));
48+ counter.forEach((key, value) -> {
49+ var part = key.split(SPLIT);
50+ metric.addValue(Arrays.asList(part), value);
51+ });
52+ return Map.of("", metric);
53+ }
54+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/server/HostMetric.java+146-0
@@ -0,0 +1,146 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.server;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.ArrayList;
9+import java.util.Arrays;
10+import java.util.Collections;
11+import java.util.HashMap;
12+import java.util.List;
13+import java.util.Map;
14+ 
15+import org.opengauss.plugin.agent.metric.Ametric;
16+import org.opengauss.plugin.agent.metric.DBmetric;
17+import org.opengauss.plugin.agent.metric.Metric;
18+import org.opengauss.plugin.agent.util.StringUtil;
19+import org.springframework.beans.factory.annotation.Value;
20+import org.springframework.boot.ApplicationArguments;
21+import org.springframework.boot.ApplicationRunner;
22+import org.springframework.core.annotation.Order;
23+import org.springframework.scheduling.annotation.Scheduled;
24+import org.springframework.stereotype.Component;
25+ 
26+import cn.hutool.core.util.StrUtil;
27+import io.prometheus.client.Collector;
28+import io.prometheus.client.Collector.MetricFamilySamples;
29+import io.prometheus.client.Collector.MetricFamilySamples.Sample;
30+import io.prometheus.client.CollectorRegistry;
31+import io.prometheus.client.exporter.HTTPServer;
32+import lombok.Data;
33+import lombok.RequiredArgsConstructor;
34+import lombok.experimental.Accessors;
35+import lombok.extern.log4j.Log4j2;
36+ 
37+@Component
38+@Order(Integer.MAX_VALUE)
39+@RequiredArgsConstructor
40+@Log4j2
41+@Data
42+@Accessors(chain = true)
43+public class HostMetric implements ApplicationRunner {
44+ private final Map<String, Ametric> collectors;
45+ private static final List<String> OS_LABEL_NAMES = Arrays.asList("host");
46+ private static final List<String> DB_LABEL_NAMES = Arrays.asList("instanceId");
47+ private static final Map<String, List<MetricFamilySamples>> CACHE = new HashMap<>();
48+ 
49+ @Value("${exporter.port}")
50+ private Integer exporterPort;
51+ @Value("${conf.hostId:#{null}}")
52+ private String hostId;
53+ @Value("${conf.node.nodeId:#{null}}")
54+ private String nodeId;
55+ @Value("${conf.node.dbport:#{null}}")
56+ private Integer dbport;
57+ @Value("${conf.node.dbUsername:#{null}}")
58+ private String dbUsername;
59+ @Value("${conf.node.dbPassword:#{null}}")
60+ private String dbPassword;
61+ 
62+ @Override
63+ public void run(ApplicationArguments args) throws Exception {
64+ if (exporterPort == null)
65+ return;
66+ collectors.forEach((collectorName, collector) -> {
67+ Map<String, Metric> metric;
68+ try {
69+ metric = collector.getMetric(null);
70+ if (metric == null) {
71+ log.error("metric null:{}", collectorName);
72+ return;
73+ }
74+ } catch (FileNotFoundException e) {
75+ log.error("collector:{}", collectorName, e);
76+ return;
77+ } catch (IOException e) {
78+ log.error("collector:{}", collectorName, e);
79+ return;
80+ }
81+ CollectorRegistry.defaultRegistry.register(new Collector() {
82+ @Override
83+ public List<MetricFamilySamples> collect() {
84+ return CACHE.getOrDefault(collectorName, Collections.emptyList());
85+ }
86+ });
87+ });
88+ // c.unregister(cpuCollector);
89+ new HTTPServer.Builder().withPort(exporterPort).build();
90+ }
91+ 
92+ private static String formatKey(String collectorName, Ametric collector, String k) {
93+ return StringUtil.replaceParenthesis((collector instanceof DBmetric) ? k : "agent_" + collectorName + k);
94+ }
95+ 
96+ @Scheduled(fixedRate = 1000)
97+ public void cache() {
98+ try {
99+ collectors.forEach((collectorName, collector) -> {
100+ if (StrUtil.isBlank(hostId))
101+ return;
102+ if (collector instanceof DBmetric && (StrUtil.isBlank(nodeId) || dbport == null))
103+ return;
104+ var list = new ArrayList<MetricFamilySamples>();
105+ try {
106+ collector.getMetric(dbport).forEach((k, v) -> {
107+ if (k == null)
108+ return;
109+ final String key = formatKey(collectorName, collector, k);
110+ var samples = new ArrayList<Sample>();
111+ List<String> labelNames = new ArrayList<String>();
112+ labelNames.addAll(v.getLabelNames());
113+ // global label
114+ labelNames.addAll(OS_LABEL_NAMES);
115+ if (collector instanceof DBmetric)
116+ labelNames.addAll(DB_LABEL_NAMES);
117+ v.getValues().forEach(value -> {
118+ List<String> labelValues = new ArrayList<String>();
119+ labelValues.addAll(value.getLabelValues());
120+ // global value
121+ labelValues.addAll(Arrays.asList(hostId));
122+ if (collector instanceof DBmetric)
123+ labelValues.addAll(Arrays.asList(nodeId));
124+ if (labelNames.size() != labelValues.size()) {
125+ log.error("collector {} metric {} error:{}{}", collectorName, key, labelNames,
126+ labelValues);
127+ return;
128+ }
129+ samples.add(new Sample(key, labelNames, labelValues, value.getValue()));
130+ });
131+ String help = v.getHelp();
132+ if (StrUtil.isBlank(help)) {
133+ help = StrUtil.upperFirst(collectorName) + " information field " + k;
134+ }
135+ list.add(new MetricFamilySamples(key, v.getType(), help, samples));
136+ });
137+ } catch (IOException e) {
138+ log.error("collect fail:{}", collectorName);
139+ }
140+ CACHE.put(collectorName, list);
141+ });
142+ } catch (Exception e) {
143+ log.error("metric cache err", e);
144+ }
145+ }
146+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/CmdUtil.java+70-0
@@ -0,0 +1,70 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.util;
5+ 
6+import java.io.BufferedReader;
7+import java.io.FileNotFoundException;
8+import java.io.IOException;
9+import java.io.InputStreamReader;
10+import java.util.Arrays;
11+import java.util.function.BiConsumer;
12+import java.util.function.Consumer;
13+ 
14+import lombok.extern.log4j.Log4j2;
15+ 
16+@Log4j2
17+public class CmdUtil {
18+ public static final String[] cmd(String... cmd) {
19+ return cmd;
20+ }
21+ 
22+ public static final void readFromCmd(String cmd, Consumer<String> consumer)
23+ throws FileNotFoundException, IOException {
24+ readFromCmd(Arrays.asList("sh", "-c", cmd).toArray(new String[3]), consumer);
25+ }
26+ 
27+ public static final void readFromCmd(String cmd, BiConsumer<Integer, String> consumer)
28+ throws FileNotFoundException, IOException {
29+ readFromCmd(Arrays.asList("sh", "-c", cmd).toArray(new String[3]), consumer);
30+ }
31+ 
32+ public static final void readFromCmd(String[] cmd, Consumer<String> consumer)
33+ throws FileNotFoundException, IOException {
34+ if (consumer != null)
35+ readFromCmd(cmd, (i, line) -> consumer.accept(line));
36+ }
37+ 
38+ public static final void readFromCmd(String[] cmd, BiConsumer<Integer, String> consumer)
39+ throws FileNotFoundException, IOException {
40+ var process = new ProcessBuilder().command(cmd).start();
41+ try {
42+ process.waitFor();
43+ } catch (InterruptedException e) {
44+ log.error("", e);
45+ return;
46+ }
47+ try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()));) {
48+ int i = -1;
49+ while (reader.ready()) {
50+ i++;
51+ String line = reader.readLine();
52+ if (line.isBlank())
53+ continue;
54+ if (consumer != null)
55+ consumer.accept(i, line);
56+ }
57+ } catch (Exception e) {
58+ log.error("cmd '{}' format error", cmd, e);
59+ throw e;
60+ }
61+ }
62+ 
63+ public static final String readFromCmd(String cmd) throws FileNotFoundException, IOException {
64+ StringBuilder str = new StringBuilder();
65+ readFromCmd(cmd, line -> {
66+ str.append(line);
67+ });
68+ return str.toString();
69+ }
70+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/DbUtil.java+61-0
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+ 
5+package org.opengauss.plugin.agent.util;
6+ 
7+import java.sql.Connection;
8+import java.sql.DriverManager;
9+import java.sql.SQLException;
10+import java.util.ArrayList;
11+import java.util.Collections;
12+import java.util.HashMap;
13+import java.util.List;
14+import java.util.Map;
15+ 
16+import org.opengauss.plugin.agent.server.HostMetric;
17+import org.springframework.stereotype.Component;
18+ 
19+import lombok.AllArgsConstructor;
20+import lombok.extern.log4j.Log4j2;
21+ 
22+@Component
23+@Log4j2
24+@AllArgsConstructor
25+public class DbUtil {
26+ private static Connection conn;
27+ 
28+ private final HostMetric hostMetric;
29+ 
30+ public final List<Map<String, Object>> query(String sql) {
31+ synchronized (conn) {
32+ try {
33+ conn = DriverManager.getConnection("jdbc:opengauss://" + "localhost" + ":" + hostMetric.getDbport()
34+ + "/" + "postgres" + "?TimeZone=UTC", hostMetric.getDbUsername(), hostMetric.getDbPassword());
35+ } catch (SQLException e) {
36+ conn = null;
37+ }
38+ }
39+ try {
40+ var stmt = conn.createStatement();
41+ var rs = stmt.executeQuery(sql);
42+ var rsmeta = rs.getMetaData();
43+ List<Map<String, Object>> list = new ArrayList<>();
44+ while (rs.next()) {
45+ Map<String, Object> map = new HashMap<>();
46+ for (int i = 0; i < rsmeta.getColumnCount(); i++) {
47+ var obj = rs.getObject(i + 1);
48+ if (obj == null) {
49+ obj = "";
50+ }
51+ map.put(rsmeta.getColumnName(i + 1), obj);
52+ }
53+ list.add(map);
54+ }
55+ return list;
56+ } catch (SQLException e) {
57+ log.error("sql error, cause :{} sql:{}", e.getMessage(), sql);
58+ return Collections.emptyList();
59+ }
60+ }
61+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/FileUtil.java+28-0
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.util;
5+ 
6+import java.io.FileNotFoundException;
7+import java.io.IOException;
8+import java.util.function.BiConsumer;
9+import java.util.function.Consumer;
10+ 
11+public class FileUtil {
12+ /**
13+ * read file to line skip blank line
14+ */
15+ public static final void readFileLine(String name, Consumer<String> consumer)
16+ throws FileNotFoundException, IOException {
17+ if (consumer != null)
18+ readFileLine(name, (i, line) -> consumer.accept(line));
19+ }
20+ 
21+ /**
22+ * read file to line skip blank line
23+ */
24+ public static final void readFileLine(String name, BiConsumer<Integer, String> consumer)
25+ throws FileNotFoundException, IOException {
26+ CmdUtil.readFromCmd(CmdUtil.cmd("cat", name), consumer);
27+ }
28+}
Aplugins/observability-instance/InstanceExporter/src/main/java/org/opengauss/plugin/agent/util/StringUtil.java+22-0
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (c) GBA-NCTI-ISDC. 2022-2023. All rights reserved.
3+ */
4+package org.opengauss.plugin.agent.util;
5+ 
6+public class StringUtil {
7+ public static final String[] splitByBlank(String str) {
8+ return str.trim().split("\\s+");
9+ }
10+ 
11+ /**
12+ * replace "()" and " " to "_" and remove last ":", example: "Active(anon):" ->
13+ * "Active_anon"
14+ */
15+ public static final String replaceParenthesis(String str) {
16+ return str.split(":")[0].replaceAll(" ", "_").replaceAll("\\((.*)\\)", "_$1").replaceAll("__", "_");
17+ }
18+ 
19+ public static final String removeLast(String str) {
20+ return str.substring(0, str.length() - 1);
21+ }
22+}
Aplugins/observability-instance/InstanceExporter/src/main/resources/application.yml+10-0
Aplugins/observability-instance/InstanceExporter/src/main/resources/logbak-spring.xml+34-0
Rplugins/observability-instance/files/og_exporter.ymlplugins/observability-instance/InstanceExporter/src/main/resources/og_exporter.yml+66-4
Mplugins/observability-instance/README.md+47-43
Mplugins/observability-instance/doc/image-20221220112324.png+0-0
Aplugins/observability-instance/doc/image-20230613093001904.png+0-0
Aplugins/observability-instance/doc/image-20230613094239374.png+0-0
Aplugins/observability-instance/doc/image-20230613094340456.png+0-0
Aplugins/observability-instance/doc/image-20230613094354675.png+0-0
Aplugins/observability-instance/doc/image-20230613094404839.png+0-0
Aplugins/observability-instance/doc/image-20230613094413660.png+0-0
Aplugins/observability-instance/doc/image-20230613094523139.png+0-0
Aplugins/observability-instance/doc/image-20230613094624547.png+0-0
Aplugins/observability-instance/doc/image-20230613113127939.png+0-0
Aplugins/observability-instance/doc/image-20230613113228968.png+0-0
Aplugins/observability-instance/doc/image-20230613113238400.png+0-0
Aplugins/observability-instance/doc/image-20230613113252448.png+0-0
Aplugins/observability-instance/doc/image-20230613113301228.png+0-0
Aplugins/observability-instance/doc/image-20230613113414520.png+0-0
Aplugins/observability-instance/doc/image-20230613113423830.png+0-0
Aplugins/observability-instance/doc/image-20230613113511602.png+0-0
Mplugins/observability-instance/doc/instance_install_btn.png+0-0
Mplugins/observability-instance/doc/instance_install_offline.png+0-0
Mplugins/observability-instance/doc/instance_install_offline_upload.png+0-0
Mplugins/observability-instance/doc/instance_install_proxy.png+0-0
Mplugins/observability-instance/doc/instance_install_server_offline.png+0-0
Mplugins/observability-instance/doc/instance_proxy_install_online.png+0-0
Mplugins/observability-instance/doc/instance_uninstall_btn.png+0-0
Mplugins/observability-instance/doc/instance_uninstall_proxy_form.png+0-0
Mplugins/observability-instance/doc/instance_uninstall_server_form.png+0-0
Dplugins/observability-instance/files/prometheus.yml+0-44
Mplugins/observability-instance/pom.xml+323-310
Dplugins/observability-instance/src/main/java/com/nctigba/common/mybatis/BasePageDTO.java+0-200
Dplugins/observability-instance/src/main/java/com/nctigba/common/mybatis/MyPage.java+0-100
Dplugins/observability-instance/src/main/java/com/nctigba/common/web/config/ControllerConfig.java+0-14
Dplugins/observability-instance/src/main/java/com/nctigba/common/web/config/package-info.java+0-21
Aplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/BaseI18nException.java+59-0
Mplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/CustomException.java+33-30
Mplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/CustomExceptionEnum.java+17-15
Mplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/InstanceException.java+32-12
Aplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/InstanceExceptionMsgEnum.java+20-0
Dplugins/observability-instance/src/main/java/com/nctigba/common/web/exception/ResponseCode.java+0-98
Dplugins/observability-instance/src/main/java/com/nctigba/common/web/model/RestResult.java+0-32
Dplugins/observability-instance/src/main/java/com/nctigba/common/web/model/query/InstanceQuery.java+0-15
Dplugins/observability-instance/src/main/java/com/nctigba/common/web/model/query/PageBaseQuery.java+0-18
Mplugins/observability-instance/src/main/java/com/nctigba/common/web/result/AppResult.java+38-79
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/ObservabilityPluginApplication.java+4-2
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/AutoFillMetaObjectHandler.java+0-50
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/DataSourceConfig.java+19-16
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/MybatisPlusConfig.java+9-6
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/ParamInfoInitConfig.java+176-173
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/PluginExtensionInfoConfig.java+15-12
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/BaseConstance.java+0-19
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/CommonConstants.java+37-32
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/DatabaseType.java+9-6
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/DriverConstants.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/JdbcConnectConstants.java+3-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MetricsLine.java+138-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MetricsValue.java+86-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MonitoringConstants.java+4-1
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MonitoringResultType.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/MonitoringType.java+4-1
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/constants/OSType.java+3-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ControllerConfig.java+29-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/EnvironmentController.java+151-169
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/IndexController.java+48-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/Installer.java+64-61
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/MonitoringController.java+26-18
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/PageController.java+181-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ParamInfoController.java+3-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ResourceCPUController.java+97-0
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/ServerInfoController.java+0-34
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/SessionController.java+115-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/TopSQLController.java+31-27
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/controller/WdrController.java+3-0
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/param/DatabaseParamDTO.java+0-89
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/param/OsParamDTO.java+0-89
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/param/ParamInfoDTO.java+22-19
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/server/ServerInfoReq.java+3-1
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/server/ServerInfoResp.java+0-22
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/session/BlockTreeDto.java+22-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/session/DetailStatisticDto.java+24-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/topsql/TopSQLInfoReq.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/topsql/TopSQLListReq.java+3-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/dto/topsql/TopSQLNowReq.java+17-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/NctigbaEnv.java+42-18
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/OpsWdrEntity.java+65-62
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/ParamInfo.java+35-32
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/ParamValueInfo.java+29-26
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/Searc.java+0-48
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/entity/ServerInfoEntity.java+4-2
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/MonitoringHandlerFactory.java+11-4
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/OSHandlerFactory.java+0-50
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/InstanceHandlerFactory.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/SessionHandlerFactory.java+23-27
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/factory/TopSQLHandlerFactory.java+8-2
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/instance/InstanceHandler.java+0-33
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/instance/NormalInstanceHandler.java+0-90
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/monitoring/MonitoringHandler.java+4-1
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/monitoring/NormalMonitoringHandler.java+327-324
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/os/NormalOSHandler.java+0-22
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/os/OSHandler.java+0-22
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/session/OpenGaussSessionHandler.java+394-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/session/SessionHandler.java+36-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/topsql/OpenGaussTopSQLHandler.java+102-59
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/handler/topsql/TopSQLHandler.java+27-6
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/listener/PluginListener.java+14-5
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/DbConfigMapper.java+22-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/NctigbaEnvMapper.java+4-1
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/OpsWdrMapper.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/ParamInfoMapper.java+46-43
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/ParamValueInfoMapper.java+73-70
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/ServerInfoMapper.java+4-1
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/mapper/TopSqlMapper.java+13-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/DictionaryConfig.java+21-18
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/ExecutionPlan.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/IndexAdvice.java+3-1
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/InstanceInfo.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/InstanceNodeInfo.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/PartitionDataResp.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/ResInfo.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/WdrGeneratorBody.java+14-11
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/WdrSnapshotVO.java+9-6
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/monitoring/MonitoringMetric.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/monitoring/MonitoringParam.java+3-0
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/DatabaseParamData.java+0-58
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/OsParamData.java+0-94
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/model/param/ParamQuery.java+6-3
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/pool/PoolManager.java+0-86
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/pool/SSHPoolManager.java+0-78
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/AbstractInstaller.java+86-83
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterManager.java+156-150
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterOpsProviderManager.java+0-66
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ExporterInstallService.java+337-0
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ExporterService.java+0-303
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/IServerInfoService.java+3-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/MetricsService.java+201-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/MonitoringService.java+3-1
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/OpsWdrService.java+333-328
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ParamInfoService.java+3-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/PrometheusService.java+208-188
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/SessionService.java+28-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/TopSQLService.java+189-44
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/MonitoringServiceImpl.java+8-3
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/ServerInfoServiceImpl.java+0-41
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/SessionServiceImpl.java+229-0
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/impl/TopSQLServiceImpl.java+0-124
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/AbstractOpsProvider.java+231-230
Rplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/ClusterOpsProvider.javaplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/ClusterOpsProvider.java+8-5
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/ClusterOpsProviderManager.java+69-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/EnterpriseOpsProvider.java+37-34
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/LiteOpsProvider.java+44-41
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/MinimaListOpsProvider.java+47-44
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerArch64EnterpriseOpsProvider.java+38-35
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerArch64LiteOpsProvider.java+38-35
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerArch64MinimaListOpsProvider.java+47-44
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerX86EnterpriseOpsProvider.java+38-35
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerX86LiteOpsProvider.java+38-35
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/service/provider/OpenEulerX86MinimaListOpsProvider.java+47-44
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/Download.java+20-17
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/HttpUtils.java+8-4
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/JschUtil.java+442-445
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/Language.java+28-0
Aplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/ListUtil.java+34-0
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/MessageSourceUtil.java+64-49
Dplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/SSHOperator.java+0-245
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/SshSession.java+105-94
Mplugins/observability-instance/src/main/java/com/nctigba/observability/instance/util/YamlUtil.java+19-16
Mplugins/observability-instance/src/main/resources/application-dev.yml+4-0
Mplugins/observability-instance/src/main/resources/application-prod.yml+4-0
Mplugins/observability-instance/src/main/resources/application.yml+4-0
Mplugins/observability-instance/src/main/resources/messages.properties+178-189
Mplugins/observability-instance/src/main/resources/messages_en_US.properties+181-189
Mplugins/observability-instance/src/main/resources/messages_zh_CN.properties+208-184
Mplugins/observability-instance/src/main/resources/og_exporter.yml+4-0
Mplugins/observability-instance/src/main/resources/schema-all.sql+4-0
Mplugins/observability-instance/src/test/java/com/nctigba/observability/instance/MonitoringServiceTest.java+7-4
Mplugins/observability-instance/src/test/java/com/nctigba/observability/instance/ObservabilityPluginApplicationTests.java+6-3
Mplugins/observability-instance/src/test/java/com/nctigba/observability/instance/TopSQLServiceTest.java+8-5
Mplugins/observability-instance/web-ui/.eslintrc.json+10-11
Mplugins/observability-instance/web-ui/.prettierrc.json+3-1
Mplugins/observability-instance/web-ui/index.html+6-1
Mplugins/observability-instance/web-ui/package-lock.json+304-304
Mplugins/observability-instance/web-ui/package.json+1-1
Mplugins/observability-instance/web-ui/pnpm-lock.yaml+4-0
Aplugins/observability-instance/web-ui/src/api/observability.ts+343-0
Mplugins/observability-instance/web-ui/src/api/prometheus.ts+74-48
Aplugins/observability-instance/web-ui/src/api/session.ts+77-0
Aplugins/observability-instance/web-ui/src/api/sqlDetail.ts+20-0
Aplugins/observability-instance/web-ui/src/api/sqlDiagnosis.ts+9-0
Aplugins/observability-instance/web-ui/src/assets/style/color.module.scss+33-0
Mplugins/observability-instance/web-ui/src/assets/style/color.scss+15-30
Mplugins/observability-instance/web-ui/src/assets/style/common.scss+215-3
Mplugins/observability-instance/web-ui/src/assets/style/dark.scss+60-33
Mplugins/observability-instance/web-ui/src/assets/style/reset.scss+79-61
Mplugins/observability-instance/web-ui/src/assets/style/style1.scss+6-17
Mplugins/observability-instance/web-ui/src/assets/style/theme.scss+47-21
Aplugins/observability-instance/web-ui/src/assets/svg/detail.svg+3-0
Aplugins/observability-instance/web-ui/src/assets/svg/info-hollow.svg+11-0
Aplugins/observability-instance/web-ui/src/assets/svg/tips.svg+3-0
Mplugins/observability-instance/web-ui/src/components.d.ts+6-3
Mplugins/observability-instance/web-ui/src/components/ClusterCascader.vue+60-47
Aplugins/observability-instance/web-ui/src/components/IndexBar.vue+105-0
Mplugins/observability-instance/web-ui/src/components/MyCard.vue+101-108
Aplugins/observability-instance/web-ui/src/components/MyCard2.vue+224-0
Mplugins/observability-instance/web-ui/src/components/MyDatePicker.vue+131-95
Mplugins/observability-instance/web-ui/src/components/MyPie.vue+88-76
Aplugins/observability-instance/web-ui/src/components/WDRBar.vue+84-0
Aplugins/observability-instance/web-ui/src/components/echarts/IndexLine.vue+343-0
Aplugins/observability-instance/web-ui/src/components/echarts/LazyLine.vue+369-0
Mplugins/observability-instance/web-ui/src/hooks/dialog.ts+4-0
Mplugins/observability-instance/web-ui/src/hooks/dom.ts+4-0
Mplugins/observability-instance/web-ui/src/hooks/echarts.ts+33-26
Aplugins/observability-instance/web-ui/src/hooks/global.ts+18-0
Mplugins/observability-instance/web-ui/src/hooks/time.ts+7-2
Mplugins/observability-instance/web-ui/src/i18n/en.ts+276-55
Mplugins/observability-instance/web-ui/src/i18n/index.ts+4-0
Mplugins/observability-instance/web-ui/src/i18n/zhCn.ts+274-67
Mplugins/observability-instance/web-ui/src/main.ts+18-14
Mplugins/observability-instance/web-ui/src/pages/dashboard/Index.vue+1-1
Mplugins/observability-instance/web-ui/src/pages/dashboard/install/installAgent.vue+173-434
Mplugins/observability-instance/web-ui/src/pages/dashboard/install/uninstallProxy.vue+0-3
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/Index.vue+214-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/common.ts+39-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/Index.vue+66-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/instanceMetrics/Index.vue+249-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/sessionMonitor/Detail.vue+548-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/sessionMonitor/Index.vue+387-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/instanceMonitor/topSQL/Index.vue+176-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/performanceLoad/Index.vue+651-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/CPU/Index.vue+332-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/IO/Index.vue+402-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/Index.vue+102-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/memory/Index.vue+414-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/resourceMonitor/network/Index.vue+315-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/systemConfiguration/Index.vue+367-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/systemConfiguration/password.vue+87-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/wdr/Index.vue+329-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/wdr/buildWdr.vue+236-0
Aplugins/observability-instance/web-ui/src/pages/dashboardV2/wdr/snapshotManage.vue+194-0
Mplugins/observability-instance/web-ui/src/pages/sql_detail/Index.vue+84-58
Mplugins/observability-instance/web-ui/src/pages/sql_detail/object_information/common.ts+4-0
Mplugins/observability-instance/web-ui/src/pages/sql_detail/statistical_information/common.ts+4-0
Aplugins/observability-instance/web-ui/src/pages/sql_detail/waitEvent/Index.vue+90-0
Mplugins/observability-instance/web-ui/src/request/axios.ts+4-0
Mplugins/observability-instance/web-ui/src/request/diagnosis.ts+4-0
Mplugins/observability-instance/web-ui/src/request/index.ts+82-45
Mplugins/observability-instance/web-ui/src/request/indexEb.ts+3-0
Mplugins/observability-instance/web-ui/src/request/platform.ts+4-0
Mplugins/observability-instance/web-ui/src/request/restful.ts+4-0
Mplugins/observability-instance/web-ui/src/router/index.ts+53-30
Mplugins/observability-instance/web-ui/src/shared.ts+4-0
Mplugins/observability-instance/web-ui/src/store/monitor.ts+178-60
Mplugins/observability-instance/web-ui/src/store/window.ts+4-0
Aplugins/observability-instance/web-ui/src/utils/date.ts+60-0
Mplugins/observability-instance/web-ui/src/utils/jsencrypt.ts+4-0
Mplugins/observability-instance/web-ui/src/utils/websocket.ts+6-1
Mplugins/observability-instance/web-ui/src/vite-env.d.ts+4-0
Mplugins/observability-instance/web-ui/tsconfig.json+22-17
Mplugins/observability-instance/web-ui/vite.config.ts+10-5
Aplugins/observability-instance/web-ui/yarn.lock+4090-0
Mplugins/observability-sql-diagnosis/pom.xml+25-2
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/common/web/exception/CustomException.java+60-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/common/web/exception/CustomExceptionEnum.java+34-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/common/web/exception/HisDiagnosisException.java+29-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/config/ElasticsearchProvider.java+70-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/config/history/HisDiagnosisInit.java+170-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/AgentParamCommon.java+15-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/DiagnosisTypeCommon.java+16-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/ElasticSearchCommon.java+25-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/MetricCommon.java+23-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/OptionCommon.java+33-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/PrometheusConstants.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/SqlCommon.java+33-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/constants/history/ThresholdCommon.java+25-0
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/controller/EnvironmentController.java+56-57
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/controller/HisDiagnosisController.java+47-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/controller/HisDiagnosisTaskController.java+56-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/controller/HisThresholdController.java+51-0
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/listener/PluginListener.java+30-27
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/mapper/history/HisDiagnosisResultMapper.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/mapper/history/HisDiagnosisTaskMapper.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/mapper/history/HisThresholdMapper.java+22-0
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/NctigbaEnv.java+40-21
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/DataStoreConfig.java+25-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/HisDiagnosisResult.java+91-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/HisDiagnosisTask.java+132-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/HisDiagnosisThreshold.java+57-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/data/AgentData.java+23-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/data/DatabaseData.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/data/ElasticSearchData.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/data/PrometheusData.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/AgentDTO.java+29-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/AnalysisDTO.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/HisDiagnosisTaskDTO.java+26-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/HisThresholdDTO.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/LockAnalysisDTO.java+23-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/LogDetailInfoDTO.java+26-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/LogInfoDTO.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/MetricToTableDTO.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/OptionDTO.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/dto/WaitEventInfo.java+23-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/AspAnalysisDTO.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/BusinessConnCountDTO.java+25-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/CurrentCpuDTO.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/CurrentCpuUsageDTO.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/LockDTO.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/MetricDataDTO.java+23-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/PrometheusDataDTO.java+26-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/point/WaitEventDTO.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/query/EsSearchQuery.java+73-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/query/HisThresholdQuery.java+23-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/query/MonitoringParam.java+40-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/query/OptionQuery.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/result/HisTreeNode.java+83-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/result/Node.java+32-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/model/history/result/TaskState.java+23-0
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/AbstractInstaller.java+3-3
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/AgentService.java+197-198
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/ClusterManager.java+2-2
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/diagnosis/TaskCreator.java+63-67
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/diagnosis/analysis/OffCpuAnaly.java+1-1
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/diagnosis/analysis/OnCpuAnaly.java+1-1
Mplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/diagnosis/caller/BccCaller.java+109-112
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/DataStoreService.java+25-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/HisDiagnosisPointService.java+32-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/HisDiagnosisService.java+21-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/HisThresholdService.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/Impl/DataStoreServiceImpl.java+56-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/Impl/HisDiagnosisServiceImpl.java+225-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/Impl/HisThresholdServiceImpl.java+71-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/Impl/TaskServiceImpl.java+292-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/TaskService.java+24-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/CollectionItem.java+18-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/agent/AgentCollectionItem.java+31-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/agent/CurrentCpuUsageItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/agent/DbProcessCurrentCpuItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/agent/OtherProcessCurrentCpuItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/agent/TopDbProcessItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/elastic/DeadlockItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/elastic/ElasticSearchCollectionItem.java+168-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/elastic/LockTimeoutItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/ActivityNumItem.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/ActivityNumItem2.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/AvgCpuItem.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/DbAvgCpuItem.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/PrometheusCollectionItem.java+110-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/ThreadPoolUsageItem.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/ThreadPoolUsageItem2.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/metric/WaitEventItem.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/BusinessConnCountItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/CpuTimeTopSqlItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/DatabaseCollectionItem.java+95-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/PoorSqlItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/SlowSqlItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/ThreadWaitEventItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/collection/table/TopCpuTimeSqlItem.java+22-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/AspAnalysis.java+241-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/AvgCpuUsageRate.java+104-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/BusinessConnCount.java+311-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/CpuTimeTopSql.java+97-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/CurrentCpuUsage.java+104-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/CurrentSlowSql.java+94-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/CurrentWaitEvent.java+127-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/DatabaseHostLowPressure.java+125-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/DatabaseHostPressure.java+125-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/DbProcessAvgCpuUsage.java+105-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/DbProcessCurrentCpuUsage.java+109-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/DeadlockAnalysis.java+102-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/HistoryWaitEvent.java+163-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/LockAnalysis.java+51-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/LockTimeout.java+102-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/OtherProcessAvgCpuUsage.java+145-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/OtherProcessCurrentCpuUsage.java+106-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/PoorSql.java+47-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/TaskInfo.java+74-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/TopCpuTimeSql.java+97-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/TopDbProcess.java+84-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/WaitEventAnalysis.java+47-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/service/history/point/WdrAnalysis.java+160-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/AgentUtil.java+99-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/ComparatorUtil.java+43-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/DbUtil.java+74-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/EsLogSearchUtils.java+316-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/HostUtil.java+38-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/HttpUtils.java+75-0
Aplugins/observability-sql-diagnosis/src/main/java/com/nctigba/observability/sql/util/PrometheusUtil.java+248-0
Mplugins/observability-sql-diagnosis/src/main/resources/messages.properties+326-1
Mplugins/observability-sql-diagnosis/src/main/resources/messages_en_US.properties+108-1
Mplugins/observability-sql-diagnosis/src/main/resources/messages_zh_CN.properties+549-224
Mplugins/observability-sql-diagnosis/src/main/resources/opengauss-ebpf-1.0.0-SNAPSHOT.jar+0-0
Binary files do not support preview
Aplugins/observability-sql-diagnosis/src/main/resources/treeNode.txt+23-0
Mplugins/observability-sql-diagnosis/web-ui/.eslintrc.json+10-11
Mplugins/observability-sql-diagnosis/web-ui/.prettierrc.json+3-3
Mplugins/observability-sql-diagnosis/web-ui/index.html+1-1
Dplugins/observability-sql-diagnosis/web-ui/package-lock.json+0-13134
此文件变更行数或变更字符数较多,你可以直接
Mplugins/observability-sql-diagnosis/web-ui/src/App.vue+57-57
Aplugins/observability-sql-diagnosis/web-ui/src/api/historyDiagnosis.ts+57-0
Aplugins/observability-sql-diagnosis/web-ui/src/assets/style/color.module.scss+30-0
Mplugins/observability-sql-diagnosis/web-ui/src/assets/style/common.scss+238-6
Mplugins/observability-sql-diagnosis/web-ui/src/assets/style/dark.scss+65-27
Mplugins/observability-sql-diagnosis/web-ui/src/assets/style/reset.scss+96-81
Mplugins/observability-sql-diagnosis/web-ui/src/assets/style/style1.scss+0-17
Aplugins/observability-sql-diagnosis/web-ui/src/assets/style/task.scss+48-0
Mplugins/observability-sql-diagnosis/web-ui/src/assets/style/theme.scss+56-17
Aplugins/observability-sql-diagnosis/web-ui/src/assets/svg/cmd.svg+3-0
Aplugins/observability-sql-diagnosis/web-ui/src/assets/svg/expand.svg+14-0
Aplugins/observability-sql-diagnosis/web-ui/src/assets/svg/note.svg+4-0
Aplugins/observability-sql-diagnosis/web-ui/src/assets/svg/right.svg+3-0
Aplugins/observability-sql-diagnosis/web-ui/src/assets/svg/suggest.svg+3-0
Mplugins/observability-sql-diagnosis/web-ui/src/components.d.ts+3-3
Mplugins/observability-sql-diagnosis/web-ui/src/components/ClusterCascader.vue+58-43
Mplugins/observability-sql-diagnosis/web-ui/src/components/MyCard.vue+104-96
Mplugins/observability-sql-diagnosis/web-ui/src/components/MyDatePicker.vue+32-19
Mplugins/observability-sql-diagnosis/web-ui/src/components/MyPie.vue+90-76
Aplugins/observability-sql-diagnosis/web-ui/src/components/echarts/LazyLine.vue+390-0
Mplugins/observability-sql-diagnosis/web-ui/src/i18n/en.ts+153-19
Mplugins/observability-sql-diagnosis/web-ui/src/i18n/zhCn.ts+126-17
Mplugins/observability-sql-diagnosis/web-ui/src/pages/datasource/task_detail/Index.vue+79-19
Mplugins/observability-sql-diagnosis/web-ui/src/pages/datasource/task_detail/detail_large.vue+199-90
Mplugins/observability-sql-diagnosis/web-ui/src/pages/datasource/task_detail/task_info.vue+111-23
Mplugins/observability-sql-diagnosis/web-ui/src/pages/datasource/track/Index.vue+2-10
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/Index.vue+294-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/PointInfo.vue+182-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/AspAnalysis/Index.vue+159-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/AvgCpuUsageRate/Index.vue+121-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/BusinessConnCount/Index.vue+159-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/CpuTimeTopSql/Index.vue+137-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/CurrentCpuUsage/Index.vue+84-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/DbProcessAvgCpuUsage/Index.vue+121-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/DbProcessCurrentCpuUsage/Index.vue+119-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/OtherProcessAvgCpuUsage/Index.vue+129-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/OtherProcessCurrentCpuUsage/Index.vue+119-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/TopCpuTimeSql/Index.vue+135-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/TopDbProcess/Index.vue+119-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/CPU/WdrAnalysis/Index.vue+121-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/DBHostPress/Index.vue+163-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/Lock/DeadlockAnalysis/Index.vue+130-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/Lock/LockAnalysis/Index.vue+79-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/Lock/LockTimeout/Index.vue+130-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/SQL/CurrentSlowSql/Index.vue+130-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/SQL/HistorySlowSql/Index.vue+121-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/SQL/PoorSql/Index.vue+79-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/TaskInfo/Index.vue+216-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/WaitEvent/CurrentWaitEvent/Index.vue+125-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/WaitEvent/HistoryWaitEvent/Index.vue+125-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/diagnosisTask/detail/pointInfos/WaitEvent/WaitEventAnalysis/Index.vue+80-0
Aplugins/observability-sql-diagnosis/web-ui/src/pages/historyDiagnosis/Index.vue+381-0
Aplugins/observability-sql-diagnosis/web-ui/src/request/restful.ts+173-0
Mplugins/observability-sql-diagnosis/web-ui/src/router/index.ts+64-35
Aplugins/observability-sql-diagnosis/web-ui/src/utils/date.ts+55-0
Mplugins/observability-sql-diagnosis/web-ui/tsconfig.json+22-17
Mplugins/observability-sql-diagnosis/web-ui/vite.config.ts+4-3
Aplugins/observability-sql-diagnosis/web-ui/vite.config.ts.timestamp-1685070249073.mjs+69-0
Aplugins/observability-sql-diagnosis/web-ui/yarn.lock+4090-0