SKILL.md 10.4 KB

name: db-init description: A4 DB 初始化——LLM 解析 docs/03-数据库设计文档.md → 生成 sql/migrations/V1__initial_schema.sql(DDL only,Flyway 初始 migration)→ 全量校验 DDL ↔ docs/03 一致性 → 验证 MySQL 连接 → 调 scripts/setup-test-db.sh 复用三层防护并 DROP+CREATE 空库 → apply V1。 user-invocable: false

allowed-tools: Read Write Edit Glob Skill Bash(mkdir *) Bash(mysql *) Bash(set *) Bash(. .env.local) Bash(sed *) Bash(cat *) Bash(grep *) Bash(./scripts/setup-test-db.sh)

所有输出必须使用中文。

db-init

前置条件

  • A3 db-design-gen 已完成:docs/03-数据库设计文档.md 已生成,用户已审阅,docs/08 § 一 A3 全部 checkbox 均 [x]
  • A2 skeleton-gen 已完成:.env.local 已存在(含 DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_SCHEMA)。

执行步骤

步骤 0:打印当前位置流程图

向用户展示当前在 A 阶段流程中的位置(A-only, 标在 A4):

┌──────────────────────────────────────────────────────┐
│ 📋 阶段 A:规划(一次性)                               │
│                                                       │
│   A0 初始化项目 → A1 锁范围(REQ 卡片)                 │
│                          ↓                            │
│     ⏸ 等你审阅 REQ,重新运行 /plan-start 继续      │
│                          ↓                            │
│   A2 生成骨架 → A3 生成 DB 设计 → ▶ A4 初始化 DB → A5 生成下游文档│
│                          ↓                            │
│            规划阶段最后一步:A5 由本 skill 末尾自动调起    │
└──────────────────────────────────────────────────────┘

A. DDL 生成(不依赖数据库连接)

A.1 读 docs/03 并 LLM 翻译为 DDL

