SKILL.md 12.1 KB

name: build-db description: 计划第 3 段——解析 docs/03 → V1 migration + validate.sh 校验 + apply 到本地 MySQL;生成 docs/05 API 契约 + 回填 REQ 依赖接口;计算模块/REQ 依赖顺序写入 docs/08 § 二(排序权威);最终占位符扫描后停下(计划完成)。 user-invocable: false

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

所有输出必须使用中文。

build-db

执行步骤

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

Bash 执行以下命令(文件不存在时静默忽略,不报错):

cat "${CLAUDE_PLUGIN_ROOT}/skills/crosscut/plan-start/banners/flow.txt" 2>/dev/null || true

打印横幅:▶ 计划阶段 ③ — build-db

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

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

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

  • 每张表一段 CREATE TABLE
  • 字段顺序与 docs/03 表格行序一致;Nullable 列直接映射 NOT NULL / NULL默认 列映射 DEFAULT <value>;列尾用 COMMENT '<业务含义>' 写注释
  • 索引
  • 外键:在所有表创建完成后统一追加

要求:

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

A.2 落盘 V1 文件

Bash: mkdir -p sql/migrations

Writesql/migrations/V1__initial_schema.sql,内容 = 头部注释 + DDL 主体:

  1. 头部注释(6 行 SQL 注释):

    • -- Flyway migration V1 — initial schema for <project_name>(从 CLAUDE.md § 🎯 项目概述 读)
    • -- Generated: <YYYY-MM-DDTHH:MM:SSZ>(UTC ISO 8601 时间戳)
    • -- Source: 由计划 ③ build-db 从 docs/03-数据库设计文档.md 翻译生成(schema SSoT 是 docs/03)
    • -- This is the FIRST migration; subsequent schema changes must be written as new files sql/migrations/V2__<desc>.sql, V3__... etc.
    • -- Apply: Flyway runs this automatically at Spring Boot startup.
    • -- Do not hand-edit this file after it is committed; write a new migration instead.
  2. DDL 主体:A.1 推导出的所有 CREATE TABLECREATE INDEXALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY,按此顺序拼接。

A.3 校验 V1 ↔ docs/03 集合一致性 + 自主修正

${CLAUDE_SKILL_DIR}/scripts/validate.sh 做脚本化的校验:

bash "${CLAUDE_SKILL_DIR}/scripts/validate.sh" \
  sql/migrations/V1__initial_schema.sql \
  docs/03-数据库设计文档.md

退出码与处理:

  • 0 → 通过,进入步骤 B
  • 1自主修正循环(最多 3 轮,docs/03 是 SSoT 不动):
    1. 解析 stderr 差异清单,修正 V1.sql
    2. 重跑 validate.sh
    3. 退出 0 → 进入 B;退出 1 且本轮 < 3 → 回步骤 1;本轮 ≥ 3 仍失败 → 停下,打印最终残留差异 + 已尝试的 3 轮修正摘要,让用户介入
  • 2 → 用法错(V1 / docs 路径找不到),打印路径并停下

完成后(V1 写入并通过 validate.sh 校验),用 Editdocs/08-模块任务管理.md 中勾选:

  • - [ ] V1 migration 已生成并校验 + apply 到本地 MySQL(步骤 C 完成后一并勾选)

B. 数据库环境检查

B.1 检查 .env.local 凭据

Glob 检查 .env.local 是否存在;不存在 → 提示用户重新运行计划 ② design 重建并停下。

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 后重跑。

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停下

C. 自动导入 MySQL

C.1 DROP+CREATE 空库

./scripts/setup-test-db.sh

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。

完成后,用 Editdocs/08-模块任务管理.md 中勾选:

  • - [ ] V1 migration 已生成并校验 + apply 到本地 MySQL

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/05 API 契约 + 回填 REQ 依赖接口

D.1 渲染 docs/05

  1. 读取 ${CLAUDE_SKILL_DIR}/templates/docs-05-header-template.md,填充 base_path(从 docs/04 § 一 推导 API 前缀,如 /api/v1)、auth_note(认证方式说明)、pagination_note(分页参数约定),写入 docs/05-API接口契约.md 头部。
  2. 对所有模块的每个 REQ:读取并推断 ${CLAUDE_SKILL_DIR}/templates/docs-05-endpoint-template.md,追加到 docs/05。

完成后,用 Editdocs/08-模块任务管理.md 中勾选:

  • - [ ] docs/05 API 契约已生成 + 回填 REQ 依赖接口(步骤 D.2 完成后一并勾选)

