[alert,webapp] 告警中心条件查询与搜索支持,支持批量已读未读

This commit is contained in:
tomsun28
2021-12-19 21:49:19 +08:00
parent fbd0f5da3b
commit 1e8c829e9b
11 changed files with 238 additions and 13 deletions

View File

@@ -21,7 +21,6 @@ import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import static com.usthe.common.util.CommonConstants.MONITOR_NOT_EXIST_CODE;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

View File

@@ -14,6 +14,8 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@@ -97,4 +99,16 @@ public class AlertsController {
return ResponseEntity.ok(message);
}
@PutMapping(path = "/status/{status}")
@ApiOperation(value = "批量修改告警状态", notes = "批量修改告警状态,设置已读未读")
public ResponseEntity<Message<Void>> applyAlertDefinesStatus(
@ApiParam(value = "告警状态值", example = "0") @PathVariable Byte status,
@ApiParam(value = "告警IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids) {
if (ids != null && status != null && !ids.isEmpty()) {
alertService.editAlertStatus(status, ids);
}
Message<Void> message = new Message<>();
return ResponseEntity.ok(message);
}
}

View File

@@ -3,7 +3,11 @@ package com.usthe.alert.dao;
import com.usthe.alert.pojo.entity.Alert;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Set;
/**
@@ -19,4 +23,13 @@ public interface AlertDao extends JpaRepository<Alert, Long>, JpaSpecificationEx
*/
void deleteAlertsByIdIn(Set<Long> alertIds);
/**
* 根据告警ID-状态值 更新告警状态
* @param status 状态值
* @param ids 告警ID列表
*/
@Modifying
@Query("update Alert set status = :status where id in :ids")
void updateAlertsStatus(@Param(value = "status") Byte status, @Param(value = "ids") List<Long> ids);
}

View File

@@ -67,7 +67,7 @@ public class Alert {
@Length(max = 1024)
private String content;
@ApiModelProperty(value = "告警状态: 0-正常告警 1-触发中:阈值触发但未达到告警次数 2-恢复告警",
@ApiModelProperty(value = "告警状态: 0-正常告警(未读) 1-阈值触发但未达到告警次数 2-恢复告警 3-已读已知",
example = "1", accessMode = READ_WRITE, position = 7)
@Min(0)
@Max(2)

View File

@@ -6,6 +6,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import java.util.HashSet;
import java.util.List;
/**
* 告警信息管理接口
@@ -34,4 +35,11 @@ public interface AlertService {
* @param ids 告警IDs
*/
void deleteAlerts(HashSet<Long> ids);
/**
* 根据告警ID-状态值 更新告警状态
* @param status 待修改为的告警状态
* @param ids 待修改的告警IDs
*/
void editAlertStatus(Byte status, List<Long> ids);
}

View File

@@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.List;
/**
* 告警信息服务实现
@@ -40,4 +41,10 @@ public class AlertServiceImpl implements AlertService {
public void deleteAlerts(HashSet<Long> ids) {
alertDao.deleteAlertsByIdIn(ids);
}
@Override
public void editAlertStatus(Byte status, List<Long> ids) {
alertDao.updateAlertsStatus(status, ids);
}
}

View File

@@ -116,7 +116,7 @@ CREATE TABLE alert
alert_define_id bigint not null comment '告警关联的告警定义ID',
priority tinyint not null default 0 comment '告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色',
content varchar(255) not null comment '告警通知实际内容',
status tinyint not null default 0 comment '告警状态: 0-正常告警 1-阈值触发但未达到告警次数 2-恢复告警',
status tinyint not null default 0 comment '告警状态: 0-正常告警(未读) 1-阈值触发但未达到告警次数 2-恢复告警 3-已读已知',
times int not null comment '触发次数,即达到告警定义的触发阈值次数要求后才会发告警',
gmt_create timestamp default current_timestamp comment 'create time',
primary key (id)

View File

@@ -3,7 +3,9 @@ export class Alert {
target!: string;
monitorId!: number;
monitorName!: string;
// 告警级别 0:高-emergency-紧急告警-红色 1:中-critical-严重告警-橙色 2:低-warning-警告告警-黄色
priority: number = 2;
// 告警状态: 0-正常告警(未读) 3-已读已知
status!: number;
content!: string;
times!: number;

View File

@@ -13,14 +13,44 @@
</nz-breadcrumb>
<nz-divider></nz-divider>
<div>
<button nz-button nzType="primary" (click)="onDeleteAlerts()">
<i nz-icon nzType="delete" nzTheme="outline"></i>
删除告警
</button>
<button nz-button nzType="primary" (click)="onMarkReadAlerts()">
<i nz-icon nzType="down-circle" nzTheme="outline"></i>
标记已读
</button>
<button nz-button nzType="primary" (click)="onMarkUnReadAlerts()">
<i nz-icon nzType="up-circle" nzTheme="outline"></i>
标记未读
</button>
<button nz-button nzType="primary" (click)="sync()" nz-tooltip nzTooltipTitle="刷新">
<i nz-icon nzType="sync" nzTheme="outline"></i>
</button>
<button style="margin-right: 25px;float: right;" nz-button nzType="primary" (click)="onFilterSearchAlerts()">
搜索
</button>
<input style="margin-right: 5px;float: right;width:150px;border-radius: 9px;text-align: center;" nz-input
type="text" placeholder="搜索告警内容" nzSize="default" [(ngModel)]="filterContent" />
<nz-select style="margin-right: 10px;float: right;width: 120px;" nzAllowClear
[nzPlaceHolder]="'告警状态过滤'" [(ngModel)]="filterStatus">
<nz-option nzLabel="全部状态" nzValue="9"></nz-option>
<nz-option nzLabel="未读告警" nzValue="0"></nz-option>
<nz-option nzLabel="已读告警" nzValue="3"></nz-option>
</nz-select>
<nz-select style="margin-right: 10px;float: right;width: 120px;" nzAllowClear
[nzPlaceHolder]="'告警级别过滤'" [(ngModel)]="filterPriority">
<nz-option nzLabel="全部级别" nzValue="9"></nz-option>
<nz-option nzLabel="警告级别" nzValue="2"></nz-option>
<nz-option nzLabel="严重级别" nzValue="1"></nz-option>
<nz-option nzLabel="紧急级别" nzValue="0"></nz-option>
</nz-select>
</div>
<nz-table #fixedTable [nzData]="alerts"
[nzPageIndex]="pageIndex" [nzPageSize]="pageSize" [nzTotal]="total"
nzFrontPagination ="false"
@@ -37,12 +67,13 @@
<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">
<tr *ngFor="let data of fixedTable.data" [ngStyle]="{'background-color':data.status === 0 ? '#E1E7A89B' : 'inherit' }">
<td nzAlign="center" nzLeft [nzChecked]="checkedAlertIds.has(data.id)" (nzCheckedChange)="onItemChecked(data.id, $event)"></td>
<td nzAlign="center">{{ data.target }}</td>
<td nzAlign="center">
@@ -65,11 +96,20 @@
</nz-tag>
</td>
<td nzAlign="center">{{ data.content }}</td>
<td nzAlign="center">
{{ data.status === 0 ? '未读' : '已读' }}
</td>
<td nzAlign="center">{{ data.gmtCreate }}</td>
<td nzAlign="center" nzRight>
<button nz-button nzType="primary" (click)="onDeleteOneAlert(data.id)" nz-tooltip nzTooltipTitle="删除告警">
<i nz-icon nzType="delete" nzTheme="outline"></i>
</button>
<button nz-button nzType="primary" (click)="onMarkReadOneAlert(data.id)" nz-tooltip nzTooltipTitle="标记已读">
<i nz-icon nzType="down-circle" nzTheme="outline"></i>
</button>
<button nz-button nzType="primary" (click)="onMarkUnReadOneAlert(data.id)" nz-tooltip nzTooltipTitle="标记未读">
<i nz-icon nzType="up-circle" nzTheme="outline"></i>
</button>
</td>
</tr>
</tbody>

View File

@@ -23,11 +23,40 @@ export class AlertCenterComponent implements OnInit {
alerts!: Alert[];
tableLoading: boolean = false;
checkedAlertIds = new Set<number>();
// 搜索过滤相关属性
filterStatus: number | undefined;
filterPriority: number | undefined;
filterContent: string | undefined;
ngOnInit(): void {
this.loadAlertsTable();
}
onFilterSearchAlerts() {
this.tableLoading = true;
let filterAlerts$ = this.alertSvc.searchAlerts(this.filterStatus, this.filterPriority,
this.filterContent, this.pageIndex - 1, this.pageSize)
.subscribe(message => {
filterAlerts$.unsubscribe();
this.tableLoading = false;
this.checkedAll = false;
this.checkedAlertIds.clear();
if (message.code === 0) {
let page = message.data;
this.alerts = page.content;
this.pageIndex = page.number + 1;
this.total = page.totalElements;
} else {
console.warn(message.msg);
}
}, error => {
this.tableLoading = false;
filterAlerts$.unsubscribe();
console.error(error.msg);
});
}
sync() {
this.loadAlertsTable();
}
@@ -51,6 +80,7 @@ export class AlertCenterComponent implements OnInit {
}, error => {
this.tableLoading = false;
alertsInit$.unsubscribe();
console.error(error.msg);
});
}
@@ -69,6 +99,35 @@ export class AlertCenterComponent implements OnInit {
});
}
onMarkReadAlerts() {
if (this.checkedAlertIds == null || this.checkedAlertIds.size === 0) {
this.notifySvc.warning("未选中任何待标记项!","");
return;
}
this.modal.confirm({
nzTitle: '请确认是否批量标记已读!',
nzOkText: '确定',
nzCancelText: '取消',
nzOkDanger: true,
nzOkType: "primary",
nzOnOk: () => this.updateAlertsStatus(this.checkedAlertIds, 3)
});
}
onMarkUnReadAlerts() {
if (this.checkedAlertIds == null || this.checkedAlertIds.size === 0) {
this.notifySvc.warning("未选中任何待标记项!","");
return;
}
this.modal.confirm({
nzTitle: '请确认是否批量标记未读!',
nzOkText: '确定',
nzCancelText: '取消',
nzOkDanger: true,
nzOkType: "primary",
nzOnOk: () => this.updateAlertsStatus(this.checkedAlertIds, 0)
});
}
onDeleteOneAlert(alertId: number) {
let alerts = new Set<number>();
alerts.add(alertId);
@@ -82,6 +141,32 @@ export class AlertCenterComponent implements OnInit {
});
}
onMarkReadOneAlert(alertId: number) {
let alerts = new Set<number>();
alerts.add(alertId);
this.modal.confirm({
nzTitle: '请确认是否标记已读!',
nzOkText: '确定',
nzCancelText: '取消',
nzOkDanger: true,
nzOkType: "primary",
nzOnOk: () => this.updateAlertsStatus(alerts, 3)
});
}
onMarkUnReadOneAlert(alertId: number) {
let alerts = new Set<number>();
alerts.add(alertId);
this.modal.confirm({
nzTitle: '请确认是否标记未读!',
nzOkText: '确定',
nzCancelText: '取消',
nzOkDanger: true,
nzOkType: "primary",
nzOnOk: () => this.updateAlertsStatus(alerts, 0)
});
}
deleteAlerts(alertIds: Set<number>) {
this.tableLoading = true;
const deleteAlerts$ = this.alertSvc.deleteAlerts(alertIds)
@@ -103,6 +188,27 @@ export class AlertCenterComponent implements OnInit {
);
}
updateAlertsStatus(alertIds: Set<number>, status: number) {
this.tableLoading = true;
const markAlertsStatus$ = this.alertSvc.applyAlertsStatus(alertIds, status)
.subscribe(message => {
markAlertsStatus$.unsubscribe();
if (message.code === 0) {
this.notifySvc.success("标记成功!", "");
this.loadAlertsTable();
} else {
this.tableLoading = false;
this.notifySvc.error("标记失败!", message.msg);
}
},
error => {
this.tableLoading = false;
markAlertsStatus$.unsubscribe();
this.notifySvc.error("标记失败!", error.msg)
}
);
}
// begin: 列表多选分页逻辑
checkedAll: boolean = false;
onAllChecked(checked: boolean) {
@@ -126,6 +232,4 @@ export class AlertCenterComponent implements OnInit {
this.loadAlertsTable();
}
// end: 列表多选分页逻辑
}

View File

@@ -7,6 +7,8 @@ import {Alert} from "../pojo/Alert";
const alerts_uri = '/alerts';
const alerts_status_uri = '/alerts/status';
@Injectable({
providedIn: 'root'
})
@@ -29,6 +31,31 @@ export class AlertService {
return this.http.get<Message<Page<Alert>>>(alerts_uri, options);
}
public searchAlerts(status: number | undefined, priority: number | undefined, content: string | undefined,
pageIndex: number, pageSize: number) : Observable<Message<Page<Alert>>> {
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
});
if (status != undefined && status != 9) {
httpParams = httpParams.append('status', status);
}
if (priority != undefined && priority != 9) {
httpParams = httpParams.append('priority', priority);
}
if (content != undefined && content != '' && content.trim() != '') {
httpParams = httpParams.append('content', content.trim());
}
const options = { params: httpParams };
return this.http.get<Message<Page<Alert>>>(alerts_uri, options);
}
public deleteAlerts(alertIds: Set<number>) : Observable<Message<any>> {
let httpParams = new HttpParams();
alertIds.forEach(alertId => {
@@ -40,4 +67,15 @@ export class AlertService {
return this.http.delete<Message<any>>(alerts_uri, options);
}
public applyAlertsStatus(alertIds: Set<number>, status: number) : Observable<Message<any>> {
let httpParams = new HttpParams();
alertIds.forEach(alertId => {
// 注意HttpParams是不可变对象 需要保存append后返回的对象为最新对象
// append方法可以叠加同一key, set方法会把key之前的值覆盖只留一个key-value
httpParams = httpParams.append('ids', alertId);
})
const options = { params: httpParams };
return this.http.put<Message<any>>(`${alerts_status_uri}/${status}`, null, options);
}
}