diff --git a/go.mod b/go.mod index 3b786315a1e3d27f57c24523e63c0bf632757368..7ef71f959b44ba346ed3cd23367fefdb33a0c2ef 100644 --- a/go.mod +++ b/go.mod @@ -26,11 +26,13 @@ require ( github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.66.0 github.com/prometheus/alertmanager v0.25.0 github.com/robfig/cron v1.2.0 + github.com/robfig/cron/v3 v3.0.1 github.com/satori/go.uuid v1.2.0 github.com/spf13/cast v1.5.0 github.com/spf13/pflag v1.0.5 github.com/tealeg/xlsx v1.0.5 github.com/thoas/go-funk v0.9.3 + github.com/tidwall/gjson v1.16.0 github.com/valyala/fasthttp v1.47.0 github.com/wanghuiyt/ding v0.0.2 go.uber.org/zap v1.24.0 @@ -107,6 +109,8 @@ require ( github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect github.com/sirupsen/logrus v1.9.2 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect diff --git a/go.sum b/go.sum index a9442f9d720866056996470eb9cbdbf81cba9de7..2c414d8c8ce8158dcd8655e2b58ea4099a409749 100644 --- a/go.sum +++ b/go.sum @@ -579,6 +579,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -645,6 +647,12 @@ github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= +github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= diff --git a/src/bean/entity/alert_class.go b/src/bean/entity/alert_class.go index 0e11ad6f05b19ab985ec1ee89ec8bdf208787336..32ba580c0e7444697c9d57db30a46b0ba5dd5c87 100644 --- a/src/bean/entity/alert_class.go +++ b/src/bean/entity/alert_class.go @@ -3,15 +3,15 @@ package entity import "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" type AlertClass struct { - ClassId int `json:"class_id" xorm:"'class_id' pk autoincr"` // 主键id - ClassName string `json:"class_name" xorm:"'class_name'"` // 分类名称 - ParentId int `json:"parent_id" xorm:"'parent_id'"` // 父级id - SortOrder int `json:"sort_order" xorm:"'sort_order'"` // 排序 - SourceFrom int `json:"source_from" xorm:"source_from"` // 数据来源 1:默认 2:自定义 - CreatedBy string `json:"created_by" xorm:"'created_by'"` // 创建人 - CreatedAt jsontime.Time `json:"created_at" xorm:"'created_at'"` // 创建时间 - UpdatedBy string `json:"updated_by" xorm:"'updated_by'"` // 更新人 - UpdatedAt jsontime.Time `json:"updated_at" xorm:"'updated_at'"` // 更新时间 + ClassId int `json:"class_id" xorm:"'class_id' pk autoincr"` // 主键id + ClassName string `json:"class_name" xorm:"'class_name'"` // 分类名称 + ParentId int `json:"parent_id" xorm:"'parent_id'"` // 父级id + SortOrder int `json:"sort_order" xorm:"'sort_order'"` // 排序 + Source int `json:"source" xorm:"source"` // 数据来源 1:默认 2:自定义 + CreatedBy string `json:"created_by" xorm:"'created_by'"` // 创建人 + CreatedAt jsontime.Time `json:"created_at" xorm:"'created_at'"` // 创建时间 + UpdatedBy string `json:"updated_by" xorm:"'updated_by'"` // 更新人 + UpdatedAt jsontime.Time `json:"updated_at" xorm:"'updated_at'"` // 更新时间 } func (m *AlertClass) TableName() string { diff --git a/src/bean/entity/alert_overview.go b/src/bean/entity/alert_overview.go index 44320cd789bb216da27865d124b8792b47095fb6..0398e8316555b004eb32c08a8c8ff9233ae6d493 100644 --- a/src/bean/entity/alert_overview.go +++ b/src/bean/entity/alert_overview.go @@ -21,25 +21,9 @@ type AlertArray struct { TotalCount int `json:"total_count"` // 总预警数 } -type RiskLevelDistribution struct { - Name string `json:"name"` // 名称 - //RiskLevel int `json:"risk_level"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 - //Percentage string `json:"percentage"` // 百分比 - Value int `json:"value"` -} - -type AlertStatusDistribution struct { - Name string `json:"name"` // 名称 - //Status int `json:"status"` // 状态,1:已恢复 2:未恢复 3:已关闭 - //Percentage string `json:"percentage"` // 百分比 - Value int `json:"value"` -} - -type AlertClassDistribution struct { - Name string `json:"name"` // 名称 - //ClassId int `json:"class_id"` // 预警分类id - //Percentage string `json:"percentage"` // 百分比 - Value int `json:"value"` +type AlertDistribution struct { + Name string `json:"name"` // 名称 + Value int `json:"value"` } type AlertFrequencyDistribution struct { diff --git a/src/bean/entity/alert_webhook.go b/src/bean/entity/alert_webhook.go deleted file mode 100644 index bcc32511b7813a75c210c9845f2446ab62d5ad45..0000000000000000000000000000000000000000 --- a/src/bean/entity/alert_webhook.go +++ /dev/null @@ -1,7 +0,0 @@ -package entity - -import ( - "github.com/prometheus/alertmanager/notify/webhook" -) - -type Message webhook.Message diff --git a/src/bean/entity/metric_config.go b/src/bean/entity/metric_config.go index 08cee08ecbe0db95b25de4c9683c0497ffeb5785..c64858fc7e70ba653212e389f64fdceeb2cfc5a8 100644 --- a/src/bean/entity/metric_config.go +++ b/src/bean/entity/metric_config.go @@ -13,7 +13,7 @@ type MetricConfig struct { CheckPeriod int `json:"check_period" xorm:"'check_period'"` // 检查周期 单位:分钟 IsEnabled int `json:"is_enabled" xorm:"'is_enabled'"` // 是否开启 1:是 2:否 AlertRuleType string `json:"alert_rule_type" xorm:"'alert_rule_type'"` // 预警规则类型 关联字典表 - SourceFrom int `json:"source_from" xorm:"source_from"` // 数据来源 1:默认 2:自定义 + Source int `json:"source" xorm:"source"` // 数据来源 1:默认 2:自定义 CreatedBy string `json:"created_by" xorm:"'created_by'"` // 创建人 CreatedAt jsontime.Time `json:"created_at" xorm:"'created_at'"` // 创建时间 UpdatedBy string `json:"updated_by" xorm:"'updated_by'"` // 更新人 diff --git a/src/bean/entity/system_user.go b/src/bean/entity/system_user.go index 6b70e552226bdddbe5659a2d319331088708f61a..8fdd6e44e85851f9cb083a4b8aec57f9d5a86f32 100644 --- a/src/bean/entity/system_user.go +++ b/src/bean/entity/system_user.go @@ -25,9 +25,9 @@ type SystemUser struct { } type SystemUserInfo struct { - Id int `json:"id" xorm:"pk autoincr" ` // id + Id int `json:"id" xorm:"pk autoincr"` // id Name string `json:"name" ` // 名称 - SystemAccount string `json:"system_account" ` // 账号 + SystemAccount string `json:"system_account"` // 账号 OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 Password string `json:"password,omitempty"` // 密码 State int `json:"state" xorm:"state"` // 状态0禁用1启用 diff --git a/src/bean/entity/work_order.go b/src/bean/entity/work_order.go index a99928366d47797f959e5550c0cfea06fbc83b79..4a9ef8c7fd789ab5d9689deaad5935981c77eaaf 100644 --- a/src/bean/entity/work_order.go +++ b/src/bean/entity/work_order.go @@ -1,13 +1,15 @@ package entity -import "time" +import ( + "time" +) type WorkOrder struct { - Id int `json:"id" xorm:"pk autoincr" ` // id - OrderName string `json:"order_name" xorm:"order_name"` // 工单名称 - OrderLevel int `json:"order_level" xorm:"order_level"` // 工单等级(1紧急任务 2重要任务 3一般任务) - OrderDesc string `json:"order_desc" xorm:"order_desc"` // 工单描述 - OrderCnt int `json:"order_cnt" xorm:"order_cnt"` // 实例工单数 + Id int `json:"id" xorm:"pk autoincr" ` // id + OrderName string `json:"order_name" xorm:"order_name"` // 工单名称 + OrderLevel int `json:"order_level" xorm:"order_level"` // 工单等级(1紧急任务 2重要任务 3一般任务) + OrderDesc string `json:"order_desc" xorm:"order_desc"` // 工单描述 + //OrderCnt int `json:"order_cnt" xorm:"order_cnt"` // 实例工单数 PushObj string `json:"push_obj" xorm:"push_obj"` // 推送对象 TimingType int `json:"timing_type" xorm:"timing_type"` // 定时类型(1手动下发 2按周 3自定义时间) TimingRule string `json:"timing_rule" xorm:"timing_rule"` // 定时规则 diff --git a/src/bean/vo/request/alert.go b/src/bean/vo/request/alert.go index c8f8cc5b317a62444dd1853f73b3381c08ba36a7..0c67cb6f6fa2330755a2c5c744bd4a11455e0207 100644 --- a/src/bean/vo/request/alert.go +++ b/src/bean/vo/request/alert.go @@ -9,14 +9,15 @@ type DetailAlert struct { } type ListAlert struct { - Id int `json:"id" form:"id"` - Ids []int `json:"ids" form:"ids"` // 预警ids - AlertRulesId string `json:"alert_rules_id" form:"alert_rules_id"` // 告警规则id - RiskLevel int `json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 - Status int `json:"status" form:"status" binding:"omitempty,oneof=1 2 3"` // 状态,1:已恢复 2:未恢复 3:已关闭 - Keyword string `json:"keyword" form:"keyword"` // 预警点/分类/指标 - StartTime string `json:"start_time" form:"start_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` - EndTime string `json:"end_time" form:"end_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` + Id int `json:"id" form:"id"` + Ids []int `json:"ids" form:"ids"` // 预警ids + AlertRulesId string `json:"alert_rules_id" form:"alert_rules_id"` // 告警规则id + RiskLevel int `json:"risk_level" form:"risk_level" binding:"omitempty,oneof=1 2 3 4"` // 风险等级,1:低风险,2:一般风险,3:较大风险,4:重大风险 + Status int `json:"status" form:"status" binding:"omitempty,oneof=1 2 3"` // 状态,1:已恢复 2:未恢复 3:已关闭 + Keyword string `json:"keyword" form:"keyword"` // 预警点/分类/指标 + SystemAccount string `json:"system_account" form:"system_account"` // 预警推送用户 + StartTime string `json:"start_time" form:"start_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` + EndTime string `json:"end_time" form:"end_time" binding:"omitempty,datetime=2006-01-02 15:04:05"` Pagination } diff --git a/src/bean/vo/request/alert_class.go b/src/bean/vo/request/alert_class.go index 49046eaf1044954bafe6e75ee125fd89e507de6f..cb65403bd1d7290d13499888a3ab074a5c7a6dc5 100644 --- a/src/bean/vo/request/alert_class.go +++ b/src/bean/vo/request/alert_class.go @@ -1,14 +1,15 @@ package request type AddAlertClass struct { - ClassName string `json:"class_name" form:"class_name" binding:"required"` - ParentId int `json:"parent_id" form:"parent_id" binding:"omitempty,oneof=0 1"` - SortOrder int `json:"sort_order" form:"sort_order"` - SourceFrom int `json:"source_from" form:"source_from" binding:"omitempty,oneof=1 2"` // 数据来源 1:默认 2:自定义 + ClassName string `json:"class_name" form:"class_name" binding:"required"` + ParentId int `json:"parent_id" form:"parent_id"` + SortOrder int `json:"sort_order" form:"sort_order"` + Source int `json:"source" form:"source" binding:"omitempty,oneof=1 2"` // 数据来源 1:默认 2:自定义 } type UpdateAlertClass struct { ClassId int `json:"class_id" form:"class_id" binding:"required"` + ParentId int `json:"parent_id" form:"parent_id"` ClassName string `json:"class_name" form:"class_name" binding:"required"` } diff --git a/src/bean/vo/request/alert_rules.go b/src/bean/vo/request/alert_rules.go index 5ae87424236f0a698a1410c1a1e58310f18b1a30..50451958c121af70c0d62508bc97cd27bddc35b0 100644 --- a/src/bean/vo/request/alert_rules.go +++ b/src/bean/vo/request/alert_rules.go @@ -33,7 +33,7 @@ type UpdateAlertRules struct { ClassId int `json:"class_id" form:"class_id" binding:"required_if=DetectionType 1"` // 预警对象id(级联:预警分类/预警对象) ClassParentName string `json:"class_parent_name" form:"class_parent_name" binding:"required_if=DetectionType 2"` // 预警分类名称 ClassName string `json:"class_name" form:"class_name" binding:"required_if=DetectionType 2"` // 预警对象名称 - MetricConfigId string `json:"metric_config_id" form:"'metric_config_id'" binding:"required_if=DetectionType 1"` // 预警指标id + MetricConfigId string `json:"metric_config_id" form:"metric_config_id" binding:"required_if=DetectionType 1"` // 预警指标id MetricConfigName string `json:"metric_config_name" form:"metric_config_name" binding:"required_if=DetectionType 2"` // 预警指标名称(映射entity.MetricConfig.MetricName) Expr string `json:"expr" form:"expr" binding:"required_if=DetectionType 2"` // 指标表达式(PromQL语句) AlertCondition []entity.AlertCondition `json:"alert_condition" form:"alert_condition" binding:"required"` // 预警规则 字典值 diff --git a/src/bean/vo/request/alert_webhook.go b/src/bean/vo/request/alert_webhook.go index 644ef17e370c69a42d4fc711b7cce462ae41b24c..f01b4c2b5700830d273f41317a4ec97bc12ce90c 100644 --- a/src/bean/vo/request/alert_webhook.go +++ b/src/bean/vo/request/alert_webhook.go @@ -10,7 +10,7 @@ type AddAlertWebhook struct { ClassId int `json:"class_id" form:"class_id" binding:"required_if=DetectionType 1"` // 预警对象id(级联:预警分类/预警对象) ClassParentName string `json:"class_parent_name" form:"class_parent_name" binding:"required_if=DetectionType 2"` // 预警分类名称 ClassName string `json:"class_name" form:"class_name" binding:"required_if=DetectionType 2"` // 预警对象名称 - MetricConfigId string `json:"metric_config_id" form:"'metric_config_id'" binding:"required_if=DetectionType 1"` // 预警指标id + MetricConfigId string `json:"metric_config_id" form:"metric_config_id" binding:"required_if=DetectionType 1"` // 预警指标id MetricConfigName string `json:"metric_config_name" form:"metric_config_name" binding:"required_if=DetectionType 2"` // 预警指标名称(映射entity.MetricConfig.MetricName) Expr string `json:"expr" form:"expr" binding:"required_if=DetectionType 2"` // 指标表达式(PromQL语句) AlertCondition []entity.AlertCondition `json:"alert_condition" form:"alert_condition" binding:"required,dive"` // 预警规则 字典值 diff --git a/src/bean/vo/request/metric_config.go b/src/bean/vo/request/metric_config.go index fc06ac1ad8a47fa562d0cd5f18ad710bc9d97c07..a1978220d275198d2a54d0b167f8862f924c0bf7 100644 --- a/src/bean/vo/request/metric_config.go +++ b/src/bean/vo/request/metric_config.go @@ -12,7 +12,7 @@ type AddMetricConfig struct { CheckPeriod int `json:"check_period" form:"check_period" binding:"oneof=1 3 5 10 20 30"` // 检查周期 单位:分钟 IsEnabled int `json:"is_enabled" form:"is_enabled" binding:"omitempty,oneof=1 2"` // 是否开启 1:是 2:否 AlertRuleType string `json:"alert_rule_type" form:"alert_rule_type" binding:"required"` // 预警规则类型 关联字典表 - SourceFrom int `json:"source_from" form:"source_from" binding:"omitempty,oneof=1 2"` // 数据来源 1:默认 2:自定义 + Source int `json:"source" form:"source" binding:"omitempty,oneof=1 2"` // 数据来源(自定义为非正常数据) 1:默认 2:自定义 } type UpdateMetricConfig struct { @@ -26,7 +26,7 @@ type UpdateMetricConfig struct { CheckPeriod int `json:"check_period" form:"check_period" binding:"omitempty,oneof=1 3 5 10 20 30"` // 检查周期 单位:分钟 IsEnabled int `json:"is_enabled" form:"is_enabled" binding:"omitempty,oneof=1 2"` // 是否开启 1:是 2:否 AlertRuleType string `json:"alert_rule_type" form:"alert_rule_type"` // 预警规则类型 关联字典表 - SourceFrom int `json:"source_from" form:"source_from" binding:"omitempty,oneof=1 2"` // 数据来源 1:默认 2:自定义 + Source int `json:"source" form:"source" binding:"omitempty,oneof=1 2"` // 数据来源 1:默认 2:自定义 } type DeleteMetricConfig struct { diff --git a/src/bean/vo/request/work_order.go b/src/bean/vo/request/work_order.go index 5a2534713d6669aa856d087e23dc59b66f0f18a4..ca3a8674d3d1a28afc3a177a3a40abf79bc9afc0 100644 --- a/src/bean/vo/request/work_order.go +++ b/src/bean/vo/request/work_order.go @@ -1,7 +1,7 @@ package request type AddWorkOrderReq struct { - IsPush int `json:"is_push"` // 是否立刻下发(0否 1是) + IsPush int `json:"is_push" binding:"oneof=0 1"` // 是否立刻下发(0否 1是) OrderName string `json:"order_name" binding:"required"` // 工单名称 OrderLevel int `json:"order_level" binding:"oneof=1 2 3"` // 工单等级(1紧急任务 2重要任务 3一般任务) OrderDesc string `json:"order_desc" binding:"required"` // 工单描述 @@ -36,7 +36,7 @@ type TimingCustom struct { } type EditWorkOrderReq struct { - IsPush int `json:"is_push" binding:"required"` // 是否立刻下发(0否 1是) + IsPush int `json:"is_push" binding:"oneof=0 1"` // 是否立刻下发(0否 1是) Id int `json:"id" binding:"required"` // 主键id OrderLevel int `json:"order_level" binding:"oneof=1 2 3"` // 工单等级(1紧急任务 2重要任务 3一般任务) OrderDesc string `json:"order_desc" binding:"required"` // 工单描述 diff --git a/src/bean/vo/response/alert.go b/src/bean/vo/response/alert.go index 5c729b01921c483d2ee7b491aea6bde1b13df0ae..70a7ea32d0eee73303f8531c13ca3764bf43f41b 100644 --- a/src/bean/vo/response/alert.go +++ b/src/bean/vo/response/alert.go @@ -62,3 +62,46 @@ type Hits struct { MaxScore float64 `json:"max_score"` Hits []SubHits `json:"hits"` } + +type AggAlertOverview struct { + Group struct { + DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"` + SumOtherDocCount int `json:"sum_other_doc_count"` + Buckets []struct { + Key int `json:"key"` + DocCount int `json:"doc_count"` + Group struct { + DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"` + SumOtherDocCount int `json:"sum_other_doc_count"` + Buckets []struct { + Key string `json:"key"` + DocCount int `json:"doc_count"` + UnresolvedCount struct { + DocCount int `json:"doc_count"` + } `json:"unresolved_count"` + } `json:"buckets"` + } `json:"group"` + } `json:"buckets"` + } `json:"group"` +} + +type AlertDistributionGroup struct { + Group struct { + DocCountErrorUpperBound int `json:"doc_count_error_upper_bound"` + SumOtherDocCount int `json:"sum_other_doc_count"` + Buckets []struct { + Key int `json:"key"` + DocCount int `json:"doc_count"` + } `json:"buckets"` + } `json:"group"` +} + +type DateHistogramGroup struct { + Group struct { + Buckets []struct { + KeyAsString string `json:"key_as_string"` + Key int64 `json:"key"` + DocCount int `json:"doc_count"` + } `json:"buckets"` + } `json:"group"` +} diff --git a/src/bean/vo/response/alert_overview.go b/src/bean/vo/response/alert_overview.go index ec2615e3106d669e23e388ff514e976b4ed57db4..c6356940aa036d94e4d101bf02c02ffe0fffbd86 100644 --- a/src/bean/vo/response/alert_overview.go +++ b/src/bean/vo/response/alert_overview.go @@ -6,9 +6,9 @@ import ( type AlertOverviewItem struct { AlertOverview []entity.AlertOverview `json:"alert_overview"` - RiskLevelDistribution []entity.RiskLevelDistribution `json:"risk_level_distribution"` - AlertStatusDistribution []entity.AlertStatusDistribution `json:"alert_status_distribution"` - AlertClassDistribution []entity.AlertClassDistribution `json:"alert_class_distribution"` + RiskLevelDistribution []entity.AlertDistribution `json:"risk_level_distribution"` + AlertStatusDistribution []entity.AlertDistribution `json:"alert_status_distribution"` + AlertClassDistribution []entity.AlertDistribution `json:"alert_class_distribution"` AlertFrequencyDistribution entity.AlertFrequencyDistribution `json:"alert_frequency_distribution"` } diff --git a/src/common/conf/options.go b/src/common/conf/options.go index 78cf9056a5c2b22fced9f97a8988cd5efff053ca..3bf9e7f157edfb6ec12cea6f8dad384cd2f0c2f8 100644 --- a/src/common/conf/options.go +++ b/src/common/conf/options.go @@ -31,7 +31,7 @@ type Config struct { MinioSecretKey string MinioBucket string //TempDirPrefix string - PrometheusHost string + AccessRuleModeKey string LocationUrl string LocationKey string @@ -49,9 +49,20 @@ type Config struct { AweRestURL string KubernetesToken string + OpenSearchIndex string OpenSearchAddresses string OpenSearchUserName string OpenSearchPassword string + + MonitorApiVersion string + MonitorMatchNs string + MonitorMatchLabelsStr string + MonitorMatchLabels map[string]interface{} + + Namespace string + PrometheusHost string + PrometheusRuleLabel string + PrometheusRuleNamePrefix string } const ( diff --git a/src/controller/alert.go b/src/controller/alert.go index 418a9c196d309c7736c22daa94504bf756f8fae3..20a4d8dd902d09f2408b870006184b49f176196d 100644 --- a/src/controller/alert.go +++ b/src/controller/alert.go @@ -31,6 +31,7 @@ func ListAlert(c *gin.Context) { return } svc := service.AlertSvc{User: header.GetUser(c)} + // TODO 我的预警工单 data, err := svc.List(req) if err != nil { SendJsonResponse(c, resp.FAIL.WithError(err), nil) diff --git a/src/controller/alert_webhook.go b/src/controller/alert_webhook.go index 6b7de1c595f3b9edea2fa5e7e8e11054003e7036..2f3ae2e935698329db27c497d81c47ab50c269b7 100644 --- a/src/controller/alert_webhook.go +++ b/src/controller/alert_webhook.go @@ -3,10 +3,10 @@ package controller import ( "github.com/gin-gonic/gin" "github.com/prometheus/alertmanager/notify/webhook" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" - "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" "go.uber.org/zap" ) @@ -14,12 +14,18 @@ import ( // AlertWebhook 回调 func AlertWebhook(c *gin.Context) { var req webhook.Message + conf.Logger.Info("------>webhook.start------>") if err := c.ShouldBind(&req); err != nil { SendJsonResponse(c, resp.InvalidParam.TranslateError(err), nil) return } conf.Logger.Info("------>webhook.Message------>", zap.Any("message", req)) - svc := service.AlertWebhookSvc{User: header.GetUser(c)} + svc := service.AlertWebhookSvc{User: entity.SystemUserInfo{ + Name: "prometheus", + SystemAccount: "prometheus", + OrganizationId: "", + State: 1, + }} db, err := client.GetDbClient() if err != nil { SendJsonResponse(c, resp.DbConnectError.WithError(err), nil) diff --git a/src/controller/work_order_manage.go b/src/controller/work_order_manage.go index 0fbb35a566d603273d9b307838b95f7e66c46563..d67587e94f535b32acdfa36d7d0b842527afdea0 100644 --- a/src/controller/work_order_manage.go +++ b/src/controller/work_order_manage.go @@ -5,6 +5,7 @@ import ( "github.com/gin-gonic/gin" "github.com/spf13/cast" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" @@ -243,3 +244,36 @@ func ListWorkOrderMe(c *gin.Context) { } SendJsonPageResponse(c, resp.OK, list, total) } + +// WorkOrderListAlert 我的预警工单 +func WorkOrderListAlert(c *gin.Context) { + var req request.ListAlert + if err := c.ShouldBind(&req); err != nil { + SendJsonResponse(c, resp.InvalidParam.TranslateError(err), nil) + return + } + user := header.GetUser(c) + req.SystemAccount = user.SystemAccount + if user.SystemAccount == "" { + SendJsonResponse(c, resp.FAIL.WithError(errors.New("system_account cannot be empty")), nil) + return + } + svc := service.AlertSvc{User: user} + data, err := svc.List(req) + // 回显使用:内部字段对外覆盖处理 + // disposed_list[].is_disposed --映射--> is_disposed + for i := 0; i < len(data.List); i++ { + for j := 0; j < len(data.List[i].DisposedList); j++ { + if data.List[i].IsDisposed != constant.IsDisposedYes { + if data.List[i].DisposedList[j].DisposalUser == user.SystemAccount { + data.List[i].IsDisposed = constant.IsDisposedYes + } + } + } + } + if err != nil { + SendJsonResponse(c, resp.FAIL.WithError(err), nil) + return + } + SendJsonResponse(c, resp.OK, data) +} diff --git a/src/main.go b/src/main.go index 5b1a95b7024ff1a4a2d93257182f5bd5c37bf9d3..0462289cb4c9b87659dc8d2159a3314c79459c2b 100644 --- a/src/main.go +++ b/src/main.go @@ -45,9 +45,15 @@ func main() { // redis client go client.GetRedisClient() + // 初始化OpenSearch的索引 + err := service.CheckAndCreateIndex() + if err != nil { + conf.Logger.Error("failed to init OpenSearch index", zap.Error(err)) + } + //启动定时任务 cron.StartCron() - service.PushWorkOrderMessage() + service.CronPushWorkOrder() // server start... conf.Logger.Info("config info", zap.Any("options", conf.Options)) conf.Logger.Error("server start err", zap.Error(newServer().ListenAndServe())) @@ -64,17 +70,16 @@ func initConfig() { RedisURL: util.SetEnvStr("REDIS_URL", "localhost:7001"), RedisDB: 0, RedisTag: "bg", - LogDirPrefix: util.SetEnvStr("LOG_DIR_PREFIX", "/app/log"), // 日志目录 - LogDirName: util.SetEnvStr("LOG_NAME", "syslog"), // 日志名称 - LogSaveDays: util.SetEnvInt("LOG_SAVE_DAYS", 7), // 日志最大存储天数 - LogMode: util.SetEnvInt("LOG_MODE", 1), // 1.标准打印 2.输出文件 - ArgBool: util.SetEnvBool("ARG_BOOL", false), // 示例参数 - ArgInt: util.SetEnvInt("ARG_INT", 10), // 示例参数 - MinioServer: util.SetEnvStr("MINIO_SERVER", "https://cache.wodcloud.com"), // Minio 服务地址 - MinioAccessKey: util.SetEnvStr("MINIO_ACCESS_KEY", "beagleadmin"), // Minio Access Key - MinioSecretKey: util.SetEnvStr("MINIO_SECRET_KEY", "H76cPmwvH7vJ"), // Minio Secret - MinioBucket: util.SetEnvStr("MINIO_BUCKET", "so-operation"), // Minio Bucket - PrometheusHost: util.SetEnvStr("PROMETHEUS_HOST", "https://prometheus.wodcloud.com"), // Prometheus Host + LogDirPrefix: util.SetEnvStr("LOG_DIR_PREFIX", "/app/log"), // 日志目录 + LogDirName: util.SetEnvStr("LOG_NAME", "syslog"), // 日志名称 + LogSaveDays: util.SetEnvInt("LOG_SAVE_DAYS", 7), // 日志最大存储天数 + LogMode: util.SetEnvInt("LOG_MODE", 1), // 1.标准打印 2.输出文件 + ArgBool: util.SetEnvBool("ARG_BOOL", false), // 示例参数 + ArgInt: util.SetEnvInt("ARG_INT", 10), // 示例参数 + MinioServer: util.SetEnvStr("MINIO_SERVER", "https://cache.wodcloud.com"), // Minio 服务地址 + MinioAccessKey: util.SetEnvStr("MINIO_ACCESS_KEY", "beagleadmin"), // Minio Access Key + MinioSecretKey: util.SetEnvStr("MINIO_SECRET_KEY", "H76cPmwvH7vJ"), // Minio Secret + MinioBucket: util.SetEnvStr("MINIO_BUCKET", "so-operation"), // Minio Bucket AccessRuleModeKey: "accessRuleMode", LocationUrl: util.SetEnvStr("LOCATION_URL", "https://apis.map.qq.com/ws/location/v1/ip"), LocationKey: util.SetEnvStr("LOCATION_KEY", "QKFBZ-PGGWJ-VZQFF-FHPA7-QWT5H-YHF4T"), @@ -85,14 +90,26 @@ func initConfig() { SmsAccessKeyId: util.SetEnvStr("SMS_ACCESS_KEY", "LTAI4GBcVubRjzX7ABPcHnhB"), // 短信key SmsAccessSecret: util.SetEnvStr("SMS_ACCESS_SECRET", "dYE2dtABFOqYtK1ijcrits0yedHkw7"), // 短信secret SmsTemplateLogin: util.SetEnvStr("SMS_TEMPLATE_LOGIN", "SMS_212925130"), // 短信验证码模板 - SmsTemplateAlert: util.SetEnvStr("Sms_Template_Alert", "SMS_461975765"), // 预警短信模板 // 短信工单下发模板 + SmsTemplateAlert: util.SetEnvStr("SMS_TEMPLATE_ALERT", "SMS_461975765"), // 预警短信模板 // 短信工单下发模板 SmsWorkOrderTemplate: util.SetEnvStr("SMS_TEMPLATE_LOGIN", "SMS_462020767"), // 短信工单下发模板 SmsSignName: util.SetEnvStr("SMS_SIGN_NAME", "比格数据"), // 签名 AweRestURL: util.SetEnvStr("AWE_REST_URL", "http://awecloud-rest.beagle-system/awecloud/rest"), // awecloud-rest KubernetesToken: util.SetEnvStr("AWE_REST_K8S_TOKEN", "eyJhbGciOiJSUzI1NiIsImtpZCI6InJ1alJzNEVGamN5UC0wRU1rS1BKQ0JZVUtNNWpzR0t2bmlrSlJhY2Q3R00ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJiZWFnbGUtc3lzdGVtIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InJvb3QiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoicm9vdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjRlMDM0OTI3LTc0ZTMtNDQ5Yy1hN2RlLWExMGE3MjU1NGYyMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpiZWFnbGUtc3lzdGVtOnJvb3QifQ.YPLE_E2kIeo-YFQtKScBt5p4KhnniJF9n3iWN2i9UMYS06lIsq2-2wBrgON-YsJihWJupYyDQRiZ9h8bUWTrQzhnpsnuJ_aUclKyAw3QOT9rjvZhJp7qP--27dmdspSHncKtvIiprWE7UTUKzvF33WsMB0fSYFqYXOggNFMoT-fXmWwUXjgar3op0iOl3c3deJ_GeBzFyLSHEuGM7OVdjU8032aUmTen0Kji_P1yB4-O3Iqd0OdVs33BQy_tycjbxhQ8TDEpqrqhLnXjAwJCprLDEpFMx7ODZbjB9Wmuns8yJhaRDxTO47rTME7ZIAxjZ-zLR_QybtW97rlwnUTaNw"), - OpenSearchAddresses: util.SetEnvStr("Open_Search_Addresses", "https://so-opensearch.wodcloud.com"), // 短信验证码模板 - OpenSearchUserName: util.SetEnvStr("Open_Search_User_Name", ""), // 短信验证码模板 - OpenSearchPassword: util.SetEnvStr("Open_Search_Password", ""), // 短信验证码模板 + + OpenSearchIndex: util.SetEnvStr("OPEN_SEARCH_INDEX", "so_alert"), + OpenSearchAddresses: util.SetEnvStr("OPEN_SEARCH_ADDRESSES", "https://so-opensearch.wodcloud.com"), // OpenSearch连接地址 + OpenSearchUserName: util.SetEnvStr("OPEN_SEARCH_USER_NAME", ""), // OpenSearch用户名 + OpenSearchPassword: util.SetEnvStr("OPEN_SEARCH_PASSWORD", ""), // OpenSearch密码 + + Namespace: util.SetEnvStr("NAMESPACE", "smart-manage"), //采集器部署命名空间 + PrometheusHost: util.SetEnvStr("PROMETHEUS_HOST", "https://prometheus.wodcloud.com"), // Prometheus Host + PrometheusRuleNamePrefix: util.SetEnvStr("PROMETHEUS_RULE_NAME_PREFIX", "beagle-prometheus-so-operation-api-rules"), // prometheusrules资源名前缀 + PrometheusRuleLabel: util.SetEnvStr("PROMETHEUS_RULE_LABEL", `{"source":"so-operation-api","severity":"warning"}`), // prometheusrules标签,用于区分项目来源 + + MonitorApiVersion: util.SetEnvStr("MONITOR_API_VERSION", "monitoring.beagle.io/v1"), //Prometheus Operator 资源版本 + MonitorMatchNs: util.SetEnvStr("MONITOR_MATCH_NS", "beagle-monitoring"), //Monitor匹配 命名空间 + MonitorMatchLabelsStr: util.SetEnvStr("MONITOR_MATCH_LABELS", `{"prometheus-operator":"monitoring"}`), //Monitor匹配 标签JSON + } } @@ -145,12 +162,6 @@ func initAnsibleHosts() { if err != nil { fmt.Println(err.Error()) } - //else { - // _, err := f.Write([]byte("[web]\n")) - // if err != nil { - // fmt.Println(err.Error()) - // } - //} } func initTempDirPrefix() { @@ -175,14 +186,4 @@ func initAnsibleSSH() { fmt.Println(err.Error()) } } - //f2, err := os.CreateIndex("/root/.ssh/id_rsa.pub") - //defer f2.Close() - //if err != nil { - // fmt.Println(err.Error()) - //} else { - // _, err := f.Write([]byte(conf.Options.PublicKeySSH)) - // if err != nil { - // fmt.Println(err.Error()) - // } - //} } diff --git a/src/pkg/beagle/constant/constant.go b/src/pkg/beagle/constant/constant.go index 65d58f14d412aff2c02502fd3cec902e8b8b3b34..01b181b04f274da7d358bb24f00e6da0ec88c012 100644 --- a/src/pkg/beagle/constant/constant.go +++ b/src/pkg/beagle/constant/constant.go @@ -123,6 +123,12 @@ var OpTypeIntMap = map[OpType]int{ Export: 14, } +// 数据来源(自定义为非正常数据) 1:默认 2:自定义 +const ( + SourceDefault = 1 + SourceCustom = 2 +) + // RiskLevel 风险等级 const ( RiskLevelLow = iota + 1 // 1:低风险 @@ -142,7 +148,7 @@ func RiskLeveText(code int) string { case RiskLevelCritical: return "重大风险" default: - return "" + return "未知" } } @@ -152,3 +158,40 @@ const ( AlertNotRecovered AlertClosed ) + +func AlertStatusText(code int) string { + switch code { + case AlertRecovered: + return "已恢复" + case AlertNotRecovered: + return "未恢复" + case AlertClosed: + return "已关闭" + default: + return "未知" + } +} + +// 是否处置(工单管理),1:已处置,2:未处置 +const ( + IsDisposedYes = 1 + IsDisposedNo = 2 +) + +func DisposedStatusText(code int) string { + switch code { + case IsDisposedYes: + return "已处置" + case IsDisposedNo: + return "未处置" + default: + return "未知" + } +} + +// 工单定时类型 +const ( + TimingClick = 1 //手动下发 + TimingWeekly = 2 //按周 + TimingCustom = 3 //自定义时间 +) diff --git a/src/router/alertrouter.go.go b/src/router/alertrouter.go.go index 72195be3f884b99af372bf50cdbb5f59cbf1ff1b..a3900422f659e094a7c9e99a558b9e9797aaddcd 100644 --- a/src/router/alertrouter.go.go +++ b/src/router/alertrouter.go.go @@ -5,11 +5,12 @@ import ( "github.com/gin-gonic/gin" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/controller" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" ) // InitAlertListRouter 初始化预警列表配置路由 func InitAlertListRouter(e *gin.Engine) { - group := e.Group(fmt.Sprintf("%s/alert", conf.Options.Prefix)) + group := e.Group(fmt.Sprintf("%s/alert", conf.Options.Prefix), header.SetContext) { group.GET("", controller.DetailAlert) group.GET("list", controller.ListAlert) diff --git a/src/router/alertrulesrouter.go.go b/src/router/alertrulesrouter.go.go index 2a262a8b541e3a5dd2f982c10c8072b5e3999156..89b82b10cdb2c90ae16427fb31c70ed7b528b2e3 100644 --- a/src/router/alertrulesrouter.go.go +++ b/src/router/alertrulesrouter.go.go @@ -5,11 +5,12 @@ import ( "github.com/gin-gonic/gin" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/controller" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" ) // InitAlertRulesRouter 初始化预警规则配置路由 func InitAlertRulesRouter(e *gin.Engine) { - group := e.Group(fmt.Sprintf("%s/alert_rules", conf.Options.Prefix)) + group := e.Group(fmt.Sprintf("%s/alert_rules", conf.Options.Prefix), header.SetContext) { group.POST("", controller.AddAlertRules) group.PUT("", controller.UpdateAlertRules) diff --git a/src/router/metricconfigrouter.go b/src/router/metricconfigrouter.go index 133ca691a24d29d4cb3075175bbe4f1c7e70a178..4367b10f65ccc0928fde1cbe6994ed7a4a52a36c 100644 --- a/src/router/metricconfigrouter.go +++ b/src/router/metricconfigrouter.go @@ -5,11 +5,12 @@ import ( "github.com/gin-gonic/gin" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/controller" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" ) // InitMetricConfigRouter 初始化指标配置路由 func InitMetricConfigRouter(e *gin.Engine) { - mcGroup := e.Group(fmt.Sprintf("%s/metric_config", conf.Options.Prefix)) + mcGroup := e.Group(fmt.Sprintf("%s/metric_config", conf.Options.Prefix), header.SetContext) { mcGroup.POST("", controller.AddMetricConfig) mcGroup.PUT("", controller.UpdateMetricConfig) @@ -18,7 +19,7 @@ func InitMetricConfigRouter(e *gin.Engine) { mcGroup.GET("list", controller.ListMetricConfig) } - acGroup := e.Group(fmt.Sprintf("%s/alert_class", conf.Options.Prefix)) + acGroup := e.Group(fmt.Sprintf("%s/alert_class", conf.Options.Prefix), header.SetContext) { acGroup.POST("", controller.AddAlertClass) acGroup.PUT("move/:direction", controller.MoveAlertClass) diff --git a/src/router/workorderrouter.go b/src/router/workorderrouter.go index ee47a2f3c38a712a25403eaff83146db71b935b9..d5ef968a9913616672caf043fb1cce0f2cb8cb61 100644 --- a/src/router/workorderrouter.go +++ b/src/router/workorderrouter.go @@ -10,13 +10,13 @@ import ( // InitWorkOrderRouter 初始化工单路由 func InitWorkOrderRouter(e *gin.Engine) { - so := e.Group(fmt.Sprintf("%s/work_order", conf.Options.Prefix)) + so := e.Group(fmt.Sprintf("%s/work_order", conf.Options.Prefix), header.SetContext) //预警工单管理 alert := so.Group("/alert") { - alert.GET("", controller.DetailAlert) // 详情 - alert.GET("/list", controller.ListAlert) // 列表 - alert.PUT("/dispose", controller.DisposeAlert) // 处置反馈 + alert.GET("", controller.DetailAlert) // 详情 + alert.GET("/list", controller.WorkOrderListAlert) // 列表 + alert.PUT("/dispose", controller.DisposeAlert) // 处置反馈 } //业务工单管理 @@ -46,5 +46,4 @@ func InitWorkOrderRouter(e *gin.Engine) { me.PUT("/feedback", controller.FeedbackWorkOrderMe) // 处置反馈 me.GET("/list", controller.ListWorkOrderMe) // 我的业务工单列表 } - //so.POST("/note_sg", controller.WorkOrderPushNoteMsg) } diff --git a/src/service/alert.go b/src/service/alert.go index 243997dda5cea9772c5e77d7a318d4a5feaef607..dd17e81146d9ab11c38d8032b682ac508759601a 100644 --- a/src/service/alert.go +++ b/src/service/alert.go @@ -32,8 +32,7 @@ type AlertSvc struct { } var ( - OpenSearchIndex = "so_alert" - Mapping = strings.NewReader(`{ + Mapping = strings.NewReader(`{ "settings": { "number_of_shards": 1, "number_of_replicas": 0, @@ -283,13 +282,50 @@ var ( }`) ) -func (a *AlertSvc) CreateIndex() error { +func CheckAndCreateIndex() (err error) { + exist, err := checkIndexExists(conf.Options.OpenSearchIndex) + if err != nil { + return + } + + if exist { + return nil + } + + err = CreateIndex(conf.Options.OpenSearchIndex) + if err != nil { + return err + } + return nil +} + +func checkIndexExists(indexName string) (exist bool, err error) { + cli, err := client.GetOpenSearch() + if err != nil { + return + } + req := opensearchapi.IndicesExistsRequest{Index: []string{indexName}} + res, err := req.Do(context.Background(), cli) + if err != nil { + return false, err + } + defer res.Body.Close() + if res.StatusCode == http.StatusOK { + return true, nil + } else if res.StatusCode == http.StatusNotFound { + return false, nil + } else { + return false, fmt.Errorf("请求失败,状态码:%d", res.StatusCode) + } +} + +func CreateIndex(indexName string) error { cli, err := client.GetOpenSearch() if err != nil { return err } res := opensearchapi.IndicesCreateRequest{ - Index: OpenSearchIndex, + Index: indexName, Body: Mapping, } do, err := res.Do(context.Background(), cli) @@ -309,7 +345,7 @@ func (a *AlertSvc) DeleteIndex() error { return err } res := opensearchapi.IndicesDeleteRequest{ - Index: []string{OpenSearchIndex}, + Index: []string{conf.Options.OpenSearchIndex}, } do, err := res.Do(context.Background(), cli) if err != nil { @@ -390,8 +426,10 @@ func (a *AlertSvc) DocSearch(req request.ListAlert) (resp response.AlertList, er // 请输入预警点/分类/指标 if req.Keyword != "" { subBoolQuery := elastic.NewBoolQuery() - subBoolQuery.Should(elastic.NewMultiMatchQuery(req.Keyword, "alert_point", "class_parent_name", "class_name", "metric_config_name")) - //subBoolQuery.Should(elastic.NewMatchQuery("class_name", req.Keyword)) + subBoolQuery.Should(elastic.NewWildcardQuery("alert_point.keyword", fmt.Sprintf("*%s*", req.Keyword))) + subBoolQuery.Should(elastic.NewWildcardQuery("class_parent_name.keyword", fmt.Sprintf("*%s*", req.Keyword))) + subBoolQuery.Should(elastic.NewWildcardQuery("class_name.keyword", fmt.Sprintf("*%s*", req.Keyword))) + subBoolQuery.Should(elastic.NewWildcardQuery("metric_config_name.keyword", fmt.Sprintf("*%s*", req.Keyword))) boolQuery.Must(subBoolQuery) } @@ -399,6 +437,11 @@ func (a *AlertSvc) DocSearch(req request.ListAlert) (resp response.AlertList, er boolQuery.Filter(elastic.NewRangeQuery("created_at").Gte(req.StartTime).Lte(req.EndTime)) } + if req.SystemAccount != "" { // 匹配我的预警工单 + boolQuery.Must(elastic.NewNestedQuery("push_records", elastic.NewTermQuery("push_records.system_account", req.SystemAccount))) + + } + querySource, _ := boolQuery.Source() b, _ := json.Marshal(querySource) log.Printf("es statements: %s\n", string(b)) @@ -425,7 +468,7 @@ func (a *AlertSvc) DocSearch(req request.ListAlert) (resp response.AlertList, er "size": %d}`, string(b), req.GetPageSize()*(req.GetPage()-1), req.GetPageSize())) res := opensearchapi.SearchRequest{ - Index: []string{OpenSearchIndex}, + Index: []string{conf.Options.OpenSearchIndex}, Body: content, Sort: []string{"id"}, } @@ -451,7 +494,39 @@ func (a *AlertSvc) DocSearch(req request.ListAlert) (resp response.AlertList, er for _, hit := range sources.Hits.Hits { resp.List = append(resp.List, hit.Source) } - resp.TotalCount = int64(len(resp.List)) + + // 推送人数:推送记录中去重的人数 + // 推送次数:发起推送的总次数(钉钉、短信、工单算单次) + for i := 0; i < len(resp.List); i++ { + var userSet []string + var pushCountSet []string + + mergedData := make(map[string]entity.PushRecord) + for _, record := range resp.List[i].PushRecords { + userSet = append(userSet, record.SystemAccount) + pushCountSet = append(pushCountSet, record.PushTime.String()) + key := record.AlertRulesId + record.PushTime.String() + // 映射中已存在相同键的数据,则合并user_id + if existingRecord, found := mergedData[key]; found { + existingRecord.SystemAccount += ", " + record.SystemAccount + existingRecord.UserName += ", " + record.UserName + mergedData[key] = existingRecord + } else { + mergedData[key] = record + } + } + + var mergedRecords []entity.PushRecord + for _, v := range mergedData { + mergedRecords = append(mergedRecords, v) + } + + resp.List[i].PushRecords = mergedRecords + resp.List[i].NotificationCount = len(funk.UniqString(userSet)) + resp.List[i].PushCount = len(funk.UniqString(pushCountSet)) + } + + resp.TotalCount = int64(sources.Hits.Total.Value) return } @@ -482,7 +557,7 @@ func (a *AlertSvc) IndexDocExist(req request.ExistAlert) (exist bool, err error) "size": %d}`, string(b), 0, 1)) res := opensearchapi.SearchRequest{ - Index: []string{OpenSearchIndex}, + Index: []string{conf.Options.OpenSearchIndex}, Body: content, Sort: []string{"id"}, } @@ -525,7 +600,7 @@ func (a *AlertSvc) CatCount(indexName ...string) (count int) { if len(indexName) > 0 && indexName[0] != "" { index = indexName[0] + "*" } else { - index = OpenSearchIndex + "*" + index = conf.Options.OpenSearchIndex + "*" } res := opensearchapi.CatCountRequest{ @@ -555,6 +630,66 @@ func (a *AlertSvc) CatCount(indexName ...string) (count int) { return } +func (a *AlertSvc) GetIndexMaxID(indexName ...string) (maxId int, err error) { + var ( + index string + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return 0, err + } + + if len(indexName) > 0 && indexName[0] != "" { + index = indexName[0] + } else { + index = conf.Options.OpenSearchIndex + } + + // 构建 aggregation 查询 + aggregationQuery := ` + { + "size": 0, + "aggs": { + "max_id": { + "max": { + "field": "id" + } + } + } + } + ` + + res := opensearchapi.SearchRequest{ + Index: []string{index}, + Body: strings.NewReader(aggregationQuery), + } + + do, err := res.Do(context.Background(), cli) + if err != nil { + return 0, err + } + defer do.Body.Close() + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { + return 0, errors.New(do.String()) + } + + // 解析聚合结果 + var responseMap map[string]interface{} + err = json.NewDecoder(do.Body).Decode(&responseMap) + if err != nil { + return 0, err + } + + // 提取最大值 + aggregations := responseMap["aggregations"].(map[string]interface{}) + maxIDAgg := aggregations["max_id"].(map[string]interface{}) + maxIDValue := maxIDAgg["value"] + maxId = cast.ToInt(maxIDValue) + + return maxId, nil +} + func (a *AlertSvc) DocCreate(req request.CreateAlert) (err error) { var ( sources response.OpenSearchSource @@ -574,7 +709,7 @@ func (a *AlertSvc) DocCreate(req request.CreateAlert) (err error) { content := strings.NewReader(fmt.Sprintf(`%s`, docStr)) res := opensearchapi.CreateRequest{ - Index: OpenSearchIndex, + Index: conf.Options.OpenSearchIndex, DocumentID: cast.ToString(req.Id), Body: content, } @@ -654,7 +789,7 @@ func (a *AlertSvc) DocUpdate(req request.UpdateAlert) (err error) { }`, docStr)) res := opensearchapi.UpdateRequest{ - Index: OpenSearchIndex, + Index: conf.Options.OpenSearchIndex, DocumentID: cast.ToString(req.Id), Body: content, Source: []string{"true"}, @@ -688,7 +823,7 @@ func (a *AlertSvc) Create() error { return err } res := opensearchapi.IndicesCreateRequest{ - Index: OpenSearchIndex, + Index: conf.Options.OpenSearchIndex, Body: Mapping, } do, err := res.Do(context.Background(), cli) @@ -715,17 +850,9 @@ func (a *AlertSvc) BatchPushAlert(req request.BatchPushAlert) (err error) { } for _, alert := range alertList.List { - pushRecords := alert.PushRecords - // TODO 批量推送短信 - //alertRulesSvc := AlertRulesSvc{User: a.User} - /*var alertRulesItem response.AlertRulesItem - alertRulesItem, err = alertRulesSvc.GetDataById(request.DetailAlertRules{Id: alert.AlertRulesId}) - if err != nil { - return - }*/ - + pushRecords := alert.PushRecords // 原始推送记录 var phones []string - for _, v := range req.NotifyRecipients { + for _, v := range req.NotifyRecipients { // +临时推送记录 phones = append(phones, v.Phone) pushRecords = append(pushRecords, entity.PushRecord{ AlertId: alert.Id, @@ -745,7 +872,7 @@ func (a *AlertSvc) BatchPushAlert(req request.BatchPushAlert) (err error) { }) } - for _, method := range req.NotifyMethod { + for _, method := range req.NotifyMethod { // 按照通知方式循环推送 switch method { // dingtalk sms case "sms": // 发送短信 @@ -781,7 +908,7 @@ func (a *AlertSvc) BatchPushAlert(req request.BatchPushAlert) (err error) { } } - for i := 0; i < len(pushRecords); i++ { + for i := 0; i < len(pushRecords); i++ { // id重新序列化 pushRecords[i].Id = i + 1 } err = a.DocUpdate(request.UpdateAlert{ @@ -793,9 +920,6 @@ func (a *AlertSvc) BatchPushAlert(req request.BatchPushAlert) (err error) { return } } - - // TODO 批量推送用户告警 - conf.Logger.Info("batch push", zap.Any("payload", req)) time.Sleep(time.Second) return nil } @@ -844,12 +968,10 @@ func (a *AlertSvc) GetDataByAlertRulesIdAndRiskLevel(alertRulesId string, riskLe func (a *AlertSvc) List(req request.ListAlert) (resp response.AlertList, err error) { resp, err = a.DocSearch(req) - resp.TotalCount = int64(len(resp.List)) return } func (a *AlertSvc) DisposeAlert(req request.DisposeAlert) (err error) { - // TODO 我的预警工单处置 var ( sources response.OpenSearchSource now = jsontime.Now() @@ -897,7 +1019,7 @@ func (a *AlertSvc) DisposeAlert(req request.DisposeAlert) (err error) { }`, docStr)) res := opensearchapi.UpdateRequest{ - Index: OpenSearchIndex, + Index: conf.Options.OpenSearchIndex, DocumentID: cast.ToString(req.Id), Body: content, Source: []string{"true"}, diff --git a/src/service/alert_class.go b/src/service/alert_class.go index a76f72784753e69a3b057f5cfd9bff1894ff8774..b1b6b180bf95584436f891d88b1af6b1c640900b 100644 --- a/src/service/alert_class.go +++ b/src/service/alert_class.go @@ -8,6 +8,7 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" "sort" @@ -26,6 +27,9 @@ func (a *AlertClassSvc) Add(session *xorm.Session, req request.AddAlertClass) (c UpdatedBy: a.User.SystemAccount, UpdatedAt: now, } + if req.Source != constant.SourceCustom { + req.Source = constant.SourceDefault + } _ = copier.Copy(&data, &req) var max int @@ -48,7 +52,7 @@ func (a *AlertClassSvc) Update(session *xorm.Session, req request.UpdateAlertCla UpdatedAt: now, } _ = copier.Copy(&data, &req) - _, err := session.Cols("class_name", "updated_by", "updated_at").ID(data.ClassId).Update(&data) + _, err := session.Cols("parent_id", "class_name", "updated_by", "updated_at").ID(data.ClassId).Update(&data) if err != nil { return err } @@ -66,7 +70,7 @@ func (a *AlertClassSvc) Move(req request.MoveAlertClass) (err error) { _ = copier.Copy(&data, &req) var list []entity.AlertClass _, err = db.NewSession().Where("class_id = ?", req.ClassId).Get(&data) - err = db.NewSession().Where("parent_id = ?", data.ParentId).Where("source_from = 1").OrderBy("sort_order asc").Find(&list) + err = db.NewSession().Where("parent_id = ?", data.ParentId).Where("source = 1").OrderBy("sort_order asc").Find(&list) var previousIndex int var nextIndex int for i := 0; i < len(list); i++ { @@ -139,18 +143,33 @@ func (a *AlertClassSvc) List(req request.ListAlertClass) (resp response.AlertCla } session := db.NewSession() defer session.Close() - session.Where("source_from = 1") + session.Where("source = 1") if req.ClassId != 0 { session.Where("class_id = ?", req.ClassId) } if req.ClassName != "" { session.Where("class_name LIKE ?", "%"+req.ClassName+"%") } + if req.Page == -1 { + req.PageSize = 100000 + } resp.TotalCount, err = session.Limit(req.GetPageSize(), (req.GetPage()-1)*req.GetPageSize()). OrderBy("sort_order").FindAndCount(&resp.List) return } +func (a *AlertClassSvc) AlertObjectList() (resp response.AlertClassList, err error) { + db, err := client.GetDbClient() + if err != nil { + return + } + session := db.NewSession() + defer session.Close() + session.Where("parent_id <> 0") + resp.TotalCount, err = session.OrderBy("sort_order").FindAndCount(&resp.List) + return +} + func (a *AlertClassSvc) Tree(req request.ListAlertClass) (resp []*response.AlertClassNode, err error) { db, err := client.GetDbClient() if err != nil { @@ -159,7 +178,7 @@ func (a *AlertClassSvc) Tree(req request.ListAlertClass) (resp []*response.Alert session := db.NewSession() defer session.Close() var list []entity.AlertClass - session.Where("source_from = 1") + session.Where("source = 1") _, err = session.OrderBy("sort_order").FindAndCount(&list) // TODO 对req进行过滤 resp, err = AlertClassTree(list) @@ -211,15 +230,68 @@ func (a *AlertClassSvc) SortOrderMax(parentId int) (max int, err error) { } _, err = db.NewSession().Table(new(entity.AlertClass)). Select("max(sort_order)"). - Where("parent_id = ?", parentId).Where("source_from = 1").Get(&max) + Where("parent_id = ?", parentId).Where("source = 1").Get(&max) return } func (a *AlertClassSvc) Delete(ids []int) (err error) { db, err := client.GetDbClient() if err != nil { - return + return err } - _, err = db.NewSession().In("class_id", ids).Delete(&entity.AlertClass{}) - return + + var classes []entity.AlertClass + err = db.In("class_id", ids).Find(&classes) + + // 检查是否所有指定的 ids 都存在于数据库中 + if len(ids) > len(classes) { + return errors.New("部分数据不存在") + } + + var notExist []int + idSet := make(map[int]bool) + for _, v := range classes { + idSet[v.ClassId] = true + } + for _, id := range ids { + if !idSet[id] { + notExist = append(notExist, id) + } + } + + if len(notExist) > 0 { + return errors.New(fmt.Sprintf("指标分类或对象id为%v的数据未查询到", notExist)) + } + + for _, v := range classes { + if v.ParentId == 0 { + // 如果是父级数据,判断是否存在子集 + var num int + has, err := db.Table(new(entity.AlertClass)).Select("count(*)").Where("parent_id = ?", v.ClassId).Get(&num) + if err != nil { + return err + } + if has && num > 0 { + return errors.New("当前分类存在指标对象子集数据,不允许删除") + } + } else { + // 如果为子集数据,判断是否存在指标配置关联 + var configCount int + has, err := db.Table(new(entity.MetricConfig)).Select("count(*)").Where("class_id = ?", v.ClassId).Get(&configCount) + if err != nil { + return err + } + if has && configCount > 0 { + return errors.New("指标对象存在指标配置关联,不允许删除") + } + } + + // 删除数据 + _, err = db.ID(v.ClassId).Delete(v) + if err != nil { + return err + } + } + + return nil } diff --git a/src/service/alert_overview.go b/src/service/alert_overview.go index 4a70ec18c7c581df1508c48a920e8b90603fc6ac..f8e1af872737f12f06bc861aa5f49e01c07a9d17 100644 --- a/src/service/alert_overview.go +++ b/src/service/alert_overview.go @@ -1,12 +1,26 @@ package service import ( + "context" + "errors" + "fmt" "github.com/jinzhu/copier" + json "github.com/json-iterator/go" + "github.com/opensearch-project/opensearch-go" + "github.com/opensearch-project/opensearch-go/opensearchapi" + "github.com/spf13/cast" + "github.com/tidwall/gjson" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" + "io" + "net/http" + "strings" + "time" "xorm.io/xorm" ) @@ -37,207 +51,385 @@ func (a *AlertOverviewSvc) Update(session *xorm.Session, req request.UpdateAlert } func (a *AlertOverviewSvc) Overview(req request.DetailAlertOverview) (resp response.AlertOverviewItem, err error) { - now := jsontime.Now() + alertOverviewList, _ := a.AlertOverview(req) + metricConfigSvc := MetricConfigSvc{User: a.User} + nameList, _ := metricConfigSvc.NameList() + for i := 0; i < len(alertOverviewList); i++ { + for j := 0; j < len(alertOverviewList[i].List); j++ { + for _, v := range nameList.List { + if v.Id == alertOverviewList[i].List[j].MetricName { + alertOverviewList[i].List[j].MetricName = v.MetricName + break + } + } + } + } + + riskLevelDistributions, _ := a.RiskLevelDistribution(req) + alertStatusDistributions, _ := a.AlertStatusDistribution(req) + alertClassDistributions, _ := a.AlertClassDistribution(req) + alertFrequencyDistribution, _ := a.AlertFrequencyDistribution(req) resp = response.AlertOverviewItem{ - AlertOverview: []entity.AlertOverview{ - { - RiskLevel: 4, - UnresolvedCount: 10, - TotalCount: 24, - List: []entity.AlertArray{ - { - MetricName: "CPU使用率过高", - UnresolvedCount: 4, - TotalCount: 8, - }, - { - MetricName: "内存不足", - UnresolvedCount: 1, - TotalCount: 2, - }, - { - MetricName: "磁盘空间不足", - UnresolvedCount: 3, - TotalCount: 4, - }, - { - MetricName: "服务中断", - UnresolvedCount: 1, - TotalCount: 4, - }, - { - MetricName: "响应时间超时", - UnresolvedCount: 2, - TotalCount: 6, - }, - }, - CreatedBy: "admin", - CreatedAt: now, - UpdatedBy: "admin", - UpdatedAt: now, - }, - { - RiskLevel: 3, - UnresolvedCount: 8, - TotalCount: 26, - List: []entity.AlertArray{ - { - MetricName: "CPU使用率过高", - UnresolvedCount: 4, - TotalCount: 12, - }, - { - MetricName: "内存不足", - UnresolvedCount: 1, - TotalCount: 10, - }, - { - MetricName: "磁盘空间不足", - UnresolvedCount: 3, - TotalCount: 8, - }, - { - MetricName: "服务中断", - UnresolvedCount: 1, - TotalCount: 4, - }, - { - MetricName: "响应时间超时", - UnresolvedCount: 2, - TotalCount: 6, - }, - }, - CreatedBy: "admin", - CreatedAt: now, - UpdatedBy: "admin", - UpdatedAt: now, - }, - { - RiskLevel: 2, - UnresolvedCount: 13, - TotalCount: 50, - List: []entity.AlertArray{ - { - MetricName: "CPU使用率过高", - UnresolvedCount: 4, - TotalCount: 12, - }, - { - MetricName: "内存不足", - UnresolvedCount: 1, - TotalCount: 10, - }, - { - MetricName: "磁盘空间不足", - UnresolvedCount: 3, - TotalCount: 8, - }, - { - MetricName: "服务中断", - UnresolvedCount: 1, - TotalCount: 4, - }, - { - MetricName: "响应时间超时", - UnresolvedCount: 2, - TotalCount: 6, - }, - }, - CreatedBy: "admin", - CreatedAt: now, - UpdatedBy: "admin", - UpdatedAt: now, - }, - { - RiskLevel: 1, - UnresolvedCount: 8, - TotalCount: 20, - List: []entity.AlertArray{ - { - MetricName: "CPU使用率过高", - UnresolvedCount: 4, - TotalCount: 12, - }, - { - MetricName: "内存不足", - UnresolvedCount: 1, - TotalCount: 10, - }, - { - MetricName: "磁盘空间不足", - UnresolvedCount: 3, - TotalCount: 8, - }, - { - MetricName: "服务中断", - UnresolvedCount: 1, - TotalCount: 4, - }, - { - MetricName: "响应时间超时", - UnresolvedCount: 2, - TotalCount: 6, - }, - }, - CreatedBy: "admin", - CreatedAt: now, - UpdatedBy: "admin", - UpdatedAt: now, - }, - }, - RiskLevelDistribution: []entity.RiskLevelDistribution{ - { - Name: "重大风险", - Value: 1, - }, - { - Name: "较大风险", - Value: 2, - }, - { - Name: "一般风险", - Value: 3, - }, - { - Name: "低风险", - Value: 4, - }, - }, - AlertStatusDistribution: []entity.AlertStatusDistribution{ - { - Name: "未恢复", - Value: 4, - }, - { - Name: "已恢复", - Value: 6, - }, - }, - AlertClassDistribution: []entity.AlertClassDistribution{ - { - Name: "容器集群", - Value: 1, - }, - { - Name: "容器节点", - Value: 2, - }, - { - Name: "容器组", - Value: 3, - }, - { - Name: "网关", - Value: 4, - }, - }, - AlertFrequencyDistribution: entity.AlertFrequencyDistribution{ - XAxis: []string{"0-3时", "3-6时", "6-9时", "9-12时", "12-15时", "15-18时", "18-21时", "21-24时"}, - Data: []int{12, 20, 11, 50, 60, 30, 16, 6}, - }, + AlertOverview: alertOverviewList, + RiskLevelDistribution: riskLevelDistributions, + AlertStatusDistribution: alertStatusDistributions, + AlertClassDistribution: alertClassDistributions, + AlertFrequencyDistribution: alertFrequencyDistribution, + } + return +} + +func (a *AlertOverviewSvc) AlertOverview(req request.DetailAlertOverview) (resp []entity.AlertOverview, err error) { + var ( + sources response.AggAlertOverview + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + if req.StartTime == "" { + req.StartTime = time.Now().Format("2006-01-02") + } + if req.EndTime == "" { + req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02") + } + + content := fmt.Sprintf(`{ + "size": 0, + "query": { + "range": { + "created_at": { + "gte": "%s", + "lte": "%s" + } + } + }, + "aggs": { + "group": { + "terms": { + "field": "risk_level", + "size": 10 + }, + "aggs": { + "group": { + "terms": { + "field": "metric_config_id", + "size": 10 + }, + "aggs": { + "unresolved_count": { + "filter": { + "term": { + "status": 2 + } + } + } + } + } + } + } + } +}`, req.StartTime, req.EndTime) + + body, err := executeQuery(cli, conf.Options.OpenSearchIndex, content) + if err != nil { + return + } + + result := gjson.GetBytes(body, "aggregations") + err = json.Unmarshal([]byte(result.String()), &sources) + if err != nil { + return + } + + now := jsontime.Now() + for _, v := range sources.Group.Buckets { + alertOverview := entity.AlertOverview{ + RiskLevel: v.Key, + UnresolvedCount: 0, + TotalCount: v.DocCount, + List: nil, + CreatedBy: a.User.SystemAccount, + CreatedAt: now, + UpdatedBy: a.User.SystemAccount, + UpdatedAt: now, + } + var unresolvedCount int + var alertArray []entity.AlertArray + for _, bucket := range v.Group.Buckets { + alertArray = append(alertArray, entity.AlertArray{ + MetricName: bucket.Key, + UnresolvedCount: bucket.UnresolvedCount.DocCount, + TotalCount: bucket.DocCount, + }) + unresolvedCount += bucket.UnresolvedCount.DocCount + } + alertOverview.UnresolvedCount = unresolvedCount + alertOverview.List = alertArray + resp = append(resp, alertOverview) + } + return + +} + +func (a *AlertOverviewSvc) RiskLevelDistribution(req request.DetailAlertOverview) (resp []entity.AlertDistribution, err error) { + var ( + sources response.AlertDistributionGroup + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + if req.StartTime == "" { + req.StartTime = time.Now().Format("2006-01-02") + } + if req.EndTime == "" { + req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02") + } + + body, err := executeQuery(cli, conf.Options.OpenSearchIndex, buildAggQueryContent(req.StartTime, req.EndTime, "risk_level")) + if err != nil { + return } + result := gjson.GetBytes(body, "aggregations") + err = json.Unmarshal([]byte(result.String()), &sources) + if err != nil { + return + } + + var alertDistributions []entity.AlertDistribution + riskLevels := []int{constant.RiskLevelLow, constant.RiskLevelModerate, constant.RiskLevelHigh, constant.RiskLevelCritical} + for _, level := range riskLevels { + riskLevelDistribution := entity.AlertDistribution{Name: constant.RiskLeveText(level)} + for _, bucket := range sources.Group.Buckets { + if level == bucket.Key { + riskLevelDistribution.Value = bucket.DocCount + break + } + } + alertDistributions = append(alertDistributions, riskLevelDistribution) + } + resp = alertDistributions return + +} + +func (a *AlertOverviewSvc) AlertStatusDistribution(req request.DetailAlertOverview) (resp []entity.AlertDistribution, err error) { + var ( + sources response.AlertDistributionGroup + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + if req.StartTime == "" { + req.StartTime = time.Now().Format("2006-01-02") + } + if req.EndTime == "" { + req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02") + } + + body, err := executeQuery(cli, conf.Options.OpenSearchIndex, buildAggQueryContent(req.StartTime, req.EndTime, "status")) + if err != nil { + return + } + result := gjson.GetBytes(body, "aggregations") + err = json.Unmarshal([]byte(result.String()), &sources) + if err != nil { + return + } + + var alertDistributions []entity.AlertDistribution + alertStatusList := []int{constant.AlertRecovered, constant.AlertNotRecovered, constant.AlertClosed} + for _, status := range alertStatusList { + alertStatusDistribution := entity.AlertDistribution{Name: constant.AlertStatusText(status)} + for _, bucket := range sources.Group.Buckets { + if status == bucket.Key { + alertStatusDistribution.Value = bucket.DocCount + break + } + } + alertDistributions = append(alertDistributions, alertStatusDistribution) + } + resp = alertDistributions + return + +} + +func (a *AlertOverviewSvc) AlertClassDistribution(req request.DetailAlertOverview) (resp []entity.AlertDistribution, err error) { + var ( + sources response.AlertDistributionGroup + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + if req.StartTime == "" { + req.StartTime = time.Now().Format("2006-01-02") + } + if req.EndTime == "" { + req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02") + } + + body, err := executeQuery(cli, conf.Options.OpenSearchIndex, buildAggQueryContent(req.StartTime, req.EndTime, "class_id")) + if err != nil { + return + } + + result := gjson.GetBytes(body, "aggregations") + err = json.Unmarshal([]byte(result.String()), &sources) + if err != nil { + return + } + + alertClassSvc := AlertClassSvc{User: a.User} + alertObjectList, err := alertClassSvc.AlertObjectList() + + var alertDistributions []entity.AlertDistribution + for _, bucket := range sources.Group.Buckets { + alertDistribution := entity.AlertDistribution{Value: bucket.DocCount} + for _, object := range alertObjectList.List { + if bucket.Key == object.ClassId { + alertDistribution.Name = object.ClassName + } + } + alertDistributions = append(alertDistributions, alertDistribution) + } + resp = alertDistributions + return + +} + +func (a *AlertOverviewSvc) AlertFrequencyDistribution(req request.DetailAlertOverview) (resp entity.AlertFrequencyDistribution, err error) { + var ( + sources response.DateHistogramGroup + ) + + cli, err := client.GetOpenSearch() + if err != nil { + return + } + + if req.StartTime == "" { + req.StartTime = time.Now().Format("2006-01-02") + } + if req.EndTime == "" { + req.EndTime = time.Now().Add(time.Hour * 24).Format("2006-01-02") + } + + /* + "gte": "now/d", + "lt": "now+1d/d" + */ + /* + "gte": "2023-07-17", + "lte": "2023-07-18" + */ + content := `{ + "size": 0, + "query": { + "range": { + "created_at": { + "gte": "now/d", + "lt": "now+1d/d" + } + } + }, + "aggs": { + "group": { + "date_histogram": { + "field": "created_at", + "interval": "3h", + "format": "HH", + "time_zone": "+00:00" + } + } + } + }` + + body, err := executeQuery(cli, conf.Options.OpenSearchIndex, content) + if err != nil { + return + } + + result := gjson.GetBytes(body, "aggregations") + err = json.Unmarshal([]byte(result.String()), &sources) + if err != nil { + return + } + + alertFrequencyDistribution := entity.AlertFrequencyDistribution{ + XAxis: []string{"0-3时", "3-6时", "6-9时", "9-12时", "12-15时", "15-18时", "18-21时", "21-24时"}, + Data: []int{0, 0, 0, 0, 0, 0, 0, 0}, + } + + for i := 0; i < len(alertFrequencyDistribution.XAxis); i++ { + for _, bucket := range sources.Group.Buckets { + if cast.ToInt(bucket.KeyAsString) == i*3 { + alertFrequencyDistribution.Data[i] = bucket.DocCount + break + } + } + } + + resp = alertFrequencyDistribution + return + +} + +func buildAggQueryContent(startTime, endTime string, field string) string { + return fmt.Sprintf(`{ + "size": 0, + "query": { + "range": { + "created_at": { + "gte": "%s", + "lte": "%s" + } + } + }, + "aggs": { + "group": { + "terms": { + "field": "%s", + "size": 10 + } + } + } + }`, startTime, endTime, field) +} + +func executeQuery(cli *opensearch.Client, index string, content string) ([]byte, error) { + res := opensearchapi.SearchRequest{ + Index: []string{index}, + Body: strings.NewReader(content), + } + + do, err := res.Do(context.Background(), cli) + if err != nil { + return nil, err + } + defer do.Body.Close() + + if do.StatusCode < http.StatusOK && do.StatusCode > http.StatusIMUsed { + return nil, errors.New(do.String()) + } + + body, err := io.ReadAll(do.Body) + if err != nil { + return nil, err + } + + return body, nil } func (a *AlertOverviewSvc) List(req request.ListAlertOverview) (resp response.AlertOverviewList, err error) { @@ -247,7 +439,7 @@ func (a *AlertOverviewSvc) List(req request.ListAlertOverview) (resp response.Al } session := db.NewSession() defer session.Close() - session.Where("source_from = 1") + session.Where("source = 1") if req.ClassId != 0 { session.Where("class_id = ?", req.ClassId) } diff --git a/src/service/alert_rules.go b/src/service/alert_rules.go index 32b89d35711e6a30e8e156c303402678d610bf63..a122a73943e25e5930acb3a585be6ca42fe12747 100644 --- a/src/service/alert_rules.go +++ b/src/service/alert_rules.go @@ -9,6 +9,7 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" @@ -56,29 +57,31 @@ func (a *AlertRulesSvc) Add(req request.AddAlertRules) (err error) { alertClassSvc := AlertClassSvc{User: a.User} classParentId, err = alertClassSvc.Add(session, request.AddAlertClass{ - ClassName: req.ClassParentName, - SortOrder: -1, - SourceFrom: 2, + ClassName: req.ClassParentName, + SortOrder: -1, + Source: constant.SourceCustom, }) if err != nil { return nil, err } classId, err = alertClassSvc.Add(session, request.AddAlertClass{ - ClassName: req.ClassName, - ParentId: classParentId, - SortOrder: -1, - SourceFrom: 2, + ClassName: req.ClassName, + ParentId: classParentId, + SortOrder: -1, + Source: constant.SourceCustom, }) if err != nil { return nil, err } data.ClassId = classId + req.ClassId = classId // 添加指标配置 metricConfigSvc := MetricConfigSvc{User: a.User} _ = copier.Copy(&addMetricConfig, &req) - addMetricConfig.SourceFrom = 2 + addMetricConfig.Source = constant.SourceCustom addMetricConfig.MetricName = req.MetricConfigName + addMetricConfig.ClassId = classId metricConfigId, err = metricConfigSvc.Add(session, addMetricConfig) if err != nil { return nil, err @@ -87,14 +90,22 @@ func (a *AlertRulesSvc) Add(req request.AddAlertRules) (err error) { // 添加预警规则配置 _, err = session.Insert(&data) + if err != nil { + return nil, err + } return nil, err }) + + err = a.CreatePrometheusRule(req.IsEnabled, data.Id, db, "") if err != nil { return err } } err = a.CreatePrometheusRule(req.IsEnabled, data.Id, db, "") + if err != nil { + return err + } return nil } @@ -124,7 +135,11 @@ func (a *AlertRulesSvc) Update(req request.UpdateAlertRules) (err error) { } switch req.DetectionType { case 1: - _, err = db.ID(data.Id).Update(&data) + _, err = db.ID(data.Id).MustCols("duration").Update(&data) + if err != nil { + return err + } + err = a.CreatePrometheusRule(req.IsEnabled, data.Id, db, "update") if err != nil { return err } @@ -133,24 +148,37 @@ func (a *AlertRulesSvc) Update(req request.UpdateAlertRules) (err error) { // 更新自定义分类 var ( updateMetricConfig request.UpdateMetricConfig - alertClassItem response.AlertClassItem + alertClass response.AlertClassItem + alertParentClass response.AlertClassItem ) alertClassSvc := AlertClassSvc{User: a.User} - alertClassItem, err = alertClassSvc.GetDataById(request.DetailAlertClass{ClassId: dbAlertRules.ClassId}) + alertClass, err = alertClassSvc.GetDataById(request.DetailAlertClass{ClassId: dbAlertRules.ClassId}) if err != nil { return nil, err } err = alertClassSvc.Update(session, request.UpdateAlertClass{ ClassId: dbAlertRules.ClassId, ClassName: req.ClassName, + ParentId: alertClass.ParentId, }) if err != nil { return nil, err } + + if alertClass.ParentId == 0 { + return nil, errors.New("预警分类为空") + } + + alertParentClass, err = alertClassSvc.GetDataById(request.DetailAlertClass{ClassId: alertClass.ParentId}) + if err != nil { + return nil, err + } + err = alertClassSvc.Update(session, request.UpdateAlertClass{ - ClassId: alertClassItem.ParentId, + ClassId: alertClass.ParentId, ClassName: req.ClassParentName, + ParentId: alertParentClass.ParentId, }) if err != nil { return nil, err @@ -159,17 +187,18 @@ func (a *AlertRulesSvc) Update(req request.UpdateAlertRules) (err error) { // 更新指标配置 metricConfigSvc := MetricConfigSvc{User: a.User} _ = copier.Copy(&updateMetricConfig, &req) - updateMetricConfig.SourceFrom = 2 + updateMetricConfig.Source = constant.SourceCustom updateMetricConfig.Id = dbAlertRules.MetricConfigId err = metricConfigSvc.Update(session, updateMetricConfig) if err != nil { return nil, err } - - // 更新预警规则配置 - _, err = session.ID(data.Id).Update(&data) return nil, err }) + + // 更新预警策略配置 + _, err = session.ID(data.Id).MustCols("duration").Update(&data) + err = a.CreatePrometheusRule(req.IsEnabled, data.Id, db, "update") if err != nil { return err } @@ -237,16 +266,20 @@ func (a *AlertRulesSvc) UpdateIsEnabled(req request.UpdateIsEnabledAlertRules) ( return } if req.IsEnabled == 2 { - if item.IsEnabled == 1 { - prSvc := PrometheusRuleSvc{User: a.User} + prSvc := PrometheusRuleSvc{User: a.User} + var exist bool + _, exist, err = prSvc.Get(item) + if exist { err = prSvc.Delete(item) if err != nil { return } } } else if req.IsEnabled == 1 { - if item.IsEnabled == 2 { - prSvc := PrometheusRuleSvc{User: a.User} + prSvc := PrometheusRuleSvc{User: a.User} + var exist bool + _, exist, err = prSvc.Get(item) + if !exist { err = prSvc.Create(item) if err != nil { return @@ -307,7 +340,7 @@ func (a *AlertRulesSvc) List(req request.ListAlertRules) (resp response.AlertRul Or("acp.class_name LIKE ?", "%"+req.Keyword+"%") } resp.TotalCount, err = session.Limit(req.GetPageSize(), (req.GetPage()-1)*req.GetPageSize()). - OrderBy("r.created_at desc"). + OrderBy("r.is_enabled asc,r.created_at desc"). FindAndCount(&resp.List) for i := 0; i < len(resp.List); i++ { _ = json.Unmarshal([]byte(resp.List[i].AlertRules.AlertCondition), &resp.List[i].AlertCondition) @@ -332,14 +365,15 @@ func (a *AlertRulesSvc) Delete(ids []string) (err error) { } if !exist { prSvc := PrometheusRuleSvc{User: a.User} - err = prSvc.Delete(response.AlertRulesItem{AlertRules: entity.AlertRules{Id: id}}) - if err != nil { - return + var has bool + _, has, err = prSvc.Get(response.AlertRulesItem{AlertRules: entity.AlertRules{Id: id}}) + if has { + err = prSvc.Delete(response.AlertRulesItem{AlertRules: entity.AlertRules{Id: id}}) + if err != nil { + return + } } _, err = db.NewSession().Where("id = ?", id).Delete(new(entity.AlertRules)) - if err != nil { - return - } } else { return errors.New("alert_rules_id already exists in opensearch") } diff --git a/src/service/alert_webhook.go b/src/service/alert_webhook.go index a6a889a71e8f3b614490cbb05cb998b4ac6e92fa..d5890b3145e0e71e12e2fb5d16ec8d498b926ca2 100644 --- a/src/service/alert_webhook.go +++ b/src/service/alert_webhook.go @@ -7,8 +7,11 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" + "go.uber.org/zap" + "time" "xorm.io/xorm" ) @@ -37,16 +40,20 @@ func (a *AlertWebhookSvc) AlertWebhook(session *xorm.Session, req webhook.Messag ) if alertRulesId, ok = alert.Labels["alert_rules_id"]; !ok { - return errors.New("alert_rules_id not found in the map") + err = errors.New("alert_rules_id not found in the map") + conf.Logger.Error("err", zap.Error(err)) } if riskLevelStr, ok = alert.Labels["risk_level"]; !ok { - return errors.New("risk_level not found in the map") + err = errors.New("risk_level not found in the map") + conf.Logger.Error("err", zap.Error(err)) } riskLevel = cast.ToInt(riskLevelStr) if currentValueStr, ok = alert.Annotations["value"]; !ok { - return errors.New("value not found in the map") + err = errors.New("value not found in the map") + conf.Logger.Error("err", zap.Error(err)) + return } currentValue = cast.ToFloat64(currentValueStr) @@ -55,6 +62,11 @@ func (a *AlertWebhookSvc) AlertWebhook(session *xorm.Session, req webhook.Messag return } + if alertRulesItem.Id == "" { + conf.Logger.Error("err", zap.Error(errors.New("告警规则查询为空"))) + return + } + alertItem, err = alertSvc.GetDataByAlertRulesIdAndRiskLevel(alertRulesId, riskLevel, 2) if err != nil { return @@ -68,12 +80,18 @@ func (a *AlertWebhookSvc) AlertWebhook(session *xorm.Session, req webhook.Messag switch isNewAlert { case true: // 新增数据到OpenSearch - max := alertSvc.CatCount(OpenSearchIndex) + var max int + max, err = alertSvc.GetIndexMaxID(conf.Options.OpenSearchIndex) + if err != nil { + // 获取id最大值 + max = alertSvc.CatCount(conf.Options.OpenSearchIndex) + } if max == 0 { err = errors.New("failed to get doc count for index") + conf.Logger.Error("err", zap.Error(err)) return } - alertId := max + 1 + id := max + 1 for _, v := range alertRulesItem.AlertCondition { if v.RiskLevel == riskLevel { alertCondition = v @@ -81,12 +99,12 @@ func (a *AlertWebhookSvc) AlertWebhook(session *xorm.Session, req webhook.Messag } } createAlert := request.CreateAlert{Alert: entity.Alert{ - Id: alertId, + Id: id, AlertPoint: alertRulesItem.ClassParentName + "/" + alertRulesItem.MetricName, AlertRulesId: alertRulesItem.Id, AlertRulesName: alertRulesItem.MetricName, RiskLevel: riskLevel, - AlertTime: jsontime.Time(alert.StartsAt), + AlertTime: jsontime.Time(alert.StartsAt.Add(time.Hour * 8)), ClassId: alertRulesItem.ClassId, ClassParentName: alertRulesItem.ClassParentName, ClassName: alertRulesItem.ClassName, diff --git a/src/service/cron/common.go b/src/service/cron/common.go index d5acf394717d8f198e26f057bc059558d8fb85c9..10e8b1a0ab9bd2bf8d4f106b63a6b75570323b39 100644 --- a/src/service/cron/common.go +++ b/src/service/cron/common.go @@ -10,6 +10,4 @@ func StartCron() { defer c.Start() c.AddFunc("0 0 0 * * *", service.CronStatusDetection) // 每天凌晨0点状态检测 - - c.AddFunc("0 0 0 * * *", service.CronWorkOrderIssuance) // 每天凌晨0点扫描当天需下发工单 } diff --git a/src/service/k8s/prometheusrule.go b/src/service/k8s/prometheusrule.go index 1697cf49899de243f91d7bc183384cad1ec9734c..72f45bad813515b118a57e4930206a79644433c4 100644 --- a/src/service/k8s/prometheusrule.go +++ b/src/service/k8s/prometheusrule.go @@ -6,30 +6,46 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "strings" + "sync" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" ) var ( - PrometheusRuleGroup = "monitoring.beagle.io" // kubectl api-resources | grep -i prome - PrometheusRuleVersion = "v1" - PrometheusRuleKind = "PrometheusRule" - Namespace = "beagle-monitoring" - PrometheusRuleApiVersion = PrometheusRuleGroup + "/" + PrometheusRuleVersion - PrometheusRuleName = strings.ToLower(PrometheusRuleKind) + "s." + PrometheusRuleGroup - PrometheusRuleNamePrefix = "beagle-prometheus-so-operation-api-rules" // beagle-monitoring beagle-prometheus-prometheus-operator 43d + promOnce sync.Once + prometheusRuleName string + + alertOnce sync.Once + alertDefLabels map[string]string ) -var AlertDefLabels = map[string]string{ - "app": "prometheus", - "app.bd-apaas.com/cluster-component": "monitoring", - "prometheus-operator": "monitoring", - "release": "beagle-prometheus", +func GetPrometheusRuleCRDName() string { + promOnce.Do(func() { + url := conf.Options.MonitorApiVersion // 请确保 conf 和其他相关配置可用 + parts := strings.Split(url, "/") + if len(parts) == 0 || parts[0] == "" { + prometheusRuleName = "prometheusrules.monitoring.beagle.io" + } else { + prometheusRuleName = fmt.Sprintf("prometheusrules.%s", parts[0]) + } + }) + return prometheusRuleName +} + +func GetAlertDefLabels() map[string]string { + alertOnce.Do(func() { + alertDefLabels = make(map[string]string) + err := json.Unmarshal([]byte(conf.Options.MonitorMatchLabelsStr), &alertDefLabels) + if err != nil { + fmt.Println("Error parsing JSON:", err) + } + }) + return alertDefLabels } -// GetPrometheusRuleName 获取规则CRD名称 -func GetPrometheusRuleName(alertRulesId string) string { - return fmt.Sprintf("%s-%s", PrometheusRuleNamePrefix, alertRulesId) +// GetPrometheusRuleId 获取规则CRD名称 +func GetPrometheusRuleId(alertPolicyId string) string { + return fmt.Sprintf("%s-%s", conf.Options.PrometheusRuleNamePrefix, alertPolicyId) } // GetPrometheusRuleGroupName 获取规则组名称 @@ -43,19 +59,19 @@ type PrometheusRule struct { func (p PrometheusRule) Create(pRule *monitoringv1.PrometheusRule) error { k8sSvc := K8sSvc{Header: p.Header} - c := &Content{Kind: PrometheusRuleKind, ApiVersion: PrometheusRuleApiVersion, Metadata: pRule.ObjectMeta, Spec: pRule.Spec} + c := &Content{Kind: "PrometheusRule", ApiVersion: conf.Options.MonitorApiVersion, Metadata: pRule.ObjectMeta, Spec: pRule.Spec} _, err := k8sSvc.SendFile(c) return err } func (p PrometheusRule) Delete(namespace string, name string) error { - delUrl := fmt.Sprintf("%s/kubernetes/api/v1/_raw/%s/namespace/%s/name/%s", conf.Options.AweRestURL, PrometheusRuleName, namespace, name) + delUrl := fmt.Sprintf("%s/kubernetes/api/v1/_raw/%s/namespace/%s/name/%s", conf.Options.AweRestURL, GetPrometheusRuleCRDName(), namespace, name) _, err := util.ProxySendRes("DELETE", delUrl, "", p.Header) return err } func (p PrometheusRule) Update(pRule *monitoringv1.PrometheusRule) error { - updateUrl := fmt.Sprintf("%s/kubernetes/api/v1/_raw/%s/namespace/%s/name/%s", conf.Options.AweRestURL, PrometheusRuleName, pRule.Namespace, pRule.Name) + updateUrl := fmt.Sprintf("%s/kubernetes/api/v1/_raw/%s/namespace/%s/name/%s", conf.Options.AweRestURL, GetPrometheusRuleCRDName(), pRule.Namespace, pRule.Name) body, _ := json.Marshal(pRule) p.Header["Content-Type"] = "application/json" _, err := util.ProxySendRes("PUT", updateUrl, string(body), p.Header) @@ -63,7 +79,7 @@ func (p PrometheusRule) Update(pRule *monitoringv1.PrometheusRule) error { } func (p PrometheusRule) Get(namespace string, name string) (obj *monitoringv1.PrometheusRule, err error) { - getUrl := fmt.Sprintf("%s/kubernetes/api/v1/_raw/%s/namespace/%s/name/%s", conf.Options.AweRestURL, PrometheusRuleName, namespace, name) + getUrl := fmt.Sprintf("%s/kubernetes/api/v1/_raw/%s/namespace/%s/name/%s", conf.Options.AweRestURL, GetPrometheusRuleCRDName(), namespace, name) res, err := util.ProxySendRes("GET", getUrl, "", p.Header) if err != nil { return diff --git a/src/service/metric_config.go b/src/service/metric_config.go index 6792a5b72874441d7eaa7abfb0e5e36e637e4c24..bdd664363db57bc0456c31318232cf7ef15aa9e7 100644 --- a/src/service/metric_config.go +++ b/src/service/metric_config.go @@ -7,6 +7,7 @@ import ( "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "xorm.io/xorm" @@ -25,6 +26,9 @@ func (m *MetricConfigSvc) Add(session *xorm.Session, req request.AddMetricConfig UpdatedBy: m.User.SystemAccount, UpdatedAt: now, } + if req.Source != constant.SourceCustom { + req.Source = constant.SourceDefault + } _ = copier.Copy(&data, &req) data.AlertRange = util.ConvertToString(req.AlertRange) _, err = session.Insert(&data) @@ -89,6 +93,17 @@ func (m *MetricConfigSvc) List(req request.ListMetricConfig) (resp response.Metr return } +func (m *MetricConfigSvc) NameList() (resp response.MetricConfigList, err error) { + db, err := client.GetDbClient() + if err != nil { + return + } + session := db.NewSession() + defer session.Close() + resp.TotalCount, err = session.Select("id,metric_name").FindAndCount(&resp.List) + return +} + func (m *MetricConfigSvc) Delete(ids []string) (err error) { db, err := client.GetDbClient() if err != nil { diff --git a/src/service/prometheus.go b/src/service/prometheus.go index 7e42291fa3b1e7109e550f306537214d0e1ca6ce..879ec93522cf9564f7b882f6f5b2b99f84111364 100644 --- a/src/service/prometheus.go +++ b/src/service/prometheus.go @@ -26,10 +26,10 @@ func (p *PrometheusSvc) Label(req request.PrometheusLabel) (resp response.Promet if req.LabelName != "" { url := fmt.Sprintf("%s%s", conf.Options.PrometheusHost, "/api/v1/series") - bytes, _ := util.Request(url, http.MethodPost, + response, _ := util.Request(url, http.MethodPost, []byte(fmt.Sprintf("match[]=%s", req.LabelName)), map[string]string{"Content-Type": util.MediaTypeForm}) - _ = json.Unmarshal(bytes, &prometheusSeries) + _ = json.Unmarshal(response.Body(), &prometheusSeries) for _, v := range prometheusSeries.Data { for k, _ := range v { resp.List = append(resp.List, k) @@ -41,8 +41,8 @@ func (p *PrometheusSvc) Label(req request.PrometheusLabel) (resp response.Promet } else { url := fmt.Sprintf("%s%s", conf.Options.PrometheusHost, "/api/v1/label/__name__/values") - bytes, _ := util.Request(url, http.MethodGet, nil, nil) - _ = json.Unmarshal(bytes, &prometheusLabel) + response, _ := util.Request(url, http.MethodGet, nil, nil) + _ = json.Unmarshal(response.Body(), &prometheusLabel) resp.TotalCount = int64(len(prometheusLabel.Data)) resp.List = prometheusLabel.Data } @@ -57,10 +57,10 @@ func (p *PrometheusSvc) LabelValue(req request.PrometheusLabelValue) (resp respo ) url := fmt.Sprintf("%s%s", conf.Options.PrometheusHost, "/api/v1/series") - bytes, _ := util.Request(url, http.MethodPost, + response, _ := util.Request(url, http.MethodPost, []byte(fmt.Sprintf("match[]=%s", req.MetricName)), map[string]string{"Content-Type": util.MediaTypeForm}) - _ = json.Unmarshal(bytes, &prometheusSeries) + _ = json.Unmarshal(response.Body(), &prometheusSeries) for _, v := range prometheusSeries.Data { for key, value := range v { metricLabelMap[key] = append(metricLabelMap[key], value) diff --git a/src/service/prometheusrule.go b/src/service/prometheusrule.go index 1f06dd9543717f0ce29a521725fd0363d0252b93..913362b6b6e3fbb633c66ba7cd479d9a20e719df 100644 --- a/src/service/prometheusrule.go +++ b/src/service/prometheusrule.go @@ -1,31 +1,59 @@ package service import ( + "errors" "fmt" + json "github.com/json-iterator/go" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "github.com/spf13/cast" + "github.com/tidwall/gjson" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service/k8s" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "go.uber.org/zap" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "net/http" + "net/url" "strings" + "sync" ) +var prometheusRuleLabel map[string]string +var once sync.Once + +func initPrometheusRuleLabel() { + once.Do(func() { + str := conf.Options.PrometheusRuleLabel + err := json.Unmarshal([]byte(str), &prometheusRuleLabel) + if err != nil { + prometheusRuleLabel = map[string]string{ // 返回默认标签 + "source": "aiops-systemmonitor-api", + } + } + }) +} + +// GetPrometheusRuleLabel 返回 prometheusRuleLabel 单例 +func GetPrometheusRuleLabel() map[string]string { + initPrometheusRuleLabel() + return prometheusRuleLabel +} + type PrometheusRuleSvc struct { User entity.SystemUserInfo } func (p *PrometheusRuleSvc) Create(data response.AlertRulesItem) (err error) { - prometheusRuleName := k8s.GetPrometheusRuleName(data.Id) + prometheusRuleObjName := k8s.GetPrometheusRuleId(data.Id) pr := monitoringv1.PrometheusRule{ ObjectMeta: v1.ObjectMeta{ - Name: prometheusRuleName, - Namespace: k8s.Namespace, - Labels: k8s.AlertDefLabels, + Name: prometheusRuleObjName, + Namespace: conf.Options.MonitorMatchNs, + Labels: k8s.GetAlertDefLabels(), }, } @@ -38,17 +66,24 @@ func (p *PrometheusRuleSvc) Create(data response.AlertRulesItem) (err error) { item := fmt.Sprintf(`%s%s"%s"`, v.MetricLabel, v.Compare, v.Value) // http_requests_total{method="GET",pod="LeaseGrant"} data.Expr = strings.ReplaceAll(data.Expr, v.VariableName, item) } - for _, v := range data.AlertCondition { + for k, v := range data.AlertCondition { + labels := map[string]string{ + "severity": "warning", + "risk_level": cast.ToString(v.RiskLevel), + "risk_level_name": constant.RiskLeveText(v.RiskLevel), + "namespace": conf.Options.MonitorMatchNs, + "alert_rules_id": data.Id, + "metric_config_id": data.MetricConfigId, + } + + for key, value := range GetPrometheusRuleLabel() { + labels[key] = value + } rule := monitoringv1.Rule{ - Alert: data.MetricConfigName, - For: &ruleFor, - Labels: map[string]string{ - "severity": "warning", - "risk_level": cast.ToString(v.RiskLevel), - "risk_level_name": constant.RiskLeveText(v.RiskLevel), - "source": "so-operation-api", - "alert_rules_id": data.MetricConfigId, - }, + // promhttp超过5万次告警-prom指标控制器请求数-较大风险-3 + Alert: fmt.Sprintf("%s-%s-%s-%d", data.MetricName, data.MetricConfigName, constant.RiskLeveText(v.RiskLevel), k+1), + For: &ruleFor, + Labels: labels, Annotations: map[string]string{ "value": "{{ $value }}", "summary": fmt.Sprintf("分组名:%s, 检查周期:%s, 持续时间:%s", group.Name, string(groupInterval), string(ruleFor)), @@ -68,6 +103,11 @@ func (p *PrometheusRuleSvc) Create(data response.AlertRulesItem) (err error) { condition += 2 } + // 为"空"状态下,默认表达式已经有比较判断,故直接使用表达式即可 + if data.AlertRuleTypeName == "空" { + condition = 0 + } + switch condition { default: expr = data.Expr @@ -79,6 +119,12 @@ func (p *PrometheusRuleSvc) Create(data response.AlertRulesItem) (err error) { expr = fmt.Sprintf("%s <= %s <=%s", cast.ToString(v.ThresholdsMin), data.Expr, cast.ToString(v.ThresholdsMax)) } + // 校验表达式正确性 + err = CheckPrometheusQuerySyntax(expr) + if err != nil { + return + } + rule.Expr = intstr.FromString(expr) group.Rules = append(group.Rules, rule) } @@ -91,12 +137,12 @@ func (p *PrometheusRuleSvc) Create(data response.AlertRulesItem) (err error) { } func (p *PrometheusRuleSvc) Get(data response.AlertRulesItem) (obj *monitoringv1.PrometheusRule, exist bool, err error) { - prometheusRuleName := k8s.GetPrometheusRuleName(data.Id) + prometheusRuleObjName := k8s.GetPrometheusRuleId(data.Id) pr := monitoringv1.PrometheusRule{ ObjectMeta: v1.ObjectMeta{ - Name: prometheusRuleName, - Namespace: k8s.Namespace, - Labels: k8s.AlertDefLabels, + Name: prometheusRuleObjName, + Namespace: conf.Options.MonitorMatchNs, + Labels: k8s.GetAlertDefLabels(), }, } header := map[string]string{"Authorization": "Bearer " + conf.Options.KubernetesToken} @@ -109,13 +155,26 @@ func (p *PrometheusRuleSvc) Get(data response.AlertRulesItem) (obj *monitoringv1 return } +// CheckPrometheusQuerySyntax 校验普罗米修斯语法正确性 +func CheckPrometheusQuerySyntax(expr string) error { + params := url.Values{} + params.Add("query", expr) + query := params.Encode() + webUrl := fmt.Sprintf("%s%s%s", conf.Options.PrometheusHost, "/api/v1/query?", query) + resp, _ := util.Request(webUrl, http.MethodGet, nil, nil) + if resp.StatusCode() != http.StatusOK { + return errors.New(fmt.Sprintf("%s, err: %s", "普罗米修斯语法PromQL错误", gjson.GetBytes(resp.Body(), "error").String())) + } + return nil +} + func (p *PrometheusRuleSvc) Delete(data response.AlertRulesItem) (err error) { - prometheusRuleName := k8s.GetPrometheusRuleName(data.Id) + prometheusRuleObjName := k8s.GetPrometheusRuleId(data.Id) pr := monitoringv1.PrometheusRule{ ObjectMeta: v1.ObjectMeta{ - Name: prometheusRuleName, - Namespace: k8s.Namespace, - Labels: k8s.AlertDefLabels, + Name: prometheusRuleObjName, + Namespace: conf.Options.MonitorMatchNs, + Labels: k8s.GetAlertDefLabels(), }, } diff --git a/src/service/task_manage.go b/src/service/task_manage.go index 00a06e9645cc597413e3ca95738d277237268e41..49e9f04b96220887d5303e70e37914b5d4e2d936 100644 --- a/src/service/task_manage.go +++ b/src/service/task_manage.go @@ -272,85 +272,6 @@ func (t *TaskManageSvc) ExecScript(req request.ExecScriptReq, script string) (id return } go ExecAnsible(id, req.TaskId, req.Value) - ////执行ansible命令 - //var cmd *exec.Cmd - //if req.Value != "" { - // cmd = exec.Command("ansible-playbook", "-i", "/etc/ansible/hosts_"+fmt.Sprintf("%d", req.TaskId), "/etc/ansible/ansible_"+fmt.Sprintf("%d", req.TaskId)+".yml", "--extra-vars", req.Value) - //} else { - // cmd = exec.Command("ansible-playbook", "-i", "/etc/ansible/hosts_"+fmt.Sprintf("%d", req.TaskId), "/etc/ansible/ansible_"+fmt.Sprintf("%d", req.TaskId)+".yml") - //} - ////ansible-playbook -i /tmp/hosts --list-hosts debug.yml - ////捕获正常日志 - //stdout, err := cmd.StdoutPipe() - //if err != nil { - // err = resp.CmdExecError.WithError(err) - // return - //} - ////捕获异常日志 - //stderr, err := cmd.StderrPipe() - //if err != nil { - // err = resp.CmdExecError.WithError(err) - // return - //} - ////执行cmd命令 - //if err = cmd.Start(); err != nil { - // err = resp.CmdExecError.WithError(err) - // return - //} - ////获取 正常/异常 输出流 - //outputBuf := bufio.NewReader(stdout) - //readerr := bufio.NewReader(stderr) - // - //var out, outErr int - //var execLog string - //for { - // - // //逐行输出日志 - // lineOut, err1 := outputBuf.ReadString('\n') - // if (err1 != nil || io.EOF == err1) && out == 0 { - // out = 1 - // } else if out == 0 { - // //存储执行日志 - // execLog = execLog + lineOut + " \n " - // UpdateExecHistory(request.UpdateExecHistory{ - // TaskHistoryId: id, - // ExecLog: execLog, - // }) - // } - // - // lineErr, err2 := readerr.ReadString('\n') - // if (err2 != nil || io.EOF == err2) && outErr == 0 { - // outErr = 1 - // } else if outErr == 0 { - // //存储异常执行日志 - // execLog = execLog + lineErr + " \n " - // UpdateExecHistory(request.UpdateExecHistory{ - // TaskHistoryId: id, - // ExecLog: execLog, - // }) - // } - // - // if out == 1 && outErr == 1 { - // break - // } - //} - //cmd.Wait() - // - //if cmd.ProcessState.Success() { - // //任务执行成功 - // UpdateExecHistory(request.UpdateExecHistory{ - // TaskHistoryId: id, - // ExecLog: execLog, - // State: 1, - // }) - //} else { - // //任务执行失败 - // UpdateExecHistory(request.UpdateExecHistory{ - // TaskHistoryId: id, - // ExecLog: execLog, - // State: 2, - // }) - //} return } diff --git a/src/service/work_order.go b/src/service/work_order.go index dc2d60e2b946f75aebf26ae9b7865c42266d89e5..6776b35a2b01a254c24d2d71ad1ff86666702670 100644 --- a/src/service/work_order.go +++ b/src/service/work_order.go @@ -5,19 +5,24 @@ import ( "fmt" "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" json "github.com/json-iterator/go" + "github.com/robfig/cron/v3" "github.com/wanghuiyt/ding" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/jsontime" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "go.uber.org/zap" "strings" "time" ) +var workOrderCron = cron.New(cron.WithSeconds()) + type WorkOrderManageSvc struct { User entity.SystemUserInfo } @@ -151,7 +156,7 @@ func (w *WorkOrderManageSvc) EditWorkOrderManage(req request.EditWorkOrderReq) ( return } -// StateWorkOrderManage 编辑业务工单管理 +// StateWorkOrderManage 定时是否启用 func (w *WorkOrderManageSvc) StateWorkOrderManage(req request.StateWorkOrderReq) (err error) { db, err := client.GetDbClient() if err != nil { @@ -165,6 +170,10 @@ func (w *WorkOrderManageSvc) StateWorkOrderManage(req request.StateWorkOrderReq) err = resp.DbUpdateError.WithError(err) return } + + //创建定时任务 + CronPushWorkOrder() + return } @@ -176,11 +185,31 @@ func (w *WorkOrderManageSvc) DelWorkOrderManage(req request.DelWorkOrderReq) (er return } + session := db.NewSession() + defer session.Close() + session.Begin() + _, err = db.Table("work_order_manage").In("id", req.Id).Delete(&entity.WorkOrder{}) if err != nil { err = resp.DbDeleteError.WithError(err) + session.Rollback() + return + } + + _, err = db.Table("work_order_issuance").In("order_id", req.Id).Delete(&entity.WorkOrderIssuance{}) + if err != nil { + err = resp.DbDeleteError.WithError(err) + session.Rollback() + return + } + + _, err = db.Table("work_order_me").In("order_id", req.Id).Delete(&entity.WorkOrderMe{}) + if err != nil { + err = resp.DbDeleteError.WithError(err) + session.Rollback() return } + session.Commit() return } @@ -251,25 +280,26 @@ func (w *WorkOrderManageSvc) ListWorkOrderManage(req request.ListWorkOrderManage return } - finder := db.Table("work_order_manage") + finder := db.Table("work_order_manage").Alias("wom") if req.Search != "" { - finder.Where(fmt.Sprintf("order_name LIKE '%s'", "%"+req.Search+"%")) + finder.Where(fmt.Sprintf("wom.order_name LIKE '%s'", "%"+req.Search+"%")) } if req.OrderLevel != 0 { - finder.Where("order_level = ?", req.OrderLevel) + finder.Where("wom.order_level = ?", req.OrderLevel) } if req.TimingType != 0 { - finder.Where("timing_type = ?", req.TimingType) + finder.Where("wom.timing_type = ?", req.TimingType) } if req.CreateDateFrom != "" { - finder.Where("create_time >= ?", req.CreateDateFrom) + finder.Where("wom.create_time >= ?", req.CreateDateFrom) } if req.CreateDateTo != "" { - finder.Where("create_time <= ?", req.CreateDateTo) + finder.Where("wom.create_time <= ?", req.CreateDateTo) } - finder.OrderBy("create_time desc") + finder.OrderBy("wom.create_time desc") //查询任务 - total, err = finder.Select("id,order_name,order_level,order_cnt,push_obj,timing_type,timing_state,create_user,create_time"). + total, err = finder.Select("wom.id,wom.order_name,wom.order_level,(select count(*) from work_order_issuance woi "+ + "where woi.order_id = wom.id) as order_cnt,wom.push_obj,wom.timing_type,wom.timing_state,wom.create_user,wom.create_time"). Limit(req.PageSize, (req.Page-1)*req.PageSize).FindAndCount(&workOrderListRes) if err != nil { err = resp.DbSelectError.WithError(err) @@ -715,194 +745,138 @@ func WorkOrderPushNoteMsg(orderName string, phone []string, orderLevel int) (err return } -// CronWorkOrderIssuance 定时任务-每天凌晨0点检测当天需下发工单 -func CronWorkOrderIssuance() { +// PushObjMsg 解析用户并推送消息 +func PushObjMsg(obj, orderName string, orderLevel int) (err error) { + var pushObj request.PushObj + err = json.Unmarshal([]byte(obj), &pushObj) + if err != nil { + return + } + + var phones []string + for _, v := range pushObj.UserObj { + phones = append(phones, v.Phone) + } + switch pushObj.PushMethod { + case 1: + //发送钉钉消息 + err = WorkOrderPushDingTalkMsg(orderName, orderLevel, phones) + if err != nil { + return + } + case 2: + //发送短信 + err = WorkOrderPushNoteMsg(orderName, phones, orderLevel) + if err != nil { + return + } + case 3: + //发送钉钉消息 + err = WorkOrderPushDingTalkMsg(orderName, orderLevel, phones) + if err != nil { + return + } + //发送短信 + err = WorkOrderPushNoteMsg(orderName, phones, orderLevel) + if err != nil { + return + } + } + conf.Logger.Info("定时下发工单完成,工单名称:" + orderName) + return +} + +// CronPushWorkOrder 创建工单下发定时任务 +func CronPushWorkOrder() { db, err := client.GetDbClient() if err != nil { - fmt.Println("CronStatusDetection err:", err.Error()) + conf.Logger.Error("获取数据库连接", zap.Error(err)) return } //查询推送工单 workOrderList := make([]entity.WorkOrder, 0) - finder := db.Table("work_order_manage").Where("timing_state = 1 AND (timing_type = 2 OR timing_type = 3)") + finder := db.Table("work_order_manage").Where("timing_state = ? AND (timing_type = ? "+ + "OR timing_type = ?)", constant.TimingClick, constant.TimingWeekly, constant.TimingCustom) err = finder.Find(&workOrderList) if err != nil { - fmt.Println("CronStatusDetection err:", err.Error()) + conf.Logger.Error("查询推送工单", zap.Error(err)) return } - week := map[time.Weekday]int{ - time.Monday: 1, - time.Tuesday: 2, - time.Wednesday: 3, - time.Thursday: 4, - time.Friday: 5, - time.Saturday: 6, - time.Sunday: 7, - } - + workOrderCron.Stop() + defer workOrderCron.Start() for _, v := range workOrderList { - if v.TimingType == 2 { - //按周 + var pushObj request.PushObj + err = json.Unmarshal([]byte(v.PushObj), &pushObj) + if err != nil { + return + } + svc := WorkOrderManageSvc{User: entity.SystemUserInfo{SystemAccount: v.CreateUser}} + + if v.TimingType == constant.TimingWeekly { var timingWeekly request.TimingWeekly err = json.Unmarshal([]byte(v.TimingRule), &timingWeekly) if err != nil { - fmt.Println("CronStatusDetection err:", err.Error()) + conf.Logger.Error("反序列化定时规则", zap.Error(err)) continue } - days := week[time.Now().Weekday()] - for _, i := range timingWeekly.Week { - if i == days { - //写redis - err = PushRedisWorkOrder(timingWeekly.PointTime, v.PushObj, v.OrderName, v.OrderLevel) - if err != nil { - fmt.Println("CronStatusDetection redis set err:", err.Error()) - continue - } - break + + for k, v1 := range timingWeekly.Week { + if v1 == 7 { + timingWeekly.Week[k] = 0 } } - } else if v.TimingType == 3 { - //自定义时间 - var timingCustom []request.TimingCustom - err = json.Unmarshal([]byte(v.TimingRule), &timingCustom) + t, err := time.Parse(jsontime.LocalTimeFormat, timingWeekly.PointTime) if err != nil { - fmt.Println("CronStatusDetection json Unmarshal err:", err.Error()) + conf.Logger.Error("时间点类型转换错误", zap.Error(err)) continue } - for _, v1 := range timingCustom { - t := time.Now() - dateFrom, err1 := time.Parse(jsontime.LocalDateFormat, v1.DateFrom) - if err1 != nil { - fmt.Println("CronStatusDetection dateFrom parse err:", err1.Error()) - continue - } - dateTo, err1 := time.Parse(jsontime.LocalDateFormat, v1.DateTo) - if err1 != nil { - fmt.Println("CronStatusDetection dateTo parse err:", err1.Error()) - continue - } - if t.After(dateFrom) && t.Before(dateTo) { - //写redis - err = PushRedisWorkOrder(v1.PointTime, v.PushObj, v.OrderName, v.OrderLevel) - if err != nil { - fmt.Println("CronStatusDetection redis set err:", err.Error()) - continue - } - break + + expr := fmt.Sprintf("%d %d %d * * %s", t.Second(), t.Minute(), t.Hour(), strings.Join(util.IntsToStrings(timingWeekly.Week), ",")) + //创建定时任务 + workOrderCron.AddFunc(expr, func() { + err = svc.PushWorkOrderManage(request.PushWorkOrderReq{Id: v.Id, PushObj: pushObj}) + //err = PushObjMsg(v.PushObj, v.OrderName, v.OrderLevel) + if err != nil { + conf.Logger.Error("发送定时消息失败", zap.Error(err)) } + }) + } else { + var timingCustom request.TimingCustom + err = json.Unmarshal([]byte(v.TimingRule), &timingCustom) + if err != nil { + conf.Logger.Error("反序列化定时规则", zap.Error(err)) + continue } - } - } - fmt.Println("CronPersonalCardDate success!") -} - -type CronRedisWorkOrder struct { - PointTime int64 `json:"point_Time"` // 时间点 - PushObj string `json:"push_obj"` //推送对象 - OrderName string `json:"order_name"` // 工单名称 - OrderLevel int `json:"order_level"` // 工单等级(1紧急任务 2重要任务 3一般任务) -} - -func PushRedisWorkOrder(pointTime, pushObj, orderName string, orderLevel int) (err error) { - redis, err := client.GetRedisClient() - if err != nil { - return - } - timeUnix, err := time.Parse(jsontime.LocalTimeFormat, pointTime) - if err != nil { - return - } - cronRedisWorkOrder := CronRedisWorkOrder{ - PointTime: timeUnix.Unix(), - PushObj: pushObj, - OrderName: orderName, - OrderLevel: orderLevel, - } - workOrderObj, err := json.Marshal(cronRedisWorkOrder) - if err != nil { - return - } - //写redis - err = redis.LPush(conf.WorkOrderPush, fmt.Sprintf("%s", workOrderObj)) - if err != nil { - return - } - return -} - -// PushWorkOrderMessage 工单定时下发消息 -func PushWorkOrderMessage() { - go func() { - for { - redis, err := client.GetRedisClient() + dateFrom, err := time.Parse(jsontime.LocalDateFormat, timingCustom.DateFrom) if err != nil { - zap.L().Error(err.Error()) + conf.Logger.Error("时间类型转换错误", zap.Error(err)) continue } - workOrderList, err := redis.LRange(conf.WorkOrderPush) + dateTo, err := time.Parse(jsontime.LocalDateFormat, timingCustom.DateTo) if err != nil { - zap.L().Error(err.Error()) + conf.Logger.Error("时间类型转换错误", zap.Error(err)) + continue + } + t, err := time.Parse(jsontime.LocalDateFormat, timingCustom.PointTime) + if err != nil { + conf.Logger.Error("时间点类型转换错误", zap.Error(err)) continue } - for _, v := range workOrderList { - var cronRedisWorkOrder CronRedisWorkOrder - err = json.Unmarshal([]byte(v), &cronRedisWorkOrder) - if err != nil { - zap.L().Error(err.Error()) - continue - } - times, err1 := time.Parse(jsontime.LocalTimeFormat, time.Now().Format(jsontime.LocalTimeFormat)) - if err1 != nil { - zap.L().Error(err1.Error()) - return - } - if cronRedisWorkOrder.PointTime == times.Unix() { - var pushObj request.PushObj - err = json.Unmarshal([]byte(cronRedisWorkOrder.PushObj), &pushObj) - if err != nil { - zap.L().Error(err.Error()) - continue - } - var phones []string - for _, v1 := range pushObj.UserObj { - phones = append(phones, v1.Phone) - } - switch pushObj.PushMethod { - case 1: - //发送钉钉消息 - err = WorkOrderPushDingTalkMsg(cronRedisWorkOrder.OrderName, cronRedisWorkOrder.OrderLevel, phones) - if err != nil { - zap.L().Error(err.Error()) - } - case 2: - //发送短信 - err = WorkOrderPushNoteMsg(cronRedisWorkOrder.OrderName, phones, cronRedisWorkOrder.OrderLevel) - if err != nil { - zap.L().Error(err.Error()) - } - case 3: - //发送钉钉消息 - err = WorkOrderPushDingTalkMsg(cronRedisWorkOrder.OrderName, cronRedisWorkOrder.OrderLevel, phones) - if err != nil { - zap.L().Error(err.Error()) - } - //发送短信 - err = WorkOrderPushNoteMsg(cronRedisWorkOrder.OrderName, phones, cronRedisWorkOrder.OrderLevel) - if err != nil { - zap.L().Error(err.Error()) - } - } - //删除redis - err = redis.LRem(conf.WorkOrderPush, v) + for d := dateFrom; d.Before(dateTo); d = d.AddDate(0, 0, 1) { + expr := fmt.Sprintf("%d %d %d %d %d %d", t.Second(), t.Minute(), t.Hour(), d.Day(), d.Month(), d.Year()) + //创建定时任务 + workOrderCron.AddFunc(expr, func() { + err = svc.PushWorkOrderManage(request.PushWorkOrderReq{Id: v.Id, PushObj: pushObj}) + //err = PushObjMsg(v.PushObj, v.OrderName, v.OrderLevel) if err != nil { - zap.L().Error(err.Error()) + conf.Logger.Error("发送定时消息失败", zap.Error(err)) } - } + }) } } - }() + } } diff --git a/src/util/http.go b/src/util/http.go index 4a8a4569f2b629d7a9b2bef21b01929c5a0a6a75..f3ddc946d20a05b5b62e791c60b12d3d82b3a1dd 100644 --- a/src/util/http.go +++ b/src/util/http.go @@ -60,7 +60,7 @@ Request("https://httpbin.org/put", "Cookie": "aweToken=3ab9f63f-b0b3-4935-80ec-405d76ac111d", }) */ -func Request(url string, method string, body []byte, headers map[string]string) ([]byte, error) { +func Request(url string, method string, body []byte, headers map[string]string) (*fasthttp.Response, error) { req := fasthttp.AcquireRequest() defer fasthttp.ReleaseRequest(req) @@ -98,8 +98,11 @@ func Request(url string, method string, body []byte, headers map[string]string) return nil, err } + result := new(fasthttp.Response) + resp.CopyTo(result) + // 返回响应体和错误信息 - return resp.Body(), nil + return result, nil } // HttpSend , http请求 GET/DELETE/POST/PUT diff --git a/src/util/serialize.go b/src/util/serialize.go index 2e576ee665b8130fc6134ddfaf0b88982fd3acaf..271b8bbaa7f21bfa614dbe4253bbd0ae1a68b841 100644 --- a/src/util/serialize.go +++ b/src/util/serialize.go @@ -16,3 +16,14 @@ func ConvertToString(v interface{}) string { } return string(jsonData) } + +// IntsToStrings 将int切片转换为字符串切片 +func IntsToStrings(ints []int) []string { + strs := make([]string, len(ints)) + + for i, v := range ints { + strs[i] = fmt.Sprint(v) + } + + return strs +}