Read 读取 docs/03-数据库设计文档.md,按字段 / 索引 / 外键 / 业务注记严格翻译为:

  • 每张表一段 CREATE TABLE \` (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='<业务用途>';`
  • 字段顺序与 docs/03 表格行序一致;Nullable 列直接映射 NOT NULL / NULL默认 列映射 DEFAULT <value>;列尾用 COMMENT '<业务含义>' 写注释
  • 索引:在表创建后追加 CREATE [UNIQUE] INDEX \` ON `` ();`(主键 / 唯一约束已在 CREATE TABLE 内联的不重复声明)
  • 外键:在所有表创建完成后统一追加ALTER TABLE \` ADD CONSTRAINT `` FOREIGN KEY (``) REFERENCES `` (``) ON DELETE ;`(避免顺序问题)
  • 要求:

    • 严禁臆造 docs/03 中没有的表 / 字段 / 索引 / 外键
    • 严禁省略 docs/03 中已有的列、注释或约束
    • 字符集统一 utf8mb4 + utf8mb4_unicode_ci,引擎统一 InnoDB,除非 docs/03 业务注记明确要求其他设置

    A.2 拼装 V1 文件

    Bash 拼装最终文件:

    mkdir -p sql/migrations
    PROJECT="<从 CLAUDE.md 读到的项目名称>"
    TS="$(date -u +%FT%TZ)"
    set -a; . .env.local; set +a
    {
      sed -e "s|{{project_name}}|$PROJECT|g" \
          -e "s|{{timestamp}}|$TS|g" \
          -e "s|{{schema_name}}|$DB_SCHEMA|g" \
          "${CLAUDE_SKILL_DIR}/templates/migration-v1-header-template.sql"
      echo ""
      cat <<'DDL_EOF'
    <把 A.1 推导出的 CREATE TABLE / CREATE INDEX / ALTER ADD FK 全部按顺序粘贴在此>
    DDL_EOF
    } > sql/migrations/V1__initial_schema.sql
    

    A.3 全量校验 DDL ↔ docs/03 一致性

    5 维度全量校验。任一不一致 → 报错停下,列出全部差异明细;V1.sql 保留以便人工 diff。

    1. 表名集合相等

      • S_doc = grep ^## \`` of docs/03
      • S_sql = grep CREATE TABLE \`` of V1
      • S_doc - S_sql 非空(漏表)/ S_sql - S_doc 非空(多表)→ 报错列出差集
    2. 每张表全列名 + 列序相等遍历 S_sql 全部表,无抽样

      • 对每张表 t:
        • 从 docs/03 抽该表 ### 字段 表格行序,得 cols_doc[t]
        • 从 V1 抽该 CREATE TABLE 段内 \`出现序,得cols_sql[t]`
      • 数组相等(含顺序);不等则列出 <table>: docs=[...], sql=[...]
    3. 每个字段类型 / Nullable / 默认值一致

      • 对每个 <table>.<col>,归一化后字符串比对。归一化规则:
        • 类型主词转小写(BIGINTbigint
        • UNSIGNED 保留为小写
        • 显示宽度 INT(11)int(去括号)
        • Nullable:docs/03 ↔ DDL NOT NULL;docs/03 ↔ DDL 无 NOT NULL
        • 默认值:去多余空格 + 统一大小写
      • COMMENT / COLLATE / CHARSET 不参与比对(避免装饰差异误报)
      • 不一致 → 列出 <table>.<col>: docs=<X>, ddl=<Y>
    4. 每张表索引名集合相等

      • idx_doc[t] = docs/03 该表 ### 索引 列表的名字
      • idx_sql[t] = V1 中归属该表的 CREATE INDEX <name> + inline KEY \` / UNIQUE KEY ``(排除PRIMARY` 自动名)
      • 集合不等 → 报错列出差集
    5. 外键名集合相等(全局,不分表)

      • fk_doc = docs/03 各表 ### 外键 列表的所有名字
      • fk_sql = V1 中所有 ADD CONSTRAINT \` FOREIGN KEY` 的名字
      • 集合不等 → 报错列出差集

    校验全部通过 → 进入步骤 B。任一维度不通过都不许继续;提示用户 diff docs/03V1.sql 后修正 docs/03(SSoT),重跑 A3 让 db-design-gen / 本 skill 重新生成 V1。

    B. 数据库环境检查

    B.1 检查 .env.local 凭据

    Glob 检查 .env.local 是否存在;不存在 → 提示用户重新运行 A2 skeleton-gen 重建并停下。

    Bash 加载并校验 5 个必填字段非空:

    set -a; . .env.local; set +a
    for v in DB_HOST DB_PORT DB_USER DB_PASSWORD DB_SCHEMA; do
      eval val=\${$v:-}
      [ -z "$val" ] && echo "MISSING: $v"
    done
    

    任一缺失 → 打印缺失字段名并停下,提示用户编辑 .env.local 后重跑。

    注:密码中含 $`、空格、! 等字符时,.env.local 需用单引号包裹,例如 DB_PASSWORD='p@ss$w0rd!'set -a; . file; set +asource 行为一致但更明确,配合单引号可避免特殊字符被 shell 展开。

    B.2 验证 MySQL 连接

    set -a; . .env.local; set +a
    mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" -e "SELECT 1;"
    
    • 成功 → 进入步骤 C
    • 失败 → 打印具体错误(认证 / 主机不可达 / 端口拒接等),提示检查 .env.local停下sql/migrations/V1__initial_schema.sql 已在步骤 A 落盘,用户可在修复连接后手动 mysql < V1.sql,无需重跑 A4 的 DDL 生成

    C. 自动导入 MySQL

    C.1 调 setup-test-db.sh 做防护 + DROP+CREATE 空库

    A2 skeleton-gen 已生成 scripts/setup-test-db.sh,内置三层防护(单一真相源,与 B 阶段 test.sh 共用同款规则):

    • 防护 1:host 白名单(默认 localhost / 127.0.0.1 / ::1,严格相等匹配;可通过 .env.localTEST_DB_ALLOWED_HOSTS 显式扩展)
    • 防护 2:schema 命名后缀检查(必须含 test 或以 _dev / _local / _ci 结尾)
    • 防护 3:远程 host DROP 警告横幅

    直接调用,由它处理凭据加载 + 三层防护 + DROP+CREATE:

    ./scripts/setup-test-db.sh
    

    任一防护失败 → 脚本自身停下并打印明细(host 不在白名单 / schema 名不像测试库 / .env.local 缺失等),本 skill 立即停下不做后续步骤sql/migrations/V1__initial_schema.sql 已在步骤 A 落盘,用户调整 .env.local(如 TEST_DB_ALLOWED_HOSTS 加 host)或 schema 命名后可直接重跑 A4,无需重新生成 V1。

    C.2 把 V1 灌入已清空的 schema

    set -a; . .env.local; set +a
    mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" "$DB_SCHEMA" \
      < sql/migrations/V1__initial_schema.sql
    

    非零退出 → 报错停下,打印 mysql stderr。

    C.3 自检 SHOW TABLES

    set -a; . .env.local; set +a
    ACTUAL=$(mysql -N -B -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -p"$DB_PASSWORD" \
      -e "SHOW TABLES;" "$DB_SCHEMA" | wc -l | tr -d ' ')
    EXPECTED=$(grep -c '^## `' docs/03-数据库设计文档.md)
    [ "$ACTUAL" = "$EXPECTED" ] || { echo "MISMATCH: actual=$ACTUAL expected=$EXPECTED"; exit 1; }
    

    行数不一致 → 报错停下;一致 → 进入步骤 D。

    D. 勾选 docs/08 进度 + 进入 A5

    1. Editdocs/08-模块任务管理.md 勾选 5 个 checkbox(A4 的 4 个子项 + A4 父项):

      • - [ ] sql/migrations/V1__initial_schema.sql 已生成[x]
      • - [ ] .env.local 凭据已验证(mysql -e "SELECT 1" OK)[x]
      • - [ ] setup-test-db.sh 防护通过 + DROP+CREATE + apply V1 已执行[x]
      • - [ ] SHOW TABLES 行数 == docs/03 表数量[x]
      • - [ ] A4 DB 初始化 — db-init[x]
    2. 输出 db-init: 完成(V1 ✓, schema=<名称>, tables=<T>,已 apply 到本地 MySQL)

    3. 立即调用 Skill(downstream-gen) 进入 A5,不等用户手动输入。

    不变量

    • DDL 来源唯一:V1 完全由 docs/03 翻译而来;docs/03 是 schema 单一真相源(SSoT)。后续 V2/V3 由 B 阶段 feature-tdd 在 REQ 实现时写入,并同步回写 docs/03 对应表小节
    • 测试夹具归属 B 阶段:测试数据由 B 阶段每个 REQ 在自己的测试代码 / Spring @Sql / testcontainers fixture 中按需提供
    • 安全守护单一真相源:DROP / 写操作的防护逻辑统一在 scripts/setup-test-db.sh(三层防护:host 白名单 + schema 命名后缀 + 远程 host 横幅),本 skill 与 B 阶段 test.sh 共用同一份规则;本 skill 不重复实现防护
    • DDL 校验 fail-closed:A.3 5 维度全量校验任一不通过都不许进入 apply 阶段
    • 失败可恢复:每次重跑都从空库 DROP+CREATE 开始;中途失败重跑无状态残留

    参考

    • ${CLAUDE_SKILL_DIR}/templates/migration-v1-header-template.sql(V1 头部注释)
    • docs/03-数据库设计文档.md(DDL 翻译输入,SSoT)
    • .env.local(DB 凭据)
    • 产物:sql/migrations/V1__initial_schema.sql(由 Flyway 在 Spring Boot 启动时验证 / apply)