From e7dee4e9a0282151d3ae1d8c9ad1346f4b7dc145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Wed, 7 Jan 2026 10:03:13 +0800 Subject: [PATCH] fix(monitor): show alert count (#24028) --- pkg/apis/monitor/monitor_resource_alert.go | 1 + .../options/monitor/monitor_resource_alert.go | 2 +- pkg/monitor/models/monitor_resource_alert.go | 151 ++++++++++++------ 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/pkg/apis/monitor/monitor_resource_alert.go b/pkg/apis/monitor/monitor_resource_alert.go index 12331707ec..5b13f42d6c 100644 --- a/pkg/apis/monitor/monitor_resource_alert.go +++ b/pkg/apis/monitor/monitor_resource_alert.go @@ -64,4 +64,5 @@ type MonitorResourceJointDetails struct { SendState string `json:"send_state"` State string `json:"state"` IsSetShield bool `json:"is_set_shield"` + AlertCount int `json:"alert_count"` } diff --git a/pkg/mcclient/options/monitor/monitor_resource_alert.go b/pkg/mcclient/options/monitor/monitor_resource_alert.go index c32db577f6..a53fb3d8a0 100644 --- a/pkg/mcclient/options/monitor/monitor_resource_alert.go +++ b/pkg/mcclient/options/monitor/monitor_resource_alert.go @@ -32,7 +32,7 @@ type MonitorResourceAlertListOptions struct { Ip string `help:"IP address" json:"ip"` StartTime time.Time `help:"start time for top query, format: 2025-01-01 00:00:00" json:"start_time"` EndTime time.Time `help:"end time for top query, format: 2025-01-01 00:00:00" json:"end_time"` - Top int `help:"return top N resources by alert count (default: 5)" json:"top"` + Top *int `help:"return top N resources by alert count (default: 5)" json:"top"` } func (o *MonitorResourceAlertListOptions) GetMasterOpt() string { diff --git a/pkg/monitor/models/monitor_resource_alert.go b/pkg/monitor/models/monitor_resource_alert.go index 90a6a0ba79..3cdb9f5bcc 100644 --- a/pkg/monitor/models/monitor_resource_alert.go +++ b/pkg/monitor/models/monitor_resource_alert.go @@ -16,6 +16,7 @@ package models import ( "context" + "fmt" "strings" "time" @@ -453,6 +454,12 @@ func min(a, b int) int { return b } +type SAlertRecordCount struct { + Count int + ResIds string + AlertId string +} + func (man *SMonitorResourceAlertManager) FetchCustomizeColumns( ctx context.Context, userCred mcclient.TokenCredential, @@ -461,66 +468,104 @@ func (man *SMonitorResourceAlertManager) FetchCustomizeColumns( fields stringutils2.SSortedStrings, isList bool, ) []monitor.MonitorResourceJointDetails { + input := &monitor.MonitorResourceJointListInput{} + query.Unmarshal(input) rows := make([]monitor.MonitorResourceJointDetails, len(objs)) + alertRecordIds := make([]string, len(objs)) + alertIds := make([]string, len(objs)) + resourceIds := make([]string, len(objs)) + for i := range rows { + obj := objs[i].(*SMonitorResourceAlert) + alertRecordIds[i] = obj.AlertRecordId + alertIds[i] = obj.AlertId + resourceIds[i] = obj.MonitorResourceId + } + records := map[string]SAlertRecord{} + err := db.FetchModelObjectsByIds(AlertRecordManager, "id", alertRecordIds, &records) + if err != nil { + log.Errorf("fetch alert records error: %v", err) + return rows + } + alerts := map[string]SCommonAlert{} + err = db.FetchModelObjectsByIds(CommonAlertManager, "id", alertIds, &alerts) + if err != nil { + log.Errorf("fetch alerts error: %v", err) + return rows + } + resources := map[string]SMonitorResource{} + err = db.FetchModelObjectsByIds(MonitorResourceManager, "res_id", resourceIds, &resources) + if err != nil { + log.Errorf("fetch monitor resources error: %v", err) + return rows + } + shields := make([]SAlertRecordShield, 0) + err = AlertRecordShieldManager.Query().GE("end_time", time.Now()).In("res_id", resourceIds).In("alert_id", alertIds).All(&shields) + if err != nil { + log.Errorf("fetch alert record shields error: %v", err) + return rows + } + shieldsMap := map[string]bool{} + for _, shield := range shields { + shieldsMap[fmt.Sprintf("%s-%s", shield.ResId, shield.AlertId)] = true + } + recordCountMap := map[string][]SAlertRecordCount{} + if !input.StartTime.IsZero() && !input.EndTime.IsZero() { + sq := AlertRecordManager.Query().GE("created_at", input.StartTime).LE("created_at", input.EndTime).In("alert_id", alertIds).SubQuery() + q := sq.Query( + sqlchemy.COUNT("count", sq.Field("id")), + sq.Field("alert_id"), + sq.Field("res_ids"), + ).GroupBy(sq.Field("alert_id"), sq.Field("res_ids")) + + recordCount := []SAlertRecordCount{} + err = q.All(&recordCount) + if err != nil { + log.Errorf("fetch alert records error: %v", err) + return rows + } + for i := range recordCount { + _, ok := recordCountMap[recordCount[i].AlertId] + if !ok { + recordCountMap[recordCount[i].AlertId] = make([]SAlertRecordCount, 0) + } + recordCountMap[recordCount[i].AlertId] = append(recordCountMap[recordCount[i].AlertId], recordCount[i]) + } + } for i := range rows { rows[i] = monitor.MonitorResourceJointDetails{} - rows[i] = objs[i].(*SMonitorResourceAlert).getMoreDetails(rows[i]) + obj := objs[i].(*SMonitorResourceAlert) + rows[i].ResId = obj.MonitorResourceId + rows[i].ResType = obj.ResType + if record, ok := records[obj.AlertRecordId]; ok { + rows[i].SendState = record.SendState + rows[i].State = record.State + } + if alert, ok := alerts[obj.AlertId]; ok { + rows[i].AlertName = alert.Name + rows[i].Level = alert.Level + silentPeriod, _ := alert.GetSilentPeriod() + rule, _ := alert.GetAlertRules(silentPeriod) + rows[i].AlertRule = jsonutils.Marshal(rule) + } + if res, ok := resources[obj.MonitorResourceId]; ok { + rows[i].ResName = res.Name + rows[i].ResType = res.ResType + } + if _, ok := shieldsMap[fmt.Sprintf("%s-%s", obj.MonitorResourceId, obj.AlertId)]; ok { + rows[i].IsSetShield = true + } + if recordCount, ok := recordCountMap[obj.AlertId]; ok { + rows[i].AlertCount = 0 + for _, record := range recordCount { + if strings.Contains(record.ResIds, obj.MonitorResourceId) { + rows[i].AlertCount += record.Count + } + } + } } return rows } -func (obj *SMonitorResourceAlert) getMoreDetails(detail monitor.MonitorResourceJointDetails) monitor.MonitorResourceJointDetails { - detail.ResType = obj.ResType - detail.ResId = obj.MonitorResourceId - resources, err := MonitorResourceManager.GetMonitorResources(monitor.MonitorResourceListInput{ResId: []string{obj. - MonitorResourceId}}) - if err != nil { - log.Errorf("getMonitorResource:%s err:%v", obj.MonitorResourceId, err) - return detail - } - if len(resources) == 0 { - return detail - } - if detail.ResType == "" { - detail.ResType = resources[0].ResType - } - detail.ResName = resources[0].Name - //detail.ResId = resources[0].ResId - - if len(obj.AlertRecordId) != 0 { - record, err := AlertRecordManager.GetAlertRecord(obj.AlertRecordId) - if err != nil { - log.Errorf("get alertRecord:%s err:%v", obj.AlertRecordId, err) - return detail - } - //detail.AlertRule = record.AlertRule - detail.SendState = record.SendState - detail.State = record.State - } - alert, err := CommonAlertManager.GetAlert(obj.AlertId) - if err != nil { - log.Errorf("SMonitorResourceAlert get alert by id :%s err:%v", obj.AlertId, err) - return detail - } - detail.AlertName = alert.Name - detail.Level = alert.Level - silentPeriod, _ := alert.GetSilentPeriod() - rule, _ := alert.GetAlertRules(silentPeriod) - detail.AlertRule = jsonutils.Marshal(rule) - - now := time.Now() - shields, err := AlertRecordShieldManager.GetRecordShields(monitor.AlertRecordShieldListInput{ResId: obj.MonitorResourceId, - AlertId: obj.AlertId, EndTime: &now}) - if err != nil { - log.Errorf("SMonitorResourceAlert get GetRecordShields by resId: %s, alertId: %s, err: %v", obj.MonitorResourceId, obj.AlertId, err) - return detail - } - if len(shields) != 0 { - detail.IsSetShield = true - } - return detail -} - func (manager *SMonitorResourceAlertManager) ResourceScope() rbacscope.TRbacScope { return manager.SScopedResourceBaseManager.ResourceScope() }