Просмотр исходного кода

docs(bmm): 添加 TypeORM 数据库迁移标准化工作流

- 创建 create-db-migration 工作流,标准化双 Data Source 架构下的迁移流程
- 工作流定义存放在 _bmad/ (升级安全)
- Skill 包装器存放在 .claude/commands/bmad/ (升级可能覆盖)
- 更新 project-context.md 记录自定义工作流和升级恢复步骤
- 包含前置检查、生成迁移、审查、执行、验证的完整流程
- 涵盖新建表、添加列、数据迁移、重命名等常见场景

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 5 дней назад
Родитель
Сommit
1048f73dca

+ 15 - 0
.claude/commands/bmad/bmm/workflows/create-db-migration.md

@@ -0,0 +1,15 @@
+---
+description: 'Create TypeORM database migrations with standardized workflow for dual data-source architecture (PROJECT CUSTOM WORKFLOW)'
+---
+
+IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @_bmad/bmm/workflows/5-utilities/create-db-migration/workflow.md, READ its entire contents and follow its directions exactly!
+
+---
+
+## ⚠️ UPGRADE NOTICE
+
+This is a **project custom workflow**. After BMM upgrades, this skill wrapper may be overwritten.
+
+**To restore after upgrade, recreate this file with the same content.**
+
+The workflow definition is safely stored in: `_bmad/bmm/workflows/5-utilities/create-db-migration/workflow.md`

+ 44 - 0
_bmad-output/project-context.md

@@ -580,4 +580,48 @@ pnpm typecheck
 
 ---
 
+## BMM 自定义工作流
+
+### 项目自定义工作流列表
+
+本项目扩展了 BMAD 框架,添加了以下自定义工作流:
+
+| 工作流 | Skill 名称 | 说明 | 定义位置 |
+|--------|-----------|------|---------|
+| **数据库迁移** | `bmad:bmm:workflows:create-db-migration` | TypeORM 数据库迁移标准化流程,支持双 Data Source 架构 | `_bmad/bmm/workflows/5-utilities/create-db-migration/workflow.md` |
+
+### ⚠️ BMM 升级注意事项
+
+当升级 BMAD 框架时:
+
+1. **工作流定义**(`_bmad/bmm/workflows/`)- ✅ 安全,不会覆盖
+2. **Skill 包装器**(`.claude/commands/bmad/bmm/workflows/`)- ⚠️ 可能被覆盖
+
+**升级后恢复步骤**:
+
+如果自定义 Skill 包装器被覆盖,从工作流定义文件中恢复:
+
+```bash
+# 1. 检查工作流定义是否还在
+ls _bmad/bmm/workflows/5-utilities/create-db-migration/workflow.md
+
+# 2. 如果 Skill 包装器被删除,重新创建
+# 内容参考工作流定义中的说明
+```
+
+### 双 Data Source 架构
+
+TypeORM 迁移使用双 Data Source 架构:
+
+| Data Source | 文件 | 用途 | 实体加载方式 |
+|-------------|------|------|-------------|
+| API Data Source | `packages/server/src/data-source.ts` | 运行时 API | 类数组 `Object.values(entities)` |
+| CLI Data Source | `packages/server/src/data-source-cli.ts` | TypeORM CLI | Glob 模式 `"../**/*.entity.ts"` |
+
+**原因**:TypeORM CLI 不支持类数组,必须使用 Glob 模式加载实体。
+
+**影响**:新增实体时,无需修改配置(Glob 自动覆盖),直接使用迁移工作流即可。
+
+---
+
 _此文档由 generate-project-context 工作流生成。更新项目时请保持此文档同步。_

+ 322 - 0
_bmad/bmm/workflows/5-utilities/create-db-migration/workflow.md

