// applysql —— 开发辅助工具:把 SQL 文件应用到 z_xk 库 // // 为什么需要它:Windows 开发机没有 mysql 客户端,而 Go 项目本身带着 // GORM 依赖与 config.Load() 的 DSN 解析逻辑,直接复用即可执行 DDL, // 且凭据不经过命令行/控制台(从 manifest/config/config.yaml 读取)。 // // 用法(必须在 nl-tcm-agent 项目根目录执行,保证能读到配置文件): // go run ./tools/applysql [sql文件2 ...] // // 说明: // - 按 ";\n" 粗粒度拆分语句(我们自己的 SQL 文件都是规范的分号+换行结尾) // - "--" 开头的注释行会被剥掉 // - 任一语句失败立即退出(DDL 顺序有依赖,跳过会导致后续更混乱) package main import ( "fmt" "log" "os" "strings" "tcm-agent/internal/config" "tcm-agent/internal/dao" ) func main() { if len(os.Args) < 2 { log.Fatal("用法: go run ./tools/applysql ") } cfg := config.Load() if cfg.DB.DSN == "" { log.Fatal("配置缺少 db.dsn(检查 manifest/config/config.yaml)") } if err := dao.Init(cfg.DB.DSN); err != nil { log.Fatalf("连接数据库失败: %v", err) } for _, path := range os.Args[1:] { raw, err := os.ReadFile(path) if err != nil { log.Fatalf("读取 %s 失败: %v", path, err) } stmts := splitStatements(string(raw)) fmt.Printf("== %s:%d 条语句 ==\n", path, len(stmts)) for i, stmt := range stmts { if err := dao.DB.Exec(stmt).Error; err != nil { log.Fatalf("第 %d 条语句执行失败: %v\n语句片段: %.120s", i+1, err, stmt) } fmt.Printf(" [%d/%d] OK %.60s...\n", i+1, len(stmts), firstLine(stmt)) } } fmt.Println("全部执行完成") } // splitStatements 把 SQL 文本拆成可执行语句:剥注释行后按分号结尾切分 func splitStatements(sqlText string) []string { // Windows 编辑器写出的文件是 CRLF,先统一成 LF,否则 ";\n" 切分匹配不到 sqlText = strings.ReplaceAll(sqlText, "\r\n", "\n") var sb strings.Builder for _, line := range strings.Split(sqlText, "\n") { trimmed := strings.TrimSpace(line) // 剥掉整行注释(字段内注释在行中不受影响) if strings.HasPrefix(trimmed, "--") || trimmed == "" { continue } sb.WriteString(line) sb.WriteString("\n") } parts := strings.Split(sb.String(), ";\n") out := make([]string, 0, len(parts)) for _, p := range parts { p = strings.TrimSpace(p) if p != "" { out = append(out, p) } } return out } // firstLine 取语句首行用于进度展示 func firstLine(stmt string) string { if i := strings.Index(stmt, "\n"); i > 0 { return stmt[:i] } return stmt }