hjjjj c734c541b2
Some checks failed
CI / Unit tests (push) Has been cancelled
CI / commit_lint (push) Has been cancelled
feat: 添加视频URL支持及Zenmux集成
refactor: 重构Gemini适配器以支持多模态输入
fix: 修复React Hooks依赖警告
style: 清理未使用的导入和代码
docs: 更新用户界面文本和提示
perf: 优化图像和视频URL处理性能
test: 添加数据迁移工具和测试
build: 更新依赖项和.gitignore
chore: 同步Zenmux模型和价格比例
2026-03-12 17:53:27 +08:00

131 lines
3.1 KiB
Go

// migrate/main.go — SQLite → MySQL one-api data migration tool
// Usage: go run tools/migrate/main.go
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3"
)
const (
sqlitePath = "one-api.db"
mysqlDSN = "root:123456@tcp(localhost:3306)/oneapi?charset=utf8mb4&parseTime=True&loc=Local"
)
func main() {
if _, err := os.Stat(sqlitePath); os.IsNotExist(err) {
log.Fatalf("SQLite file not found: %s", sqlitePath)
}
sqlite, err := sql.Open("sqlite3", sqlitePath)
if err != nil {
log.Fatalf("open sqlite: %v", err)
}
defer sqlite.Close()
mysql, err := sql.Open("mysql", mysqlDSN)
if err != nil {
log.Fatalf("open mysql: %v", err)
}
defer mysql.Close()
if err = mysql.Ping(); err != nil {
log.Fatalf("mysql ping failed: %v\nCheck if MySQL is running and DSN is correct.", err)
}
fmt.Println("Connected to both databases. Starting migration...")
tables := []string{"users", "channels", "tokens", "options", "redemptions", "logs", "abilities"}
for _, table := range tables {
if err := migrateTable(sqlite, mysql, table); err != nil {
fmt.Printf("[WARN] table %s: %v\n", table, err)
}
}
fmt.Println("\nMigration complete!")
}
func migrateTable(src, dst *sql.DB, table string) error {
// Check table exists in SQLite
var count int
err := src.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'", table)).Scan(&count)
if err != nil || count == 0 {
fmt.Printf("[SKIP] table %s not found in SQLite\n", table)
return nil
}
// Get row count
var total int
_ = src.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM `%s`", table)).Scan(&total)
if total == 0 {
fmt.Printf("[SKIP] table %s is empty\n", table)
return nil
}
// Read all rows
rows, err := src.Query(fmt.Sprintf("SELECT * FROM `%s`", table))
if err != nil {
return fmt.Errorf("select from sqlite: %w", err)
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return err
}
// Build INSERT statement with placeholders
placeholders := ""
colNames := ""
for i, col := range cols {
if i > 0 {
placeholders += ","
colNames += ","
}
placeholders += "?"
colNames += fmt.Sprintf("`%s`", col)
}
insertSQL := fmt.Sprintf("INSERT IGNORE INTO `%s` (%s) VALUES (%s)", table, colNames, placeholders)
stmt, err := dst.Prepare(insertSQL)
if err != nil {
return fmt.Errorf("prepare insert for %s: %w", table, err)
}
defer stmt.Close()
inserted := 0
vals := make([]interface{}, len(cols))
valPtrs := make([]interface{}, len(cols))
for i := range vals {
valPtrs[i] = &vals[i]
}
for rows.Next() {
if err := rows.Scan(valPtrs...); err != nil {
return fmt.Errorf("scan: %w", err)
}
// Convert []byte to string for MySQL compatibility
args := make([]interface{}, len(vals))
for i, v := range vals {
if b, ok := v.([]byte); ok {
args[i] = string(b)
} else {
args[i] = v
}
}
if _, err := stmt.Exec(args...); err != nil {
fmt.Printf("[WARN] insert row in %s: %v\n", table, err)
continue
}
inserted++
}
fmt.Printf("[OK] %s: %d/%d rows migrated\n", table, inserted, total)
return nil
}