package business import ( "fmt" "strings" "gorm.io/gorm" "kra/pkg/database/migration" ) // These source tables carry history. An incomplete copy must not be replaced // with empty tables. SDK and OA schemas belong to their respective databases. var importedTables = []string{ "tab_game", "tab_game_server", "tab_promote", "tab_promote_apply", "tab_spend", "tab_user", "tab_user_day_login", "tab_user_play_info", "tab_sw_report_day", "tab_sw_report_day_key", "tab_sw_report_server_day", "tab_sw_report_server_day_key", } func CheckPrerequisites(db *gorm.DB) error { if db.Dialector.Name() != "mysql" { return nil } if db.Migrator().HasTable("sys_user") && !db.Migrator().HasTable("sys_users") { return fmt.Errorf("legacy framework conversion must run before incremental migrations") } var missing []string for _, table := range importedTables { if !db.Migrator().HasTable(table) { missing = append(missing, table) } } if len(missing) > 0 { return fmt.Errorf("incomplete legacy copy, missing tables: %s", strings.Join(missing, ", ")) } return nil } func CheckSchema(db *gorm.DB) error { if err := CheckPrerequisites(db); err != nil { return err } if err := migration.CheckModels(db, &preferencePO{}, &bindingPO{}, &bindingLockPO{}, &resetCodePO{}, &resetTokenPO{}, &alertRulePO{}, &alertRecordPO{}, &alertLogPO{}, &alertGapPO{}, &alertExemptionPO{}, &alertChannelLockPO{}, &alertTriggerLockPO{}, &reportBuildLockPO{}, &businessGroupPO{}, &businessGroupMemberPO{}, &businessMemberProfilePO{}, &businessGroupStatePO{}, &performanceGoalPO{}, &effectivenessStatePO{}, &effectivenessPO{}, ); err != nil { return err } if db.Dialector.Name() != "mysql" { return nil } indexes := append([]businessQueryIndex{}, businessQueryIndexes...) var idType string if err := db.Raw("SELECT data_type FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tab_promote' AND column_name='id'").Scan(&idType).Error; err != nil { return err } if idType != "bigint" { return fmt.Errorf("tab_promote.id must be BIGINT after capacity migration") } indexes = append(indexes, reportBuildQueryIndexes...) indexes = append(indexes, alertV2QueryIndexes...) indexes = append(indexes, businessQueryIndex{"tab_spend", "idx_sw_spend_effective_scan", []string{"promote_id", "source_db", "pay_status", "pay_time", "id", "pay_amount"}}, businessQueryIndex{"tab_promote_apply", "idx_sw_apply_game_scope", []string{"game_id", "source_db", "status", "promote_id"}}, businessQueryIndex{"tab_user", "idx_sw_user_registration", []string{"source_db", "puid", "sw_register_time_unix", "id"}}, businessQueryIndex{"tab_user_play_info", "idx_sw_play_create_time", []string{"create_time"}}, businessQueryIndex{"sys_alert_record", "uk_alert_record_v2_open", []string{"business_id", "platform_code", "target_promote_id", "rule_type", "v2_open_key"}}, ) for _, index := range indexes { var existing []struct { ColumnName string NonUnique int SubPart *int } if err := db.Raw("SELECT column_name,non_unique,sub_part FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name=? AND index_name=? ORDER BY seq_in_index", index.table, index.name).Scan(&existing).Error; err != nil { return err } columns := []string{} for _, field := range existing { if field.SubPart != nil || index.name == "uk_alert_record_v2_open" && field.NonUnique != 0 { return fmt.Errorf("invalid index %s.%s", index.table, index.name) } columns = append(columns, field.ColumnName) } if strings.Join(columns, ",") != strings.Join(index.columns, ",") { return fmt.Errorf("missing or incompatible index %s.%s", index.table, index.name) } } return nil }