Просмотр исходного кода

[web-app] 告警配置列表信息展示

tomsun28 4 лет назад
Родитель
Сommit
25bf425617

+ 17 - 0
web-app/src/app/pojo/AlertDefine.ts

@@ -0,0 +1,17 @@
+export class AlertDefine {
+  id!: number;
+  app!: string;
+  metric!: string;
+  field!: string;
+  preset!: boolean;
+  expr!: string;
+  // 告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色
+  priority!: number;
+  duration!: number;
+  enable!: boolean;
+  template!: string;
+  creator!: string;
+  modifier!: string;
+  gmtCreate!: number;
+  gmtUpdate!: number;
+}

+ 1 - 0
web-app/src/app/routes/alert/alert-center/alert-center.component.html

@@ -0,0 +1 @@
+<p>alert-center works!</p>

+ 25 - 0
web-app/src/app/routes/alert/alert-center/alert-center.component.spec.ts

@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AlertCenterComponent } from './alert-center.component';
+
+describe('AlertCenterComponent', () => {
+  let component: AlertCenterComponent;
+  let fixture: ComponentFixture<AlertCenterComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ AlertCenterComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AlertCenterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 16 - 0
web-app/src/app/routes/alert/alert-center/alert-center.component.ts

@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-alert-center',
+  templateUrl: './alert-center.component.html',
+  styles: [
+  ]
+})
+export class AlertCenterComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit(): void {
+  }
+
+}

+ 1 - 0
web-app/src/app/routes/alert/alert-notice/alert-notice.component.html

@@ -0,0 +1 @@
+<p>alert-notice works!</p>

+ 25 - 0
web-app/src/app/routes/alert/alert-notice/alert-notice.component.spec.ts

@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AlertNoticeComponent } from './alert-notice.component';
+
+describe('AlertNoticeComponent', () => {
+  let component: AlertNoticeComponent;
+  let fixture: ComponentFixture<AlertNoticeComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ AlertNoticeComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AlertNoticeComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 16 - 0
web-app/src/app/routes/alert/alert-notice/alert-notice.component.ts

@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-alert-notice',
+  templateUrl: './alert-notice.component.html',
+  styles: [
+  ]
+})
+export class AlertNoticeComponent implements OnInit {
+
+  constructor() { }
+
+  ngOnInit(): void {
+  }
+
+}

+ 19 - 0
web-app/src/app/routes/alert/alert-routing.module.ts

