Files
mengyastore/mengyastore-backend-go/cmd/migrate/main.go
2026-05-13 12:11:46 +08:00

306 lines
9.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// migrate 将历史上导出的 JSON 数据文件导入 MySQL。
// 在切到数据库存储方式后执行一次即可:
//
// 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() {
// 从环境变量 / .env.development 加载(在仓库根目录执行 go run ./cmd/migrate或设置 ENV_FILE
cfg, err := config.Load()
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)
}
// 确保表结构存在
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("✅ 数据导入完成!")
}
// ─── 商品 ─────────────────────────────────────────────────────────────────────
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
}
// 卡密写入 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))
}
// ─── 订单 ─────────────────────────────────────────────────────────────────────
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))
}
// ─── 收藏夹 ───────────────────────────────────────────────────────────────────
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)
}
// ─── 聊天 ─────────────────────────────────────────────────────────────────────
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)
}
// ─── 站点设置 ─────────────────────────────────────────────────────────────────
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)
}