Files

129 lines
3.4 KiB
Go
Raw Permalink Normal View History

2026-08-14 13:17:03 +08:00
// dbtool 数据库初始化工具:读取 sql/init.sql 并逐条执行
// 用法(在 nl-game-api-gin 目录下go run ./cmd/dbtool -f ../sql/init.sql
// 连接参数复用 config.yaml 中的 mysql 配置(不指定库名,脚本自带 CREATE DATABASE
package main
import (
"database/sql"
"flag"
"fmt"
"log"
"os"
"strings"
_ "github.com/go-sql-driver/mysql"
"nl-game-api-gin/internal/config"
)
func main() {
file := flag.String("f", "../sql/init.sql", "SQL 脚本路径")
conf := flag.String("c", "config.yaml", "配置文件路径")
flag.Parse()
if err := config.Load(*conf); err != nil {
log.Fatalln("配置加载失败:", err)
}
raw, err := os.ReadFile(*file)
if err != nil {
log.Fatalln("读取 SQL 文件失败:", err)
}
// 服务器级 DSN不带库名multiStatements 关闭、逐条执行便于定位错误
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8mb4&parseTime=false",
config.C.MySQL.User, config.C.MySQL.Password, config.C.MySQL.Host, config.C.MySQL.Port)
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatalln("连接 MySQL 失败:", err)
}
defer db.Close()
// 限制单连接:保证 USE xiaoyouxi 对后续语句生效
db.SetMaxOpenConns(1)
if err := db.Ping(); err != nil {
log.Fatalln("MySQL 无法连通,请确认服务已启动且账号密码正确:", err)
}
stmts := splitSQL(string(raw))
log.Printf("共解析出 %d 条语句,开始执行…\n", len(stmts))
for i, s := range stmts {
if _, err := db.Exec(s); err != nil {
head := s
if len(head) > 80 {
head = head[:80] + "…"
}
log.Fatalf("第 %d 条语句执行失败: %v\n语句开头: %s", i+1, err, head)
}
}
log.Println("✅ 数据库初始化完成(库名 xiaoyouxi含种子数据与超管 liqi")
}
// splitSQL 把脚本按分号拆成独立语句:跟踪引号与注释状态,避免误切字符串内的分号
func splitSQL(text string) []string {
var stmts []string
var sb strings.Builder
inSingle, inDouble, inBacktick, inLineComment := false, false, false, false
runes := []rune(text)
for i := 0; i < len(runes); i++ {
ch := runes[i]
// 行注释:遇到换行结束(注释内容不写入语句)
if inLineComment {
if ch == '\n' {
inLineComment = false
}
continue
}
// 检测行注释起始(不在任何引号内)
if !inSingle && !inDouble && !inBacktick && ch == '-' && i+1 < len(runes) && runes[i+1] == '-' {
inLineComment = true
i++
continue
}
// 引号状态翻转MySQL 字符串内以 \' 转义,需要跳过转义符)
switch {
case inSingle:
if ch == '\\' && i+1 < len(runes) {
sb.WriteRune(ch)
i++
sb.WriteRune(runes[i])
continue
}
if ch == '\'' {
inSingle = false
}
case inDouble:
if ch == '\\' && i+1 < len(runes) {
sb.WriteRune(ch)
i++
sb.WriteRune(runes[i])
continue
}
if ch == '"' {
inDouble = false
}
case inBacktick:
if ch == '`' {
inBacktick = false
}
default:
if ch == '\'' {
inSingle = true
} else if ch == '"' {
inDouble = true
} else if ch == '`' {
inBacktick = true
} else if ch == ';' {
// 语句结束
stmt := strings.TrimSpace(sb.String())
if stmt != "" {
stmts = append(stmts, stmt)
}
sb.Reset()
continue
}
}
sb.WriteRune(ch)
}
// 收尾:最后一条可能没有分号
if tail := strings.TrimSpace(sb.String()); tail != "" {
stmts = append(stmts, tail)
}
return stmts
}