// Command ac15fixture 是 AC-15 阶段 2 前置件的 CLI 入口。 // // 用法(在仓库根目录执行): // // go run ./cmd/ac15fixture ddl # 用 core-model-ddl.sql 补齐目标库结构(只 CREATE,不 DROP) // go run ./cmd/ac15fixture count # 打印 5 张相关表当前行数 // go run ./cmd/ac15fixture map --user 53691 # 抽样搬运(不改任何已有 target 业务代码) // go run ./cmd/ac15fixture verify --user 53691 # 逐列核对(退出码 0=PASS,1=FAIL) // go run ./cmd/ac15fixture run --user 53691 # map + verify // go run ./cmd/ac15fixture clean --user 53691 # 删除 fixture 数据并复核回 0 行 // // 环境变量(口令不落盘、不打印): // // AC15_DB_HOST / AC15_DB_PORT / AC15_DB_USER / AC15_DB_PASS / AC15_TARGET_DB // AC15_LEGACY_USER / AC15_LEGACY_PASS / AC15_LEGACY_DB // AC15_CORE_DDL core DDL 路径(默认取仓库外部的 02-data-model/core-model-ddl.sql) // // 退出码:0 = 成功/PASS;1 = 核对 FAIL;2 = 参数错误;3 = 环境或守卫失败。 package main import ( "context" "flag" "fmt" "os" "yuansiwei/unified-backend/internal/academic/dualrun/fixture" ) func main() { if len(os.Args) < 2 { usage() os.Exit(2) } cmd := os.Args[1] args := os.Args[2:] switch cmd { case "ddl": os.Exit(runDDL(args)) case "map": os.Exit(runMap(args)) case "verify": os.Exit(runVerify(args)) case "run": os.Exit(runAll(args)) case "clean": os.Exit(runClean(args)) case "count": os.Exit(runCount(args)) case "-h", "--help", "help": usage() os.Exit(0) default: fmt.Fprintf(os.Stderr, "未知子命令:%s\n\n", cmd) usage() os.Exit(2) } } func usage() { fmt.Fprint(os.Stderr, `ac15fixture —— AC-15 阶段 2 前置件(资金域 4 表映射 + 逐列核对) 子命令: ddl 补齐 core-model-ddl.sql 的 13 张表(只 CREATE TABLE IF NOT EXISTS,禁止 DROP) map 抽样搬运一个 legacy 用户(账户 + 校区账户 + 流水) verify 逐列核对(4 张表 + AUTO_INCREMENT + 空值语义 + float 陷阱) run map + verify clean 删除 fixture 数据并复核 4 张表回 0 行 count 打印相关表当前行数 通用参数:--user (默认 53691), --ddl `) } // bootstrap 解析公共参数、加载配置并跑守卫。 func bootstrap(args []string, withUser bool) (fixture.Config, int64, string, int) { fs := flag.NewFlagSet("ac15fixture", flag.ContinueOnError) fs.SetOutput(os.Stderr) user := fs.Int64("user", fixture.DefaultLegacyUserID, "legacy userid") ddl := fs.String("ddl", "", "core DDL 路径(默认 AC15_CORE_DDL 或仓库外部默认路径)") if err := fs.Parse(args); err != nil { return fixture.Config{}, 0, "", 2 } if !withUser { *user = fixture.DefaultLegacyUserID } repoRoot, err := os.Getwd() if err != nil { fmt.Fprintf(os.Stderr, "FATAL: 取工作目录失败:%v\n", err) return fixture.Config{}, 0, "", 3 } cfg, err := fixture.LoadConfig(repoRoot) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) return fixture.Config{}, 0, "", 3 } if err := cfg.Guard(); err != nil { fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) return fixture.Config{}, 0, "", 3 } fmt.Printf("target=%s legacy=%s\n\n", cfg.TargetMasked(), cfg.LegacyMasked()) return cfg, *user, *ddl, 0 } func runDDL(args []string) int { cfg, _, ddlPath, code := bootstrap(args, false) if code != 0 { return code } rep, err := fixture.ApplyCoreDDL(context.Background(), cfg, ddlPath) if rep != nil { fixture.PrintDDLReport(os.Stdout, rep) } if err != nil { fmt.Fprintf(os.Stderr, "\nFATAL: %v\n", err) return 3 } return 0 } func runMap(args []string) int { cfg, user, _, code := bootstrap(args, true) if code != 0 { return code } rep, err := fixture.PrepareAndLoad(context.Background(), cfg, user) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) return 3 } fixture.PrintMapReport(os.Stdout, rep) return 0 } func runVerify(args []string) int { cfg, user, _, code := bootstrap(args, true) if code != 0 { return code } rep, err := fixture.Verify(context.Background(), cfg, user) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) return 3 } fixture.PrintVerifyReport(os.Stdout, rep) if !rep.OK() { return 1 } return 0 } func runAll(args []string) int { if code := runMap(args); code != 0 { return code } fmt.Println() return runVerify(args) } func runClean(args []string) int { cfg, user, _, code := bootstrap(args, true) if code != 0 { return code } rep, err := fixture.Cleanup(context.Background(), cfg, user) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) return 3 } fixture.PrintCleanReport(os.Stdout, rep) return 0 } func runCount(args []string) int { cfg, _, _, code := bootstrap(args, false) if code != 0 { return code } counts, err := fixture.CountTargetTables(context.Background(), cfg) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: %v\n", err) return 3 } fmt.Println("== 目标表行数 ==") for _, t := range []string{"t_user", "t_legacy_mapping", "t_user_expense", "t_user_expense_school", "t_user_expense_log"} { fmt.Printf(" %-24s %d\n", t, counts[t]) } return 0 }