D.2 回填模块头 + REQ 卡片的 TBD 字段

  1. docs/01-需求清单/*/_module.md(模块头)和 docs/01-需求清单/*/REQ-*.md(REQ 卡片)中搜索 TBD(build-db 自动补) 并回填。不动 TBD(design 自动补)(应已由 design 回填完毕)。
    • _module.md依赖模块 字段:填入该模块所依赖的其他模块 ID(逗号分隔,无则填
    • REQ-*.md依赖接口 字段:填入该 REQ 依赖的接口路径(逗号分隔,无则填
  2. 打印回填统计:build-db 回填 <M> 处模块"依赖模块" + <N> 处 REQ"依赖接口"

完成后,用 Editdocs/08-模块任务管理.md 中勾选:

  • - [ ] docs/05 API 契约已生成 + 回填 REQ 依赖接口

E. 模块/REQ 依赖顺序 → docs/08 § 二

本步骤计算模块/REQ 依赖拓扑顺序,将结果写入 docs/08 § 二(该 § 二 行序是 coding-start 的排序权威,无单独开发计划文件)。

E.1 构建模块依赖 DAG + 拓扑排序

  1. 读取所有 docs/01-需求清单/*/_module.md,提取每个模块的 依赖模块 字段(经步骤 D.2 已回填)。
  2. 构建模块依赖 DAG(有向无环图),节点 = 模块 ID,边 = 「A 依赖 B → B 先于 A」。
  3. 拓扑排序得到 module_topo_order[]
  4. 环依赖打破:若模块 DAG 存在环(module_A ↔ module_B),按启发式(字母序 / 被依赖次数多者先)破环排出 module_topo_order,并在参与环的模块里第一个 REQnote 字段填入原因(如 "A↔B 互依赖:先做 A 的骨架")。

E.2 模块内 REQ 排序

对每个模块内部:

  1. 读取该模块所有 REQ-*.md,提取每个 REQ 的 依赖接口 + 依赖表 字段,推导 REQ 间依赖关系。
  2. 拓扑排序得到模块内 req_order[]
  3. REQ 级环依赖打破:若模块内 REQ 互依赖,按字母序 / 被依赖次数多者先破环,note 填原因;非环 REQ note

E.3 渲染 docs/08 § 二

module_topo_order[] 依次为每个模块渲染 bullet,追加到 docs/08-模块任务管理.md § 二(追加在 § 二 的注释块之后,保留原有注释格式示例):

读取 ${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md,填充:

  • {{module_id}}:模块 ID
  • {{module_name}}:模块中文名
  • {{depends_on}}:依赖模块 ID 列表(逗号分隔,无则填
  • {{path_scopes}}backend/module/<id>/
  • {{req_checklist}}:按模块内 req_order[] 生成,每行 - [ ] REQ-XXX-NNN 功能名

每个模块 bullet 间空一行,一次性追加全部模块。

完成后,用 Editdocs/08-模块任务管理.md 中勾选:

  • - [ ] docs/08 § 二 模块清单(含 REQ 顺序)已生成

F. 一致性检查 + 占位符扫描 + 计划完成

F.1 一致性检查(最多 3 轮自修复)

检查项

  • 每个 docs/01 REQ 都出现在 docs/05(作为接口,如适用)
  • docs/08 § 二 的 module_id 集合 = docs/01-需求清单/ 的模块目录集合

不一致 → 按差异类型自主修复

  • REQ 缺 docs/05 endpoint → 按步骤 D.1 规则为该 REQ 推测,并追加到 docs/05
  • module_id 缺 docs/08 § 二 → 按步骤 E 规则渲染该模块 bullet,按拓扑序插入正确位置

修复后重跑检查;通过 → 进入 F.2;3 轮仍失败 → 停下,打印最终残留差异 + 已尝试的 3 轮修复摘要让用户介入。

F.2 最终占位符扫描

a. TBD → 自动补齐:Grep 搜索 TBD(design 自动补)TBD(build-db 自动补)。有命中则就地补填(design 残留 → 查 docs/03 填 依赖表;build-db 残留 → 查 docs/05 按 REQ-ID 填 依赖接口),再 Grep 确认 0 命中;仍残留报错停下。

b. 【人工填写:...】 → QA 循环等用户补

循环执行直到搜索不到 【人工填写: 且用户选「继续」:

  • 0 命中 → 直接放行进入步骤 F.3
  • 有命中 → 打印残留清单(<文件:行号> — <内容摘要>),用 AskUserQuestion 弹「继续」/「有疑问想先沟通」二选一;用户回答后重扫验证再决定放行 / 继续循环

每次弹 QA 前都重扫一次——保证用户看到的 N 是最新的,避免「用户以为填完但实际还有残留」直接放行。

F.3 勾选 § 一 ③ 顶层 + 打印计划完成横幅

完成后,用 Editdocs/08-模块任务管理.md 中勾选 ③ 顶层:

  • - [ ] 计划 ③ DB 初始化 + 下游文档 — build-db

打印计划阶段终止横幅并停下(不自动进入 coding 阶段):

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 [build-db] ✅ 计划阶段(①②③)全部完成

 所有规划文档已就绪,docs/08 § 一 全部勾选。

 ⚠️ 进入 Coding 阶段前必须完成:
   1. 审核全部 Plan 产出(docs/01 / 03 / 04 / 05 / 08 + CLAUDE.md + sql/migrations/V1 + scripts/*)

   2. 把全部 Plan 产物 commit 到本地默认分支(main / master):
        git add -A && git commit -m "chore: plan phase done"

   3. 运行 /erp-workflow:coding-start 进入编码阶段
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

停止,不调用任何下游 skill。

参考

  • docs/03-数据库设计文档.md(DDL 翻译输入,SSoT)
  • docs/01-需求清单/index.md(模块索引)
  • docs/01-需求清单/<module>/_module.md(模块依赖:回填 依赖模块;拓扑排序输入)
  • docs/01-需求清单/<module>/REQ-*.md(REQ 依赖:回填 依赖接口;模块内 REQ 排序输入)
  • docs/08-模块任务管理.md § 二(模块拓扑顺序追加目标;是 coding-start 的排序权威)
  • ${CLAUDE_SKILL_DIR}/scripts/validate.sh(A.3 表名 + 每表列名集合校验脚本)
  • ${CLAUDE_SKILL_DIR}/templates/docs-05-header-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-05-endpoint-template.md
  • ${CLAUDE_SKILL_DIR}/templates/docs-08-module-row-template.md(模块 bullet 行模板)
  • .env.local(DB 凭据)
  • 产物:sql/migrations/V1__initial_schema.sql(由 Flyway 在 Spring Boot 启动时 apply)