| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- // 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 <legacy userid>(默认 53691), --ddl <path>
- `)
- }
- // 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
- }
|