// migrate imports existing JSON data files into the MySQL database. // Run once after switching to DB storage: // // go run ./cmd/migrate/main.go package main import ( "encoding/json" "log" "os" "strconv" "time" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/logger" "mengyastore-backend/internal/config" "mengyastore-backend/internal/database" ) func main() { cfg, err := config.Load("data/json/settings.json") if err != nil { log.Fatalf("load config: %v", err) } db, err := gorm.Open(mysql.Open(cfg.DatabaseDSN), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) if err != nil { log.Fatalf("open db: %v", err) } // Ensure tables exist if err := db.AutoMigrate( &database.ProductRow{}, &database.ProductCodeRow{}, &database.OrderRow{}, &database.SiteSettingRow{}, &database.WishlistRow{}, &database.ChatMessageRow{}, ); err != nil { log.Fatalf("auto migrate: %v", err) } log.Println("数据库连接成功,开始导入...") migrateProducts(db) migrateOrders(db) migrateWishlists(db) migrateChats(db) migrateSite(db) log.Println("✅ 数据导入完成!") } // ─── Products ───────────────────────────────────────────────────────────────── type jsonProduct struct { ID string `json:"id"` Name string `json:"name"` Price float64 `json:"price"` DiscountPrice float64 `json:"discountPrice"` Tags []string `json:"tags"` CoverURL string `json:"coverUrl"` ScreenshotURLs []string `json:"screenshotUrls"` Description string `json:"description"` Active bool `json:"active"` RequireLogin bool `json:"requireLogin"` MaxPerAccount int `json:"maxPerAccount"` TotalSold int `json:"totalSold"` ViewCount int `json:"viewCount"` DeliveryMode string `json:"deliveryMode"` ShowNote bool `json:"showNote"` ShowContact bool `json:"showContact"` Codes []string `json:"codes"` CreatedAt time.Time `json:"createdAt"` } func migrateProducts(db *gorm.DB) { data, err := os.ReadFile("data/json/products.json") if err != nil { log.Printf("[products] 文件不存在,跳过: %v", err) return } var products []jsonProduct if err := json.Unmarshal(data, &products); err != nil { log.Printf("[products] JSON 解析失败: %v", err) return } for _, p := range products { if p.ID == "" { continue } if p.DeliveryMode == "" { p.DeliveryMode = "auto" } row := database.ProductRow{ ID: p.ID, Name: p.Name, Price: p.Price, DiscountPrice: p.DiscountPrice, Tags: database.StringSlice(p.Tags), CoverURL: p.CoverURL, ScreenshotURLs: database.StringSlice(p.ScreenshotURLs), Description: p.Description, Active: p.Active, RequireLogin: p.RequireLogin, MaxPerAccount: p.MaxPerAccount, TotalSold: p.TotalSold, ViewCount: p.ViewCount, DeliveryMode: p.DeliveryMode, ShowNote: p.ShowNote, ShowContact: p.ShowContact, CreatedAt: p.CreatedAt, } if row.CreatedAt.IsZero() { row.CreatedAt = time.Now() } result := db.Clauses(clause.OnConflict{DoNothing: true}).Create(&row) if result.Error != nil { log.Printf("[products] 导入 %s 失败: %v", p.ID, result.Error) continue } // Codes → product_codes for _, code := range p.Codes { if code == "" { continue } db.Clauses(clause.OnConflict{DoNothing: true}).Create(&database.ProductCodeRow{ ProductID: p.ID, Code: code, }) } } log.Printf("[products] 导入 %d 条商品", len(products)) } // ─── Orders ─────────────────────────────────────────────────────────────────── type jsonOrder struct { ID string `json:"id"` ProductID string `json:"productId"` ProductName string `json:"productName"` UserAccount string `json:"userAccount"` UserName string `json:"userName"` Quantity int `json:"quantity"` DeliveredCodes []string `json:"deliveredCodes"` Status string `json:"status"` DeliveryMode string `json:"deliveryMode"` Note string `json:"note"` ContactPhone string `json:"contactPhone"` ContactEmail string `json:"contactEmail"` CreatedAt time.Time `json:"createdAt"` } func migrateOrders(db *gorm.DB) { data, err := os.ReadFile("data/json/orders.json") if err != nil { log.Printf("[orders] 文件不存在,跳过: %v", err) return } var orders []jsonOrder if err := json.Unmarshal(data, &orders); err != nil { log.Printf("[orders] JSON 解析失败: %v", err) return } for _, o := range orders { if o.ID == "" { continue } if o.DeliveryMode == "" { o.DeliveryMode = "auto" } if o.DeliveredCodes == nil { o.DeliveredCodes = []string{} } row := database.OrderRow{ ID: o.ID, ProductID: o.ProductID, ProductName: o.ProductName, UserAccount: o.UserAccount, UserName: o.UserName, Quantity: o.Quantity, DeliveredCodes: database.StringSlice(o.DeliveredCodes), Status: o.Status, DeliveryMode: o.DeliveryMode, Note: o.Note, ContactPhone: o.ContactPhone, ContactEmail: o.ContactEmail, CreatedAt: o.CreatedAt, } if row.CreatedAt.IsZero() { row.CreatedAt = time.Now() } if result := db.Clauses(clause.OnConflict{DoNothing: true}).Create(&row); result.Error != nil { log.Printf("[orders] 导入 %s 失败: %v", o.ID, result.Error) } } log.Printf("[orders] 导入 %d 条订单", len(orders)) } // ─── Wishlists ──────────────────────────────────────────────────────────────── func migrateWishlists(db *gorm.DB) { data, err := os.ReadFile("data/json/wishlists.json") if err != nil { log.Printf("[wishlists] 文件不存在,跳过: %v", err) return } var wl map[string][]string if err := json.Unmarshal(data, &wl); err != nil { log.Printf("[wishlists] JSON 解析失败: %v", err) return } count := 0 for account, productIDs := range wl { for _, pid := range productIDs { db.Clauses(clause.OnConflict{DoNothing: true}).Create(&database.WishlistRow{ AccountID: account, ProductID: pid, }) count++ } } log.Printf("[wishlists] 导入 %d 条收藏记录", count) } // ─── Chats ──────────────────────────────────────────────────────────────────── type jsonChatMsg struct { ID string `json:"id"` AccountID string `json:"accountId"` AccountName string `json:"accountName"` Content string `json:"content"` SentAt time.Time `json:"sentAt"` FromAdmin bool `json:"fromAdmin"` } func migrateChats(db *gorm.DB) { data, err := os.ReadFile("data/json/chats.json") if err != nil { log.Printf("[chats] 文件不存在,跳过: %v", err) return } var convs map[string][]jsonChatMsg if err := json.Unmarshal(data, &convs); err != nil { log.Printf("[chats] JSON 解析失败: %v", err) return } count := 0 for _, msgs := range convs { for _, m := range msgs { if m.ID == "" { continue } db.Clauses(clause.OnConflict{DoNothing: true}).Create(&database.ChatMessageRow{ ID: m.ID, AccountID: m.AccountID, AccountName: m.AccountName, Content: m.Content, SentAt: m.SentAt, FromAdmin: m.FromAdmin, }) count++ } } log.Printf("[chats] 导入 %d 条聊天消息", count) } // ─── Site settings ──────────────────────────────────────────────────────────── type jsonSite struct { TotalVisits int `json:"totalVisits"` Maintenance bool `json:"maintenance"` MaintenanceReason string `json:"maintenanceReason"` } func migrateSite(db *gorm.DB) { data, err := os.ReadFile("data/json/site.json") if err != nil { log.Printf("[site] 文件不存在,跳过: %v", err) return } var site jsonSite if err := json.Unmarshal(data, &site); err != nil { log.Printf("[site] JSON 解析失败: %v", err) return } upsert := func(key, value string) { db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "key"}}, DoUpdates: clause.AssignmentColumns([]string{"value"}), }).Create(&database.SiteSettingRow{Key: key, Value: value}) } upsert("totalVisits", strconv.Itoa(site.TotalVisits)) maintenance := "false" if site.Maintenance { maintenance = "true" } upsert("maintenance", maintenance) upsert("maintenanceReason", site.MaintenanceReason) log.Printf("[site] 站点设置导入完成(访问量: %d)", site.TotalVisits) }