--- 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 `;列尾用 `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` 拼装最终文件: ```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]` - **数组相等**(含顺序);不等则列出 `
: docs=[...], sql=[...]` 3. **每个字段类型 / Nullable / 默认值一致** - 对每个 `
.`,归一化后字符串比对。归一化规则: - 类型主词转小写(`BIGINT` → `bigint`) - `UNSIGNED` 保留为小写 - 显示宽度 `INT(11)` → `int`(去括号) - Nullable:docs/03 `否` ↔ DDL `NOT NULL`;docs/03 `是` ↔ DDL 无 `NOT NULL` - 默认值:去多余空格 + 统一大小写 - **`COMMENT` / `COLLATE` / `CHARSET` 不参与比对**(避免装饰差异误报) - 不一致 → 列出 `
.: docs=, ddl=` 4. **每张表索引名集合相等** - `idx_doc[t]` = docs/03 该表 `### 索引` 列表的名字 - `idx_sql[t]` = V1 中归属该表的 `CREATE INDEX ` + inline `KEY \`\` / UNIQUE KEY \`\``(排除 `PRIMARY` 自动名) - 集合不等 → 报错列出差集 5. **外键名集合相等**(全局,不分表) - `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` 是否存在;不存在 → 提示用户重新运行 A2 `skeleton-gen` 重建并停下。 用 `Bash` 加载并校验 5 个必填字段非空: ```bash 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 连接 ```bash 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: ```bash ./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 ```bash 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 ```bash 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. 用 `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]` 2. 输出 `db-init: 完成(V1 ✓, schema=<名称>, tables=,已 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)