[alert,webapp] 告警中心条件查询与搜索支持,支持批量已读未读
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -13,13 +13,43 @@
|
||||
</nz-breadcrumb>
|
||||
<nz-divider></nz-divider>
|
||||
|
||||
<button nz-button nzType="primary" (click)="onDeleteAlerts()">
|
||||
<i nz-icon nzType="delete" nzTheme="outline"></i>
|
||||
删除告警
|
||||
</button>
|
||||
<button nz-button nzType="primary" (click)="sync()" nz-tooltip nzTooltipTitle="刷新">
|
||||
<i nz-icon nzType="sync" nzTheme="outline"></i>
|
||||
</button>
|
||||
<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"
|
||||
@@ -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>
|
||||
|
||||
@@ -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: 列表多选分页逻辑
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user