package models import ( "errors" "gorm.io/gorm" ) // GetAllModels 数据库迁移用模型列表 func GetAllModels() []interface{} { return []interface{}{ &LogEvent{}, &AlertOutbox{}, &ResourceMapping{}, &ResourceEventDedup{}, &TrapDictionaryEntry{}, &SyslogRule{}, &TrapRule{}, &TrapShield{}, &AuditLog{}, &DangerousOperationApproval{}, } } // InitData 初始化默认规则数据(幂等) func InitData(db *gorm.DB) error { if db == nil { return nil } if err := backfillDispatchOutboxIDs(db); err != nil { return err } if err := seedDefaultSyslogRules(db); err != nil { return err } if err := seedDefaultTrapRules(db); err != nil { return err } if err := seedDefaultTrapDictionary(db); err != nil { return err } return nil } func backfillDispatchOutboxIDs(db *gorm.DB) error { const postgresUpdate = ` WITH latest AS ( SELECT target_event.id AS log_event_id, current_outbox.id, current_outbox.status FROM logs_events AS target_event JOIN LATERAL ( SELECT id, status FROM logs_alert_outbox WHERE log_event_id = target_event.id ORDER BY id DESC LIMIT 1 ) AS current_outbox ON TRUE WHERE target_event.dispatch_outbox_id = 0 ) UPDATE logs_events AS target_event SET dispatch_outbox_id = latest.id, dispatch_status = CASE latest.status WHEN 'processing' THEN 'retrying' ELSE latest.status END, alert_sent = CASE WHEN latest.status = 'sent' THEN TRUE ELSE FALSE END FROM latest WHERE target_event.id = latest.log_event_id AND target_event.dispatch_outbox_id = 0` const mysqlUpdate = ` UPDATE logs_events AS target_event JOIN ( SELECT candidate.id AS log_event_id, MAX(outbox.id) AS id FROM logs_events AS candidate JOIN logs_alert_outbox AS outbox ON outbox.log_event_id = candidate.id WHERE candidate.dispatch_outbox_id = 0 GROUP BY candidate.id ) AS latest_id ON latest_id.log_event_id = target_event.id JOIN logs_alert_outbox AS latest ON latest.id = latest_id.id SET dispatch_outbox_id = latest.id, dispatch_status = CASE latest.status WHEN 'processing' THEN 'retrying' ELSE latest.status END, alert_sent = CASE WHEN latest.status = 'sent' THEN TRUE ELSE FALSE END WHERE target_event.dispatch_outbox_id = 0` switch db.Dialector.Name() { case "postgres": return db.Exec(postgresUpdate).Error case "mysql": return db.Exec(mysqlUpdate).Error default: return gorm.ErrUnsupportedDriver } } func seedDefaultSyslogRules(db *gorm.DB) error { rows := []SyslogRule{ { Name: "默认-系统严重错误", Enabled: true, Priority: 100, DeviceNameContains: "", KeywordRegex: "(?i)(panic|fatal|segmentation fault|kernel panic|out of memory|oom)", AlertName: "Syslog严重错误", SeverityCode: "critical", PolicyID: 0, }, { Name: "默认-链路中断告警", Enabled: true, Priority: 90, DeviceNameContains: "", KeywordRegex: "(?i)(link down|interface .* down|port .* down)", SourceMatch: "", MessageRegex: "(?i)(link down|interface .* down|port .* down|LINK_DOWN)", AlertName: "Syslog链路中断", SeverityCode: "major", SeverityMappingJSON: `{"(?i)(critical|fatal|emergency)":"critical","(?i)(error|LINK_DOWN|down)":"major","(?i)(warning|warn)":"warning"}`, ResourceUIDExtractRegex: `(?i)(?:resource_uid=|resource=)(?P[a-z0-9_-]+:[a-z0-9_.:/-]+)|Interface (?P[A-Za-z0-9/._-]+)`, PolicyID: 0, }, { Name: "H3C-Syslog-接口中断", Enabled: true, Priority: 120, SourceMatch: "h3c", MessageRegex: `(?i)(LINK_DOWN|Interface .* down|port .* down)`, AlertName: "H3C Syslog接口中断", SeverityCode: "major", SeverityMappingJSON: `{"(?i)(LINK_DOWN|down)":"major","(?i)(LINK_UP|up)":"info"}`, ResourceUIDExtractRegex: `(?i)(?:resource_uid=|resource=)(?Pnetwork:[a-z0-9_.:/-]+)|Interface (?P[A-Za-z0-9/._-]+)`, PolicyID: 0, }, } for _, row := range rows { var existing SyslogRule err := db.Where("name = ?", row.Name).First(&existing).Error if errors.Is(err, gorm.ErrRecordNotFound) { if err := db.Create(&row).Error; err != nil { return err } continue } if err != nil { return err } if err := db.Model(&existing).Omit("updated_at").Select( "name", "enabled", "priority", "device_name_contains", "source_match", "keyword_regex", "message_regex", "alert_name", "severity_code", "severity_mapping_json", "resource_uid_extract_regex", "policy_id", ).Updates(&row).Error; err != nil { return err } } return nil } func seedDefaultTrapRules(db *gorm.DB) error { rows := []TrapRule{ { Name: "默认-Trap链路中断", Enabled: true, Priority: 100, OIDPrefix: "1.3.6.1.6.3.1.1.5", VarbindMatchRegex: "(?i)(linkdown|ifdown|down)", AlertName: "SNMP Trap链路中断", SeverityCode: "major", PolicyID: 0, }, } for _, row := range rows { var existing TrapRule err := db.Where("name = ?", row.Name).First(&existing).Error if errors.Is(err, gorm.ErrRecordNotFound) { if err := db.Create(&row).Error; err != nil { return err } continue } if err != nil { return err } if err := db.Model(&existing).Omit("updated_at").Select( "name", "enabled", "priority", "oid_prefix", "varbind_match_regex", "alert_name", "severity_code", "policy_id", ).Updates(&row).Error; err != nil { return err } } return nil } func seedDefaultTrapDictionary(db *gorm.DB) error { rows := []TrapDictionaryEntry{ { Vendor: "H3C", OID: "1.3.6.1.6.3.1.1.5.3", OIDPrefix: "1.3.6.1.6.3.1.1.5.3", Name: "H3C ifDown 接口中断", Title: "ifDown 接口中断", Description: "检测到设备接口状态变为 down。", SeverityCode: "major", SeverityMappingJSON: `{"down":"major","up":"info"}`, ParseExpression: `(?i)(ifName|interface)=?(?P[A-Za-z0-9/._-]+)`, RecoveryMessage: "请检查链路、端口状态和对端设备。", Enabled: true, }, { Vendor: "H3C", OID: "1.3.6.1.6.3.1.1.5.4", OIDPrefix: "1.3.6.1.6.3.1.1.5.4", Name: "H3C ifUp 接口恢复", Title: "ifUp 接口恢复", Description: "检测到设备接口状态恢复为 up。", SeverityCode: "info", SeverityMappingJSON: `{"up":"info"}`, ParseExpression: `(?i)(ifName|interface)=?(?P[A-Za-z0-9/._-]+)`, RecoveryMessage: "接口已恢复,请确认业务连通性。", Enabled: true, }, } for _, row := range rows { var existing TrapDictionaryEntry err := db.Where("o_id_prefix = ?", row.OIDPrefix).First(&existing).Error if errors.Is(err, gorm.ErrRecordNotFound) { if err := db.Create(&row).Error; err != nil { return err } continue } if err != nil { return err } if err := db.Model(&existing).Omit("updated_at").Select( "o_id_prefix", "vendor", "o_id", "name", "title", "description", "severity_code", "severity_mapping_json", "parse_expression", "recovery_message", "enabled", ).Updates(&row).Error; err != nil { return err } } return nil }