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

docs(backend): 添加数据库迁移工作流规范

DATABASE MIGRATION WORKFLOW 章节:
- 6步标准流程:错误定位→查实体→检查schema.sql→加迁移→同步schema.sql→编译验证
- ensureColumn() 辅助方法优先使用
- 幂等性要求:所有迁移必须可重复执行
- 同步原则:schema.sql 与 DatabaseInitializer 保持最终一致
- 4种场景代码模板:ADD COLUMN / CREATE TABLE / ADD INDEX / MODIFY COLUMN
Xiaogang Liao 4 месяцев назад
Родитель
Сommit
2b85c4577c
1 измененных файлов с 121 добавлено и 0 удалено
  1. 121 0
      cfc-backend/AGENTS.md

+ 121 - 0
cfc-backend/AGENTS.md

@@ -70,6 +70,127 @@ Spring Boot 2.7.18 后端服务,MyBatis-Plus ORM,JWT 认证。
 - 孩子快照:每次测评时保存孩子身高/体重/学校等动态信息到 DanAssessmentResult
 - 多数据源:cfc 主库 `spring.datasource.*` + sfms 源库 `sfms.datasource.*`(只读)
 
+## DATABASE MIGRATION WORKFLOW
+
+当出现 `Unknown column 'xxx' in 'field list'` 等字段不存在错误,或需要进行表结构变更时,按以下流程处理:
+
+### 工作流程
+
+```
+错误日志定位 → 查找实体类 → 检查 schema.sql → 添加迁移 → 同步 schema.sql → 编译验证
+```
+
+### Step 1: 诊断错误
+
+后端日志中出现类似以下错误:
+```
+Unknown column 'xxx' in 'field list'
+Table 'xxx' doesn't exist
+```
+从错误信息中提取:**表名、列名/对象名**,并从异常堆栈找到触发点(哪个 Service/Controller)。
+
+### Step 2: 查找实体与建表 SQL
+
+- 在 `entity/` 目录找到对应实体类,确认字段已定义(`@TableField` 或成员变量)
+- 在 `src/main/resources/schema.sql` 中找到该表的 `CREATE TABLE` 语句,确认列是否已包含
+
+### Step 3: 判断修复位置
+
+| 情况 | 修复方式 |
+|------|---------|
+| schema.sql 建表语句有该列,但生产库没有(旧表) | 只需在 `DatabaseInitializer.runMigrations()` 添加 ALTER TABLE |
+| schema.sql 没有该列,实体也没有 | 在实体添加字段 + schema.sql 建表语句补列 + DatabaseInitializer 加迁移 |
+| 全新表 | schema.sql 添加 CREATE TABLE + DatabaseInitializer 的 runMigrations() 加迁移 |
+| 仅调整约束/类型/默认值 | DatabaseInitializer.runMigrations() 加 MODIFY COLUMN |
+
+### Step 4: 在 DatabaseInitializer 添加迁移
+
+`DatabaseInitializer.java` 的 `runMigrations()` 方法是所有 DDL 迁移的唯一入口。
+
+#### 添加列(首选 ensureColumn 辅助方法)
+
+`ensureColumn(table, column, definition)` 是已有的辅助方法(第 4099 行),自动处理重复列异常:
+
+```java
+// 迁移N: xxx表添加yyy字段
+ensureColumn("table_name", "column_name", "VARCHAR(50) COMMENT '字段说明'");
+```
+
+`ensureColumn` 不可用时(如需要 MODIFY / 多个列在同一个 try 块),使用标准模式:
+
+```java
+// 迁移N: xxx表添加yyy字段(原因描述/需求编号)
+try {
+    jdbcTemplate.execute("ALTER TABLE xxx ADD COLUMN yyy VARCHAR(50) COMMENT '字段说明'");
+    log.info("已添加yyy列到xxx表");
+} catch (Exception e) {
+    // 列已存在,忽略错误
+}
+```
+
+#### 创建新表
+
+```java
+// 迁移N: 创建 xxx 表(功能描述)
+try {
+    jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS xxx (" +
+            "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
+            "...字段定义..." +
+            "INDEX idx_xxx (column)" +
+            ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='表注释'");
+    log.info("已创建xxx表");
+} catch (Exception e) {
+    // 表已存在,忽略错误
+}
+```
+
+#### 添加索引
+
+```java
+try {
+    jdbcTemplate.execute("ALTER TABLE xxx ADD INDEX idx_xxx (column)");
+    log.info("已添加idx_xxx索引到xxx表");
+} catch (Exception e) {
+    // 索引已存在,忽略错误
+}
+```
+
+#### 修改字段
+
+```java
+try {
+    jdbcTemplate.execute("ALTER TABLE xxx MODIFY COLUMN yyy VARCHAR(100) COMMENT '新注释'");
+    log.info("已修改xxx.yyy字段");
+} catch (Exception e) {
+    // 修改失败,忽略错误
+}
+```
+
+### Step 5: 同步 schema.sql
+
+schema.sql 中保持该表最新的完整定义:
+
+- **新增列**:在 `schema.sql` 对应表的 `CREATE TABLE` 语句中也加上该列定义(保持建表即包含所有列)
+- **新建表**:在 `schema.sql` 追加该表的 `CREATE TABLE IF NOT EXISTS` 语句
+- **仅修改约束/默认值**:无需更新 schema.sql
+- **迁移中 `CREATE TABLE IF NOT EXISTS` 创建的表**:也追加到 schema.sql(保持 schema.sql 为完整定义快照)
+
+### Step 6: 验证
+
+```bash
+mvn clean compile   # 编译验证通过即可
+```
+迁移代码是幂等的(try-catch 忽略已存在的列/表/索引),无需运行时验证。
+
+### 规范要点
+
+- **迁移编号**:按现有顺序递增(当前最大编号 `迁移N`,在文件末尾搜索 `// 迁移` 确认最新编号)
+- **注释格式**:`// 迁移N: 表名 操作说明(原因/需求编号)`
+- **日志**:每个成功的 ALTER/CREATE 后加 `log.info()`,失败统一用空 catch 或 `// 忽略`
+- **幂等性**:所有迁移必须可重复执行而不报错(try-catch 包裹 + 列存在检查)
+- **辅助方法**:优先使用 `ensureColumn()` 添加列,它自动处理重复列异常;原始 `try-catch` 模式用于索引/表/非标准操作
+- **同步原则**:**对 schema.sql 的每一条 DDL 变更,必须在 DatabaseInitializer 中有对应的迁移**,反之亦然——两者保持最终一致
+
 ## COMMANDS
 
 ```bash