Commit de712f7c authored by 黄智's avatar 黄智

Merge remote-tracking branch 'origin/dev' into dev

parents 847da67f b4a16368
......@@ -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
......
......@@ -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=
......
......@@ -7,7 +7,7 @@ type AlertClass struct {
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:自定义
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'"` // 更新人
......
......@@ -21,24 +21,8 @@ type AlertArray struct {
TotalCount int `json:"total_count"` // 总预警数
}
type RiskLevelDistribution struct {
type AlertDistribution 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"`
}
......
package entity
import (
"github.com/prometheus/alertmanager/notify/webhook"
)
type Message webhook.Message
......@@ -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'"` // 更新人
......
......@@ -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启用
......
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"` // 实例工单数
//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"` // 定时规则
......
......@@ -15,6 +15,7 @@ type ListAlert struct {
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
......
......@@ -2,13 +2,14 @@ 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"`
ParentId int `json:"parent_id" form:"parent_id"`
SortOrder int `json:"sort_order" form:"sort_order"`
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 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"`
}
......
......@@ -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"` // 预警规则 字典值
......
......@@ -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"` // 预警规则 字典值
......
......@@ -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 {
......
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"` // 工单描述
......
......@@ -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"`
}
......@@ -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"`
}
......
......@@ -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 (
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
}
......@@ -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()))
......@@ -74,7 +80,6 @@ func initConfig() {
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
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())
// }
//}
}
......@@ -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 //自定义时间
)
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -10,12 +10,12 @@ 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.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)
}
......@@ -32,7 +32,6 @@ type AlertSvc struct {
}
var (
OpenSearchIndex = "so_alert"
Mapping = strings.NewReader(`{
"settings": {
"number_of_shards": 1,
......@@ -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"},
......
......@@ -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
}
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,
},
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"
}
}
},
CreatedBy: "admin",
"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: "admin",
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"
}
}
},
},
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{
"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{12, 20, 11, 50, 60, 30, 16, 6},
},
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)
}
......
......@@ -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"
......@@ -58,7 +59,7 @@ func (a *AlertRulesSvc) Add(req request.AddAlertRules) (err error) {
classParentId, err = alertClassSvc.Add(session, request.AddAlertClass{
ClassName: req.ClassParentName,
SortOrder: -1,
SourceFrom: 2,
Source: constant.SourceCustom,
})
if err != nil {
return nil, err
......@@ -67,18 +68,20 @@ func (a *AlertRulesSvc) Add(req request.AddAlertRules) (err error) {
ClassName: req.ClassName,
ParentId: classParentId,
SortOrder: -1,
SourceFrom: 2,
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}
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}
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}
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
}
_, err = db.NewSession().Where("id = ?", id).Delete(new(entity.AlertRules))
} else {
return errors.New("alert_rules_id already exists in opensearch")
}
......
......@@ -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,
......
......@@ -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点扫描当天需下发工单
}
......@@ -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
......
......@@ -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 {
......
......@@ -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)
......
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 {
rule := monitoringv1.Rule{
Alert: data.MetricConfigName,
For: &ruleFor,
Labels: map[string]string{
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),
"source": "so-operation-api",
"alert_rules_id": data.MetricConfigId,
},
"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{
// 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(),
},
}
......
......@@ -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
}
......
......@@ -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() {
db, err := client.GetDbClient()
if err != nil {
fmt.Println("CronStatusDetection err:", err.Error())
return
}
//查询推送工单
workOrderList := make([]entity.WorkOrder, 0)
finder := db.Table("work_order_manage").Where("timing_state = 1 AND (timing_type = 2 OR timing_type = 3)")
err = finder.Find(&workOrderList)
// PushObjMsg 解析用户并推送消息
func PushObjMsg(obj, orderName string, orderLevel int) (err error) {
var pushObj request.PushObj
err = json.Unmarshal([]byte(obj), &pushObj)
if err != nil {
fmt.Println("CronStatusDetection err:", err.Error())
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,
var phones []string
for _, v := range pushObj.UserObj {
phones = append(phones, v.Phone)
}
for _, v := range workOrderList {
if v.TimingType == 2 {
//按周
var timingWeekly request.TimingWeekly
err = json.Unmarshal([]byte(v.TimingRule), &timingWeekly)
switch pushObj.PushMethod {
case 1:
//发送钉钉消息
err = WorkOrderPushDingTalkMsg(orderName, orderLevel, phones)
if err != nil {
fmt.Println("CronStatusDetection err:", err.Error())
continue
return
}
days := week[time.Now().Weekday()]
for _, i := range timingWeekly.Week {
if i == days {
//写redis
err = PushRedisWorkOrder(timingWeekly.PointTime, v.PushObj, v.OrderName, v.OrderLevel)
case 2:
//发送短信
err = WorkOrderPushNoteMsg(orderName, phones, orderLevel)
if err != nil {
fmt.Println("CronStatusDetection redis set err:", err.Error())
continue
}
break
}
return
}
} else if v.TimingType == 3 {
//自定义时间
var timingCustom []request.TimingCustom
err = json.Unmarshal([]byte(v.TimingRule), &timingCustom)
case 3:
//发送钉钉消息
err = WorkOrderPushDingTalkMsg(orderName, orderLevel, phones)
if err != nil {
fmt.Println("CronStatusDetection json Unmarshal err:", err.Error())
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
return
}
if t.After(dateFrom) && t.Before(dateTo) {
//写redis
err = PushRedisWorkOrder(v1.PointTime, v.PushObj, v.OrderName, v.OrderLevel)
//发送短信
err = WorkOrderPushNoteMsg(orderName, phones, orderLevel)
if err != nil {
fmt.Println("CronStatusDetection redis set err:", err.Error())
continue
}
break
}
}
return
}
}
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一般任务)
conf.Logger.Info("定时下发工单完成,工单名称:" + orderName)
return
}
func PushRedisWorkOrder(pointTime, pushObj, orderName string, orderLevel int) (err error) {
redis, err := client.GetRedisClient()
// CronPushWorkOrder 创建工单下发定时任务
func CronPushWorkOrder() {
db, err := client.GetDbClient()
if err != nil {
conf.Logger.Error("获取数据库连接", zap.Error(err))
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)
//查询推送工单
workOrderList := make([]entity.WorkOrder, 0)
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 {
conf.Logger.Error("查询推送工单", zap.Error(err))
return
}
//写redis
err = redis.LPush(conf.WorkOrderPush, fmt.Sprintf("%s", workOrderObj))
workOrderCron.Stop()
defer workOrderCron.Start()
for _, v := range workOrderList {
var pushObj request.PushObj
err = json.Unmarshal([]byte(v.PushObj), &pushObj)
if err != nil {
return
}
return
}
svc := WorkOrderManageSvc{User: entity.SystemUserInfo{SystemAccount: v.CreateUser}}
// PushWorkOrderMessage 工单定时下发消息
func PushWorkOrderMessage() {
go func() {
for {
redis, err := client.GetRedisClient()
if err != nil {
zap.L().Error(err.Error())
continue
}
workOrderList, err := redis.LRange(conf.WorkOrderPush)
if v.TimingType == constant.TimingWeekly {
var timingWeekly request.TimingWeekly
err = json.Unmarshal([]byte(v.TimingRule), &timingWeekly)
if err != nil {
zap.L().Error(err.Error())
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
for k, v1 := range timingWeekly.Week {
if v1 == 7 {
timingWeekly.Week[k] = 0
}
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)
t, err := time.Parse(jsontime.LocalTimeFormat, timingWeekly.PointTime)
if err != nil {
zap.L().Error(err.Error())
conf.Logger.Error("时间点类型转换错误", zap.Error(err))
continue
}
var phones []string
for _, v1 := range pushObj.UserObj {
phones = append(phones, v1.Phone)
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))
}
switch pushObj.PushMethod {
case 1:
//发送钉钉消息
err = WorkOrderPushDingTalkMsg(cronRedisWorkOrder.OrderName, cronRedisWorkOrder.OrderLevel, phones)
})
} else {
var timingCustom request.TimingCustom
err = json.Unmarshal([]byte(v.TimingRule), &timingCustom)
if err != nil {
zap.L().Error(err.Error())
conf.Logger.Error("反序列化定时规则", zap.Error(err))
continue
}
case 2:
//发送短信
err = WorkOrderPushNoteMsg(cronRedisWorkOrder.OrderName, phones, cronRedisWorkOrder.OrderLevel)
dateFrom, err := time.Parse(jsontime.LocalDateFormat, timingCustom.DateFrom)
if err != nil {
zap.L().Error(err.Error())
conf.Logger.Error("时间类型转换错误", zap.Error(err))
continue
}
case 3:
//发送钉钉消息
err = WorkOrderPushDingTalkMsg(cronRedisWorkOrder.OrderName, cronRedisWorkOrder.OrderLevel, phones)
dateTo, err := time.Parse(jsontime.LocalDateFormat, timingCustom.DateTo)
if err != nil {
zap.L().Error(err.Error())
conf.Logger.Error("时间类型转换错误", zap.Error(err))
continue
}
//发送短信
err = WorkOrderPushNoteMsg(cronRedisWorkOrder.OrderName, phones, cronRedisWorkOrder.OrderLevel)
t, err := time.Parse(jsontime.LocalDateFormat, timingCustom.PointTime)
if err != nil {
zap.L().Error(err.Error())
conf.Logger.Error("时间点类型转换错误", zap.Error(err))
continue
}
}
//删除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))
}
})
}
}
}
}()
}
......@@ -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
......
......@@ -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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment