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.sqlA.3 全量校验 DDL ↔ docs/03 一致性
5 维度全量校验。任一不一致 → 报错停下,列出全部差异明细;V1.sql 保留以便人工 diff。
-
表名集合相等
-
S_doc= grep^## \`` of docs/03 -
S_sql= grepCREATE TABLE \`` of V1 -
S_doc - S_sql非空(漏表)/S_sql - S_doc非空(多表)→ 报错列出差集
-
-
每张表全列名 + 列序相等(遍历 S_sql 全部表,无抽样)
- 对每张表 t:
- 从 docs/03 抽该表
### 字段表格行序,得cols_doc[t] - 从 V1 抽该
CREATE TABLE段内\`出现序,得cols_sql[t]`
- 从 docs/03 抽该表
-
数组相等(含顺序);不等则列出
<table>: docs=[...], sql=[...]
- 对每张表 t:
-
每个字段类型 / Nullable / 默认值一致
- 对每个
<table>.<col>,归一化后字符串比对。归一化规则:- 类型主词转小写(
BIGINT→bigint) -
UNSIGNED保留为小写 - 显示宽度
INT(11)→int(去括号) - Nullable:docs/03
否↔ DDLNOT NULL;docs/03是↔ DDL 无NOT NULL - 默认值:去多余空格 + 统一大小写
- 类型主词转小写(
-
COMMENT/COLLATE/CHARSET不参与比对(避免装饰差异误报) - 不一致 → 列出
<table>.<col>: docs=<X>, ddl=<Y>
- 对每个
-
每张表索引名集合相等
-
idx_doc[t]= docs/03 该表### 索引列表的名字 -
idx_sql[t]= V1 中归属该表的CREATE INDEX <name>+ inlineKEY \` / UNIQUE KEY ``(排除PRIMARY` 自动名) - 集合不等 → 报错列出差集
-
-
外键名集合相等(全局,不分表)
-
fk_doc= docs/03 各表### 外键列表的所有名字 -
fk_sql= V1 中所有ADD CONSTRAINT \` FOREIGN KEY` 的名字 - 集合不等 → 报错列出差集
-
校验全部通过 → 进入步骤 B。任一维度不通过都不许继续;提示用户 diff
docs/03↔V1.sql后修正 docs/03(SSoT),重跑 A3 让db-design-gen/ 本 skill 重新生成 V1。B. 数据库环境检查
B.1 检查 .env.local 凭据
用
Glob检查.env.local是否存在;不存在 → 提示用户重新运行 A2skeleton-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 +a与source行为一致但更明确,配合单引号可避免特殊字符被 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.local的TEST_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
-
用
Edit在docs/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]
-
输出
db-init: 完成(V1 ✓, schema=<名称>, tables=<T>,已 apply 到本地 MySQL)。立即调用
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)
- 外键:在所有表创建完成后统一追加: