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 + "=? AND " + stamp + "