@@ -0,0 +1,322 @@
+---
+name: create-db-migration
+description: TypeORM 数据库迁移工作流 - 双 Data Source 架构下的标准化迁移创建流程
+---
+
+# TypeORM 数据库迁移工作流
+
+**目标:** 在双 Data Source 架构下,标准化 TypeORM 数据库迁移的创建和执行流程。
+
+**你的角色:** 数据库迁移专家,熟悉 TypeORM CLI、双 Data Source 架构、PostgreSQL 数据库操作。
+
+---
+
+## 工作流架构
+
+这是一个**单流程、检查清单驱动**的工作流:
+
+- 每个步骤必须按顺序完成
+- 每个步骤都有明确的验证标准
+- 失败时停止并解决问题后再继续
+
+---
+
+## 背景:双 Data Source 架构
+
+### 为什么需要两个 Data Source?
+
+**TypeORM CLI 的技术限制:**
+
+| Data Source | 文件 | 用途 | 实体加载方式 |
+|-------------|------|------|-------------|
+| **API Data Source** | `packages/server/src/data-source.ts` | 运行时 API 使用 | 类数组 `Object.values(entities)` |
+| **CLI Data Source** | `packages/server/src/data-source-cli.ts` | TypeORM CLI 迁移 | Glob 模式 `"../**/*.entity.ts"` |
+
+**关键约束:**
+- TypeORM CLI **不支持**直接传递类数组
+- CLI 必须使用 Glob 模式加载实体
+- 两个文件都有存在必要,不可合并
+
+**好消息:**
+- CLI 的 Glob 已配置覆盖所有模块路径
+- 新增实体**无需修改**任何配置
+- 迁移文件自动被 CLI 发现
+
+---
+
+## 执行流程
+
+### 步骤 1:前置检查
+
+**检查实体文件是否已创建:**
+
+```bash
+# 确认实体文件存在
+ls allin-packages/[module-name]/src/entities/[entity-name].entity.ts
+```
+
+**检查实体是否在 Glob 覆盖范围内:**
+
+查看 `packages/server/src/data-source-cli.ts` 确认实体路径已被包含:
+```typescript
+entities: [
+  "../allin-packages/[your-module]/src/entities/*.ts",  // 确认你的模块在这里
+  // ...
+]
+```
+
+**检查表是否已存在于数据库:**
+
+```bash
+psql -h 127.0.0.1 -U postgres -c "\dt [table_name]"
+```
+
+如果表已存在,**停止工作流** - 考虑是否需要修改表而不是创建新表。
+
+---
+
+### 步骤 2:生成迁移文件
+
+**进入 server 目录:**
+
+```bash
+cd packages/server
+```
+
+**运行 TypeORM 迁移生成命令:**
+
+```bash
+pnpm migration:generate -- -n [MigrationName]
+```
+
+**迁移命名规范:**
+- 使用 PascalCase
+- 描述性名称,如 `CreateDisabledPersonPhoneTable`
+- 如涉及数据迁移,添加 `AndMigrateData` 后缀
+
+**预期输出:**
+```
+Migration /path/to/packages/server/migrations/[timestamp]-[MigrationName].ts has been generated successfully.
+```
+
+---
+
+### 步骤 3:审查生成的迁移文件
+
+**打开生成的迁移文件:**
+
+```bash
+# 迁移文件位于
+packages/migrations/[timestamp]-[MigrationName].ts
+```
+
+**检查清单:**
+
+- [ ] `up()` 方法包含正确的 `CREATE TABLE` 或 `ALTER TABLE` 语句
+- [ ] `down()` 方法包含正确的回滚逻辑
+- [ ] 列类型、约束、索引与实体定义一致
+- [ ] 外键关系正确设置
+- [ ] CASCADE 删除策略符合预期
+
+**常见问题修复:**
+
+| 问题 | 原因 | 解决方案 |
+|------|------|---------|
+| 迁移为空 | 实体与数据库表已同步 | 检查是否真的需要迁移 |
+| 缺少索引 | 实体 `@Index()` 未被识别 | 手动添加 `queryRunner.createIndex()` |
+| 列类型不匹配 | TypeORM 推断错误 | 手动修改列类型定义 |
+
+---
+
+### 步骤 4:执行迁移
+
+**确认当前数据库状态(可选):**
+
+```bash
+pnpm migration:show
+```
+
+**运行迁移:**
+
+```bash
+pnpm migration:run
+```
+
+**预期输出:**
+```
+query: SELECT * FROM "migrations" "migrations" ...
+query: CREATE TABLE "[table_name]" ...
+Migration [MigrationName] has been executed successfully.
+```
+
+---
+
+### 步骤 5:验证迁移结果
+
+**检查表是否创建成功:**
+
+```bash
+psql -h 127.0.0.1 -U postgres -c "\d+ [table_name]"
+```
+
+**验证表结构:**
+
+- [ ] 列名和类型正确
+- [ ] 主键存在
+- [ ] 外键约束正确
+- [ ] 索引已创建
+- [ ] 默认值设置正确
+
+**如涉及数据迁移,验证数据:**
+
+```bash
+psql -h 127.0.0.1 -U postgres -c "SELECT COUNT(*) FROM [table_name];"
+```
+
+---
+
+### 步骤 6:测试回滚(可选但推荐)
+
+**在开发环境中测试回滚:**
+
+```bash
+pnpm migration:revert
+```
+
+**验证表已删除/还原**
+
+**重新运行迁移:**
+
+```bash
+pnpm migration:run
+```
+
+---
+
+## 常见场景
+
+### 场景 A:新建实体的表迁移
+
+1. 实体已创建 → 执行步骤 2 生成迁移
+2. 审查迁移(通常无需修改)
+3. 运行并验证
+
+### 场景 B:添加新列到现有表
+
+1. 修改实体添加 `@Column()`
+2. 生成迁移
+3. 审查 `ALTER TABLE` 语句
+4. 如涉及现有数据,考虑默认值
+5. 运行并验证
+
+### 场景 C:带数据迁移的表结构变更
+
+1. 生成迁移(只含结构变更)
+2. 手动编辑迁移文件,在 `up()` 中添加数据迁移 SQL
+3. 示例:
+
+```typescript
+public async up(queryRunner: QueryRunner): Promise<void> {
+  // 1. 结构变更
+  await queryRunner.addColumn(
+    'disabled_person',
+    new TableColumn({
+      name: 'new_field',
+      type: 'varchar',
+      length: '50',
+    })
+  );
+
+  // 2. 数据迁移
+  await queryRunner.query(`
+    UPDATE disabled_person
+    SET new_field = SUBSTRING(old_field, 1, 50)
+    WHERE new_field IS NULL
+  `);
+}
+```
+
+### 场景 D:重命名列或表
+
+TypeORM 有时检测不到重命名,会生成 DROP + ADD。
+
+**手动优化:**
+
+```typescript
+// TypeORM 生成的(不理想)
+public async up(queryRunner: QueryRunner): Promise<void> {
+  await queryRunner.dropColumn('table', 'old_name');
+  await queryRunner.addColumn('table', new TableColumn({ name: 'new_name', ... }));
+}
+
+// 手动优化为重命名
+public async up(queryRunner: QueryRunner): Promise<void> {
+  await queryRunner.renameColumn('table', 'old_name', 'new_name');
+}
+```
+
+---
+
+## 注意事项
+
+### ⚠️ 生产环境迁移
+
+1. **先在开发/测试环境验证**
+2. **备份数据库**
+3. **评估迁移时间**(大表操作可能很慢)
+4. **准备回滚计划**
+5. **在低峰时段执行**
+
+### ⚠️ 数据迁移
+
+1. **分批处理**:大表数据迁移分批执行
+2. **事务控制**:考虑是否需要关闭自动提交
+3. **验证脚本**:先 SELECT 预览,再 UPDATE
+4. **保留原字段**:先保留,验证无误后再清理
+
+### ⚠️ 外键约束
+
+1. **先创建被引用的表**
+2. **CASCADE 设置要谨慎**
+3. **考虑数据的完整性**
+
+---
+
+## 参考命令
+
+```bash
+# 查看迁移状态
+pnpm migration:show
+
+# 生成迁移
+pnpm migration:generate -- -n MigrationName
+
+# 运行迁移
+pnpm migration:run
+
+# 回滚最后一次迁移
+pnpm migration:revert
+
+# 查看表结构
+psql -h 127.0.0.1 -U postgres -c "\d+ table_name"
+
+# 查看所有表
+psql -h 127.0.0.1 -U postgres -c "\dt"
+```
+
+---
+
+## 工作流完成
+
+当所有步骤通过验证后,工作流完成。
+
+**在 Story 任务中记录:**
+
+```yaml
+- [x] Task: 数据库迁移
+  - [x] 检查实体在 glob 覆盖范围内
+  - [x] 使用 pnpm migration:generate 生成迁移
+  - [x] 审查并调整迁移文件
+  - [x] 运行 pnpm migration:run
+  - [x] 验证表结构和数据
+```