169 lines
7.0 KiB
Go
169 lines
7.0 KiB
Go
package business
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
"kra/internal/biz/business"
|
|
)
|
|
|
|
type alertTriggerRepo struct{ data Provider }
|
|
|
|
func NewAlertTriggerRepo(data Provider) business.AlertTriggerRepo {
|
|
return &alertTriggerRepo{data: data}
|
|
}
|
|
|
|
type alertTriggerLockPO struct {
|
|
SourceDB string `gorm:"column:source_db;primaryKey;size:50"`
|
|
PromoteID uint64 `gorm:"primaryKey;autoIncrement:false"`
|
|
RuleType string `gorm:"primaryKey;size:50"`
|
|
}
|
|
|
|
func (alertTriggerLockPO) TableName() string { return "sys_business_alert_locks" }
|
|
|
|
func (r *alertTriggerRepo) Input(ctx context.Context) (*business.AlertTriggerInput, error) {
|
|
db := r.data.DB().WithContext(ctx)
|
|
out := &business.AlertTriggerInput{}
|
|
var rules []alertRulePO
|
|
if err := db.Where("enabled=1 AND is_deleted=0").Order("id").Find(&rules).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range rules {
|
|
out.Rules = append(out.Rules, toBizAlertRule(row))
|
|
}
|
|
if len(rules) == 0 {
|
|
return out, nil
|
|
}
|
|
var accounts []accountPO
|
|
if err := db.Select("id,username,level,parent_id,id_path,authority_id").Find(&accounts).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range accounts {
|
|
out.Accounts = append(out.Accounts, toBizAccount(row))
|
|
}
|
|
var bindings []bindingPO
|
|
if err := db.Where("status=?", "0").Find(&bindings).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range bindings {
|
|
out.Bindings = append(out.Bindings, toBizBinding(row))
|
|
}
|
|
if len(bindings) == 0 {
|
|
return out, nil
|
|
}
|
|
available, err := businessTableAvailable(db, "tab_promote")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !available {
|
|
return nil, business.ErrAlertChannelsUnavailable
|
|
}
|
|
var channels []struct {
|
|
ID, BusierID uint64
|
|
SourceDB string `gorm:"column:source_db"`
|
|
Account string
|
|
LastLoginTime, CreateTime *int64
|
|
}
|
|
bound := db.Table("sys_user_bind_platform b").Select("1").Where("b.status=? AND b.source_db=p.source_db AND b.platform_busier_id=p.busier_id", "0")
|
|
if err := db.Table("tab_promote p").Select("p.id,p.source_db,p.account,p.busier_id,p.last_login_time,p.create_time").Where("p.promote_level=1 AND p.status=1").Where("EXISTS (?)", bound).Order("p.source_db,p.id").Scan(&channels).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range channels {
|
|
channel := &business.AlertTriggerChannel{ChannelKey: business.ChannelKey{SourceDB: row.SourceDB, ID: row.ID}, Account: row.Account, BusierID: row.BusierID}
|
|
if row.LastLoginTime != nil {
|
|
channel.LastLoginUnix = *row.LastLoginTime
|
|
}
|
|
if row.CreateTime != nil {
|
|
channel.CreatedUnix = *row.CreateTime
|
|
}
|
|
out.Channels = append(out.Channels, channel)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (r *alertTriggerRepo) Comparison(ctx context.Context, metric int, channels []business.ChannelKey, today time.Time, days int) (map[business.ChannelKey]business.AlertComparison, error) {
|
|
db := r.data.DB().WithContext(ctx)
|
|
table, stamp, amount, condition := "tab_user", "register_time", "1", "(puid=0 OR puid IS NULL)"
|
|
if metric == 2 {
|
|
table, stamp, amount, condition = "tab_spend", "pay_time", "pay_amount", "pay_status=1"
|
|
} else if metric != 1 {
|
|
return nil, business.ErrAlertRuleInput
|
|
}
|
|
available, err := businessTableAvailable(db, table)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !available {
|
|
return nil, business.ErrReportBuildUnavailable
|
|
}
|
|
result := map[business.ChannelKey]business.AlertComparison{}
|
|
currentStart, previousStart := today.AddDate(0, 0, -days).Unix(), today.AddDate(0, 0, -2*days).Unix()
|
|
for start := 0; start < len(channels); start += 500 {
|
|
var rows []struct {
|
|
SourceDB string `gorm:"column:source_db"`
|
|
PromoteID uint64
|
|
FirstUnix *int64
|
|
CurrentValue, PreviousValue string
|
|
}
|
|
// 原始主账号与成功支付口径,不扩展下级渠道,也不使用身份证偏好。
|
|
selection := "source_db,promote_id,MIN(" + stamp + ") AS first_unix," +
|
|
"COALESCE(SUM(CASE WHEN " + stamp + ">=? AND " + stamp + "<? THEN " + amount + " ELSE 0 END),0) AS current_value," +
|
|
"COALESCE(SUM(CASE WHEN " + stamp + ">=? AND " + stamp + "<? THEN " + amount + " ELSE 0 END),0) AS previous_value"
|
|
if err := db.Table(table).Select(selection, currentStart, today.Unix(), previousStart, currentStart).Where(condition).Where("(source_db,promote_id) IN ?", channelPairs(channels[start:min(start+500, len(channels))])).Group("source_db,promote_id").Scan(&rows).Error; err != nil {
|
|
return nil, business.ErrStorage
|
|
}
|
|
for _, row := range rows {
|
|
value := business.AlertComparison{Current: row.CurrentValue, Previous: row.PreviousValue}
|
|
if row.FirstUnix != nil {
|
|
value.FirstUnix = *row.FirstUnix
|
|
}
|
|
result[business.ChannelKey{SourceDB: row.SourceDB, ID: row.PromoteID}] = value
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *alertTriggerRepo) StoreHit(ctx context.Context, hit *business.AlertRecord, open []int, shouldWrite func(*business.AlertRecord) bool) (bool, error) {
|
|
written := false
|
|
err := r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
key := alertTriggerLockPO{SourceDB: hit.PlatformCode, PromoteID: hit.TargetPromoteID, RuleType: hit.RuleType}
|
|
if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&key).Error; err != nil {
|
|
return business.ErrStorage
|
|
}
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("source_db=? AND promote_id=? AND rule_type=?", key.SourceDB, key.PromoteID, key.RuleType).Take(&key).Error; err != nil {
|
|
return business.ErrStorage
|
|
}
|
|
var previous alertRecordPO
|
|
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("platform_code=? AND target_promote_id=? AND rule_type=? AND status IN ?", key.SourceDB, key.PromoteID, key.RuleType, open).Order("id").Take(&previous).Error
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return business.ErrStorage
|
|
}
|
|
var current *business.AlertRecord
|
|
if err == nil {
|
|
current = toBizAlertRecord(previous)
|
|
}
|
|
if !shouldWrite(current) {
|
|
return nil
|
|
}
|
|
day := time.Date(hit.CreatedAt.Year(), hit.CreatedAt.Month(), hit.CreatedAt.Day(), 0, 0, 0, 0, hit.CreatedAt.Location())
|
|
row := alertRecordPO{PlatformCode: hit.PlatformCode, PlatformName: hit.PlatformName, RuleID: hit.RuleID, RuleName: hit.RuleName, RuleType: hit.RuleType,
|
|
MonitorMetric: hit.MonitorMetric, TargetPromoteID: hit.TargetPromoteID, TargetPromoteName: hit.TargetPromoteName, BusinessID: hit.BusinessID, BusinessName: hit.BusinessName,
|
|
AlertContent: hit.AlertContent, Status: hit.Status, AlertDate: &day, CreatedAt: hit.CreatedAt, UpdatedAt: hit.UpdatedAt}
|
|
if current == nil {
|
|
err = tx.Create(&row).Error
|
|
} else {
|
|
row.ID = previous.ID
|
|
err = tx.Model(&alertRecordPO{}).Where("id=?", previous.ID).Select("platform_name", "rule_id", "rule_name", "rule_type", "monitor_metric", "target_promote_name", "business_id", "business_name", "alert_content", "status", "alert_date", "created_at", "updated_at").Updates(&row).Error
|
|
}
|
|
if err != nil {
|
|
return business.ErrStorage
|
|
}
|
|
written = true
|
|
return nil
|
|
})
|
|
return written, err
|
|
}
|