package main import ( "flag" "log" "strings" "git.mchus.pro/mchus/priceforge/internal/config" "git.mchus.pro/mchus/priceforge/internal/models" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) func main() { configPath := flag.String("config", "config.yaml", "path to config file") flag.Parse() cfg, err := config.Load(*configPath) if err != nil { log.Fatalf("Failed to load config: %v", err) } db, err := gorm.Open(mysql.Open(cfg.Database.DSN()), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } log.Println("Connected to database") // Ensure tables exist if err := models.Migrate(db); err != nil { log.Fatalf("Migration failed: %v", err) } if err := models.SeedCategories(db); err != nil { log.Fatalf("Seeding categories failed: %v", err) } // Load categories for lookup var categories []models.Category db.Find(&categories) categoryMap := make(map[string]uint) for _, c := range categories { categoryMap[c.Code] = c.ID } log.Printf("Loaded %d categories", len(categories)) // Get all lots var lots []models.Lot if err := db.Find(&lots).Error; err != nil { log.Fatalf("Failed to load lots: %v", err) } log.Printf("Found %d lots to import", len(lots)) // Import each lot var imported, skipped, updated int for _, lot := range lots { category, model := ParsePartNumber(lot.LotName) var categoryID *uint if id, ok := categoryMap[category]; ok && id > 0 { categoryID = &id } else { // Try to find by prefix match for code, id := range categoryMap { if strings.HasPrefix(category, code) { categoryID = &id break } } } // Check if already exists var existing models.LotMetadata result := db.Where("lot_name = ?", lot.LotName).First(&existing) if result.Error == gorm.ErrRecordNotFound { // Check if there are prices in the last 90 days var recentPriceCount int64 db.Model(&models.LotLog{}). Where("lot = ? AND date >= DATE_SUB(NOW(), INTERVAL 90 DAY)", lot.LotName). Count(&recentPriceCount) // Default to 90 days, but use "all time" (0) if no recent prices periodDays := 90 if recentPriceCount == 0 { periodDays = 0 } // Create new metadata := models.LotMetadata{ LotName: lot.LotName, CategoryID: categoryID, Model: model, PricePeriodDays: periodDays, } if err := db.Create(&metadata).Error; err != nil { log.Printf("Failed to create metadata for %s: %v", lot.LotName, err) continue } imported++ } else if result.Error == nil { // Update if needed needsUpdate := false if existing.Model == "" { existing.Model = model needsUpdate = true } if existing.CategoryID == nil { existing.CategoryID = categoryID needsUpdate = true } // Check if using default period (90 days) but no recent prices if existing.PricePeriodDays == 90 { var recentPriceCount int64 db.Model(&models.LotLog{}). Where("lot = ? AND date >= DATE_SUB(NOW(), INTERVAL 90 DAY)", lot.LotName). Count(&recentPriceCount) if recentPriceCount == 0 { existing.PricePeriodDays = 0 needsUpdate = true } } if needsUpdate { db.Save(&existing) updated++ } else { skipped++ } } } log.Printf("Import complete: %d imported, %d updated, %d skipped", imported, updated, skipped) // Show final counts var metadataCount int64 db.Model(&models.LotMetadata{}).Count(&metadataCount) log.Printf("Total metadata records: %d", metadataCount) } // ParsePartNumber extracts category and model from lot_name // Examples: // "CPU_AMD_9654" → category="CPU", model="AMD_9654" // "MB_INTEL_4.Sapphire_2S" → category="MB", model="INTEL_4.Sapphire_2S" func ParsePartNumber(lotName string) (category, model string) { parts := strings.SplitN(lotName, "_", 2) if len(parts) >= 1 { category = parts[0] } if len(parts) >= 2 { model = parts[1] } return }