@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import {AlertCenterComponent} from "./alert-center/alert-center.component";
+import {AlertSettingComponent} from "./alert-setting/alert-setting.component";
+import {AlertNoticeComponent} from "./alert-notice/alert-notice.component";
+
+const routes: Routes = [
+  { path: '', component: AlertCenterComponent },
+  { path: 'center', component: AlertCenterComponent },
+  { path: 'setting', component: AlertSettingComponent },
+  { path: 'notice', component: AlertNoticeComponent },
+  { path: '**', component: AlertCenterComponent }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class AlertRoutingModule { }

+ 98 - 0
web-app/src/app/routes/alert/alert-setting/alert-setting.component.html

@@ -0,0 +1,98 @@
+<nz-divider></nz-divider>
+<nz-breadcrumb>
+  <nz-breadcrumb-item>
+    <a [routerLink]="['/']">
+      <i nz-icon nzType="home"></i>
+    </a>
+  </nz-breadcrumb-item>
+  <nz-breadcrumb-item>
+    <i nz-icon nzType="alert"></i>
+    <span>告警阈值配置</span>
+  </nz-breadcrumb-item>
+</nz-breadcrumb>
+<nz-divider></nz-divider>
+
+<button nz-button nzType="primary" (click)="onNewAlertDefine()">
+  <i nz-icon nzType="appstore-add" nzTheme="outline"></i>
+  新增阈值
+</button>
+<button nz-button nzType="primary" (click)="onEditAlertDefine()" >
+  <i nz-icon nzType="edit" nzTheme="outline"></i>
+  编辑
+</button>
+<button nz-button nzType="primary" (click)="onDeleteAlertDefines()">
+  <i nz-icon nzType="delete" nzTheme="outline"></i>
+  删除
+</button>
+
+<nz-table #fixedTable [nzData]="defines"
+          [nzPageIndex]="pageIndex" [nzPageSize]="pageSize" [nzTotal]="total"
+          nzFrontPagination ="false"
+          [nzLoading] = "tableLoading"
+          nzShowSizeChanger
+          [nzShowTotal]="rangeTemplate"
+          [nzPageSizeOptions]="[8,15,25]"
+          (nzQueryParams)="onTablePageChange($event)"
+          nzShowPagination = "true" [nzScroll]="{ x: '1150px', y: '1240px' }">
+  <thead>
+  <tr>
+    <th nzAlign="center" nzLeft nzWidth="60px" [(nzChecked)]="checkedAll" (nzCheckedChange)="onAllChecked($event)"></th>
+    <th nzAlign="center">指标对象</th>
+    <th nzAlign="center">阈值触发表达式</th>
+    <th nzAlign="center">告警级别</th>
+    <th nzAlign="center">持续时间</th>
+    <th nzAlign="center">通知模版</th>
+    <th nzAlign="center">预置默认</th>
+    <th nzAlign="center">最新修改时间</th>
+    <th nzAlign="center" nzRight>操作</th>
+  </tr>
+  </thead>
+  <tbody>
+  <tr *ngFor="let data of fixedTable.data">
+    <td nzAlign="center" nzLeft [nzChecked]="checkedDefineIds.has(data.id)" (nzCheckedChange)="onItemChecked(data.id, $event)"></td>
+    <td nzAlign="center">
+        <span>{{ data.app + '.'  + data.metric + '.' + data.field }}</span>
+    </td>
+    <td nzAlign="center">
+      <span>{{ data.expr}}</span>
+    </td>
+    <td nzAlign="center">
+      <nz-tag *ngIf="data.priority == 0" nzColor="red">
+        <i nz-icon nzType="robot" nzTheme="outline"></i>
+        <span>紧急告警</span>
+      </nz-tag>
+      <nz-tag *ngIf="data.priority == 1" nzColor="orange">
+        <i nz-icon nzType="smile" nzTheme="outline"></i>
+        <span>严重告警</span>
+      </nz-tag>
+      <nz-tag *ngIf="data.priority == 2" nzColor="yellow">
+        <i nz-icon nzType="meh" nzTheme="outline"></i>
+        <span>警告告警</span>
+      </nz-tag>
+    </td>
+    <td nzAlign="center">{{ data.duration }}</td>
+    <td nzAlign="center">{{ data.template }}</td>
+    <td nzAlign="center">
+      <nz-tag *ngIf="data.preset" nzColor="green">
+        <span>是</span>
+      </nz-tag>
+      <nz-tag *ngIf="!data.preset" nzColor="orange">
+        <span>否</span>
+      </nz-tag>
+    </td>
+    <td nzAlign="center">{{ data.gmtUpdate? data.gmtUpdate : data.gmtCreate }}</td>
+    <td nzAlign="center" nzRight>
+      <button nz-button nzType="primary" (click)="onEditOneAlertDefine(data.id)">
+        <i nz-icon nzType="edit" nzTheme="outline"></i>
+      </button>
+      <button nz-button nzType="primary" (click)="onDeleteOneAlertDefine(data.id)">
+        <i nz-icon nzType="delete" nzTheme="outline"></i>
+      </button>
+    </td>
+  </tr>
+  </tbody>
+</nz-table>
+
+<ng-template #rangeTemplate>
+  总量 {{ total }}
+</ng-template>

+ 25 - 0
web-app/src/app/routes/alert/alert-setting/alert-setting.component.spec.ts

@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AlertSettingComponent } from './alert-setting.component';
+
+describe('AlertSettingComponent', () => {
+  let component: AlertSettingComponent;
+  let fixture: ComponentFixture<AlertSettingComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ AlertSettingComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AlertSettingComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 150 - 0
web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts

@@ -0,0 +1,150 @@
+import { Component, OnInit } from '@angular/core';
+import {NzTableQueryParams} from "ng-zorro-antd/table";
+import {ActivatedRoute, Router} from "@angular/router";
+import {NzModalService} from "ng-zorro-antd/modal";
+import {NzNotificationService} from "ng-zorro-antd/notification";
+import {NzMessageService} from "ng-zorro-antd/message";
+import {AlertDefineService} from "../../../service/alert-define.service";
+import {AlertDefine} from "../../../pojo/AlertDefine";
+
+@Component({
+  selector: 'app-alert-setting',
+  templateUrl: './alert-setting.component.html',
+  styles: [
+  ]
+})
+export class AlertSettingComponent implements OnInit {
+
+  constructor(private route: ActivatedRoute,
+              private router: Router,
+              private modal: NzModalService,
+              private notifySvc: NzNotificationService,
+              private msg: NzMessageService,
+              private alertDefineSvc: AlertDefineService) { }
+
+  pageIndex: number = 1;
+  pageSize: number = 8;
+  total: number = 0;
+  defines!: AlertDefine[];
+  tableLoading: boolean = true;
+  checkedDefineIds = new Set<number>();
+
+  ngOnInit(): void {
+    this.loadAlertDefineTable();
+  }
+
+  loadAlertDefineTable() {
+    this.tableLoading = true;
+    let alertDefineInit$ = this.alertDefineSvc.getAlertDefines(this.pageIndex - 1, this.pageSize)
+      .subscribe(message => {
+        this.tableLoading = false;
+        this.checkedAll = false;
+        this.checkedDefineIds.clear();
+        if (message.code === 0) {
+          let page = message.data;
+          this.defines = page.content;
+          this.pageIndex = page.number + 1;
+          this.total = page.totalElements;
+        } else {
+          console.warn(message.msg);
+        }
+        alertDefineInit$.unsubscribe();
+      }, error => {
+        this.tableLoading = false;
+        alertDefineInit$.unsubscribe();
+      });
+  }
+
+  onNewAlertDefine() {
+
+  }
+
+  onEditAlertDefine() {
+
+  }
+
+  onDeleteAlertDefines() {
+    if (this.checkedDefineIds == null || this.checkedDefineIds.size === 0) {
+      this.notifySvc.warning("未选中任何待删除项!","");
+      return;
+    }
+    this.modal.confirm({
+      nzTitle: '请确认是否批量删除!',
+      nzOkText: '确定',
+      nzCancelText: '取消',
+      nzOkDanger: true,
+      nzOkType: "primary",
+      nzOnOk: () => this.deleteAlertDefines(this.checkedDefineIds)
+    });
+  }
+
+  onDeleteOneAlertDefine(alertDefineId: number) {
+    let defineIds = new Set<number>();
+    defineIds.add(alertDefineId);
+    this.modal.confirm({
+      nzTitle: '请确认是否删除!',
+      nzOkText: '确定',
+      nzCancelText: '取消',
+      nzOkDanger: true,
+      nzOkType: "primary",
+      nzOnOk: () => this.deleteAlertDefines(defineIds)
+    });
+  }
+
+  onEditOneAlertDefine(alertDefineId: number) {
+
+  }
+
+
+  deleteAlertDefines(defineIds: Set<number>) {
+    if (defineIds == null || defineIds.size == 0) {
+      this.notifySvc.warning("未选中任何待删除项!","");
+      return;
+    }
+    this.tableLoading = true;
+    const deleteDefines$ = this.alertDefineSvc.deleteAlertDefines(defineIds)
+      .subscribe(message => {
+        deleteDefines$.unsubscribe();
+        if (message.code === 0) {
+          this.notifySvc.success("删除成功!", "");
+          this.loadAlertDefineTable();
+        } else {
+          this.tableLoading = false;
+          this.notifySvc.error("删除失败!", message.msg);
+        }
+      }, error => {
+        this.tableLoading = false;
+        deleteDefines$.unsubscribe();
+        this.notifySvc.error("删除失败!", error.msg)
+      })
+  }
+
+  // begin: 列表多选逻辑
+  checkedAll: boolean = false;
+  onAllChecked(checked: boolean) {
+    if (checked) {
+      this.defines.forEach(monitor => this.checkedDefineIds.add(monitor.id));
+    } else {
+      this.checkedDefineIds.clear();
+    }
+  }
+  onItemChecked(monitorId: number, checked: boolean) {
+    if (checked) {
+      this.checkedDefineIds.add(monitorId);
+    } else {
+      this.checkedDefineIds.delete(monitorId);
+    }
+  }
+  // end: 列表多选逻辑
+
+  /**
+   * 分页回调
+   * @param params 页码信息
+   */
+  onTablePageChange(params: NzTableQueryParams) {
+    const { pageSize, pageIndex, sort, filter } = params;
+    this.pageIndex = pageIndex;
+    this.pageSize = pageSize;
+    // this.loadMonitorTable();
+  }
+}

+ 27 - 0
web-app/src/app/routes/alert/alert.module.ts

@@ -0,0 +1,27 @@
+import { NgModule, Type } from '@angular/core';
+import { SharedModule } from '@shared';
+import { AlertRoutingModule } from './alert-routing.module';
+import {NzBreadCrumbModule} from "ng-zorro-antd/breadcrumb";
+import {NzDividerModule} from "ng-zorro-antd/divider";
+import {AlertCenterComponent} from "./alert-center/alert-center.component";
+import {AlertSettingComponent} from "./alert-setting/alert-setting.component";
+import {AlertNoticeComponent} from "./alert-notice/alert-notice.component";
+import {NzTagModule} from "ng-zorro-antd/tag";
+
+const COMPONENTS: Type<void>[] = [
+  AlertCenterComponent,
+  AlertSettingComponent,
+  AlertNoticeComponent
+];
+
+@NgModule({
+  imports: [
+    SharedModule,
+    AlertRoutingModule,
+    NzDividerModule,
+    NzBreadCrumbModule,
+    NzTagModule,
+  ],
+  declarations: COMPONENTS,
+})
+export class AlertModule { }

+ 4 - 6
web-app/src/app/routes/monitor/monitor-list/monitor-list.component.ts

@@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core';
 import {ActivatedRoute, Router} from "@angular/router";
 import {MonitorService} from "../../../service/monitor.service";
 import {Monitor} from "../../../pojo/Monitor";
-import {Page} from "../../../pojo/Page";
 import {NzModalService} from "ng-zorro-antd/modal";
 import {NzNotificationService} from "ng-zorro-antd/notification";
 import {NzMessageService} from "ng-zorro-antd/message";
@@ -28,7 +27,6 @@ export class MonitorListComponent implements OnInit {
   pageSize: number = 8;
   total: number = 0;
   monitors!: Monitor[];
-  pageMonitors!: Page<Monitor>;
   tableLoading: boolean = true;
   checkedMonitorIds = new Set<number>();
 
@@ -51,10 +49,10 @@ export class MonitorListComponent implements OnInit {
         this.checkedAll = false;
         this.checkedMonitorIds.clear();
         if (message.code === 0) {
-          this.pageMonitors = message.data;
-          this.monitors = this.pageMonitors.content;
-          this.pageIndex = this.pageMonitors.number + 1;
-          this.total = this.pageMonitors.totalElements;
+          let page = message.data;
+          this.monitors = page.content;
+          this.pageIndex = page.number + 1;
+          this.total = page.totalElements;
         } else {
           console.warn(message.msg);
         }

+ 2 - 1
web-app/src/app/routes/routes-routing.module.ts

@@ -26,7 +26,8 @@ const routes: Routes = [
       { path: '', redirectTo: 'dashboard', pathMatch: 'full'},
       { path: 'dashboard', component: DashboardComponent, data: { title: '仪表盘' } },
       { path: 'exception', loadChildren: () => import('./exception/exception.module').then(m => m.ExceptionModule) },
-      { path: 'monitors', loadChildren: () => import('./monitor/monitor.module').then((m) => m.MonitorModule) },]
+      { path: 'monitors', loadChildren: () => import('./monitor/monitor.module').then((m) => m.MonitorModule) },
+      { path: 'alert', loadChildren: () => import('./alert/alert.module').then((m) => m.AlertModule) },]
   },
   // 空白布局
   // {

+ 16 - 0
web-app/src/app/service/alert-define.service.spec.ts

@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AlertDefineService } from './alert-define.service';
+
+describe('AlertDefineService', () => {
+  let service: AlertDefineService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(AlertDefineService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});

+ 66 - 0
web-app/src/app/service/alert-define.service.ts

@@ -0,0 +1,66 @@
+import { Injectable } from '@angular/core';
+import {HttpClient, HttpParams} from "@angular/common/http";
+import {Observable} from "rxjs";
+import {Message} from "../pojo/Message";
+import {Page} from "../pojo/Page";
+import {AlertDefine} from "../pojo/AlertDefine";
+
+const alert_define_uri = "/alert/define";
+const alert_defines_uri = "/alert/defines";
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AlertDefineService {
+
+  constructor(private http : HttpClient) { }
+
+  public newAlertDefine(body: AlertDefine) : Observable<Message<any>> {
+    return this.http.post<Message<any>>(alert_define_uri, body);
+  }
+
+  public editAlertDefine(body: AlertDefine) : Observable<Message<any>> {
+    return this.http.put<Message<any>>(alert_define_uri, body);
+  }
+
+  public getAlertDefine(alertDefineId: number) : Observable<Message<AlertDefine>> {
+    return this.http.get<Message<AlertDefine>>(`${alert_define_uri}/${alertDefineId}`);
+  }
+
+  /**
+   * 应用告警定义与监控关联
+   * @param alertDefineId 告警定义ID
+   * @param monitorMap 关联的监控ID-监控名称
+   */
+  public applyAlertDefineMonitorsBind(alertDefineId: number,
+                                      monitorMap: Record<number, string>): Observable<Message<AlertDefine>> {
+    return this.http.post<Message<AlertDefine>>(`${alert_define_uri}/${alertDefineId}/monitors`, monitorMap);
+  }
+
+  public deleteAlertDefines(alertDefineIds: Set<number>) : Observable<Message<any>> {
+    let httpParams = new HttpParams();
+    alertDefineIds.forEach(alertDefineId => {
+      // 注意HttpParams是不可变对象 需要保存append后返回的对象为最新对象
+      // append方法可以叠加同一key, set方法会把key之前的值覆盖只留一个key-value
+      httpParams = httpParams.append('ids', alertDefineId);
+    })
+    const options = { params: httpParams };
+    return this.http.delete<Message<any>>(alert_defines_uri, options);
+  }
+
+  public getAlertDefines(pageIndex: number, pageSize: number) : Observable<Message<Page<AlertDefine>>> {
+    pageIndex = pageIndex ? pageIndex : 0;
+    pageSize = pageSize ? pageSize : 8;
+    // 注意HttpParams是不可变对象 需要保存set后返回的对象为最新对象
+    let httpParams = new HttpParams();
+    httpParams = httpParams.appendAll({
+      'sort': 'id',
+      'order': 'desc',
+      'pageIndex': pageIndex,
+      'pageSize': pageSize
+    });
+    const options = { params: httpParams };
+    return this.http.get<Message<Page<AlertDefine>>>(alert_defines_uri, options);
+  }
+
+}

+ 9 - 9
web-app/src/assets/tmp/app-data.json

@@ -90,38 +90,38 @@
           "text": "告警中心",
           "i18n": "menu.alert.center",
           "icon": "anticon-alert",
-          "link": "/style/typography"
+          "link": "/alert/center"
         },
         {
           "text": "告警配置",
           "i18n": "menu.alert.setting",
-          "icon": "anticon-setting",
-          "link": "/style/typography"
+          "icon": "anticon-bulb",
+          "link": "/alert/setting"
         },
         {
           "text": "告警转发",
           "i18n": "menu.alert.dispatch",
           "icon": "anticon-notification",
-          "link": "/style/typography"
+          "link": "/alert/notice"
         }
       ]
     },
     {
       "text": "More",
-      "i18n": "menu.more",
+      "i18n": "menu.extras",
       "group": true,
       "hideInBreadcrumb": true,
       "children": [
         {
           "text": "Help Center",
-          "link": "/extras/helpcenter",
-          "i18n": "menu.extras.helpcenter",
+          "link": "/extras/help",
+          "i18n": "menu.extras.help",
           "icon": "anticon-link"
         },
         {
           "text": "Settings",
-          "link": "/extras/settings",
-          "i18n": "menu.extras.settings",
+          "link": "/extras/setting",
+          "i18n": "menu.extras.setting",
           "icon": "anticon-setting"
         }
       ]

+ 5 - 0
web-app/src/assets/tmp/i18n/zh-CN.json

@@ -27,6 +27,11 @@
       "setting": "告警配置",
       "dispatch": "告警转发"
     },
+    "extras": {
+      "": "更多",
+      "help": "帮助中心",
+      "setting": "设置"
+    },
     "more": "更多"
   },
   "monitor": {