Chatbuddy AI 升级开发文档

本文是教作为开发者如果执行升级脚本书写:
什么时候需写 migration
什么时候需写Upgrade
Upgrade应该怎么写
有没有命令可以生成升级文件
写完以后系统是怎么识别并执行的
本文适用于主程序范围内的二开:
packages/api
packages/client
packages/core
packages/@chatbuddy-ai/*

1. 先记住一句话

主程序升级的执行顺序是:
migration -> Upgrade -> data/versions/<version>
也就是说:
1.
先做数据库结构升级
2.
再做业务数据升级
3.
完成时写版本完成标记
对应入口在:
packages/api/src/core/database/services/database-init.service.ts
packages/api/src/core/database/services/version-manager.service.ts

2. migration 和 Upgrade 到底怎么分

这是二开时最容易写错的地方。

2.1 什么时候写 migration

只要你改的是“数据库结构”,就应该优先写 migration。
典型场景:
新增表
新增字段
修改字段类型
新增索引
新增约束
重命名表或字段
一句话判断:
如果这次改动可以描述成“数据库 schema 变了”,就写 migration。

2.2 什么时候写 Upgrade

只要你改的是“已有数据怎么迁移、修复、初始化”,就应该写Upgrade
典型场景:
给旧数据补默认值
把旧配置结构改成新配置结构
新版本上线后要批量清洗历史数据
新增功能需初始化系统数据、菜单数据、配置数据
需跨表读取旧数据再写入新表
一句话判断:
如果这次改动依赖“读取已有数据后再处理”,就写 Upgrade。

2.3 什么情况两者都要写

很常见。
比如你给某张表新增了一个字段status
1.
migration:先把字段加出来
2.
Upgrade:再把历史数据的status补成合理值
这就是最标准的写法。

2.4 什么情况两者都不用写

下面这些通常不需升级脚本:
纯前端页面调整
纯接口逻辑调整,但不涉及历史数据
文案、样式、交互变化
只改了运行时计算逻辑,没有改数据库结构和存量数据

3. 主程序 migration 写在哪里

代码目录:
packages/@chatbuddy-ai/db/src/migrations/
运行时实际读取的是构建产物:
packages/@chatbuddy-ai/db/dist/migrations/
升级器对文件名的识别规则是:
{timestamp}-{version}-{description}.js
代码里对应的 ts 文件命名也应该遵循同样结构,比如:
1776000000000-26.1.0-add-report-table.ts
仓库里的现有例子:
packages/@chatbuddy-ai/db/src/migrations/1765088629599-25.1.0-add-member.ts
packages/@chatbuddy-ai/db/src/migrations/1774943726484-26.0.0-upgrade.ts

4. 主程序 migration 有哪些生成命令

@chatbuddy-ai/db里已经提供了生成命令。
打开目录:

4.1 手工创建 migration 模板

比如:
这个命令会:
1.
src/migrations/下创建文件
2.
帮你生成基础模板
3.
需你自己补 SQL 或 TypeORM 逻辑

4.2 根据实体变化自动生成 migration

比如:
这个命令会:
1.
对比实体定义和当前数据库 schema
2.
自动生成 migration
3.
自动改名成项目要求的版本命名格式
使用前提:
数据库必须是实体文件改动前的数据库
数据库必须可连接
.env已配置数据库连接
实体变更已经写好

5. 主程序 Upgrade 写在哪里

主程序Upgrade@chatbuddy-ai/upgrade包管理。
推荐目录:
packages/@chatbuddy-ai/upgrade/src/scripts/<version>/index.ts
比如:
packages/@chatbuddy-ai/upgrade/src/scripts/26.1.0/index.ts
兼容旧写法:
packages/@chatbuddy-ai/upgrade/src/scripts/26.1.0.ts
但从加载逻辑看,优先推荐目录形式:
scripts/<version>/index.js

6. 主程序 Upgrade 有没有生成命令

目前仓库里没有“自动生成 Upgrade 文件”的现成命令。
也就是说:
migration:可以用脚本创建或自动生成
Upgrade:需你按约定目录手工新建文件
这是二开文档里最重要的一个现实约束。

7. 主程序 Upgrade 的最小写法

最推荐直接继承BaseUpgradeScript
示例:
import { BaseUpgradeScript, UpgradeContext } from "../../index"; export class Upgrade extends BaseUpgradeScript { readonly version = "26.1.0"; async execute(context: UpgradeContext): Promise<void> { this.log("Start upgrading data for 26.1.0"); const { dataSource } = context; await dataSource.query(` UPDATE report SET summary = '' WHERE summary IS NULL `); this.success("Upgrade finished"); }
} export default Upgrade;
关键点:
1.
类名建议叫Upgrade
2.
要实现execute(context)
3.
version要和目录版本一致
4.
推荐同时export default Upgrade
相关基础类型定义在:
packages/@chatbuddy-ai/upgrade/src/index.ts

8. 主程序 Upgrade 里最常用的方法怎么用

execute(context)里最核心的是context.dataSource

8.1 执行原生 SQL

适合:
批量更新
一次性修复旧数据
复杂 SQL
示例:
await context.dataSource.query( ` UPDATE "config" SET value = $1 WHERE key = $2 `, [JSON.stringify({ enabled: true }), "site_settings"],
);

8.2 用 Repository 读写实体

适合:
你已经有明确实体
想利用实体映射和条件查询
示例:
const repo = context.dataSource.getRepository("User"); const users = await repo.find({ where: { status: "active" },
}); for (const user of users) { user.nickname = user.nickname || user.username;
} await repo.save(users);

8.3 用dataSource.manager

适合:
想统一通过 manager 操作多个实体
示例:
const manager = context.dataSource.manager; const items = await manager.find("Config", { where: { group: "system" },
});

8.4 用事务

只要你的升级逻辑里包含“多个流程必须一起成功”,就建议显式开事务。
示例:
const queryRunner = context.dataSource.createQueryRunner;
await queryRunner.connect;
await queryRunner.startTransaction; try { await queryRunner.query(` UPDATE "report" SET "status" = 'ready' WHERE "status" IS NULL `); await queryRunner.query(` INSERT INTO "audit_log" ("action") VALUES ('upgrade-26.1.0') `); await queryRunner.commitTransaction;
} catch (error) { await queryRunner.rollbackTransaction; throw error;
} finally { await queryRunner.release;
}

9. 如何决定“写 migration 还是写 Upgrade”

可以直接按下面这张表判断:
场景应该写什么
新增表、字段、索引migration
把旧字段数据迁移到新字段migration + Upgrade
给旧数据补默认值Upgrade
清理脏数据Upgrade
初始化新版本系统配置Upgrade
纯前端改动都不用

10. 写 Upgrade 时的几个硬规则

10.1 尽量幂等

也就是重复执行不要把数据写坏。
比如:
先判断字段值是不是已经迁移过
UPDATE ... WHERE target IS NULL
INSERT前先查是否已存在

10.2 不要把建表、加字段写进 Upgrade

结构变更必须优先放 migration。
因为系统执行顺序本来就是:
migration -> Upgrade
如果你把 DDL 塞进Upgrade,后面维护会很乱。

10.3 一个版本只处理这个版本该做的事

不要把很多历史修复混进同一个脚本。
建议按版本拆开,这样升级失败时更容易定位。

10.4 日志要能看懂

建议至少打这些日志:
开始执行
当前处理批次/数据量
完成

11. 写完之后怎么让系统识别

主程序升级器识别版本的依据是:
1.
根目录package.json.version
2.
@chatbuddy-ai/db构建产物里的 migration
3.
@chatbuddy-ai/upgrade构建产物里的脚本
所以你写完升级代码后,通常需:
接着重启应用。
系统启动时会自动:
1.
判断data/versions/<current-version>是否存在
2.
如果不存在,就按版本顺序跑 migration
3.
再跑Upgrade
4.
完成时写版本文件

12. 开发者最常见的几个错误

12.1 只改了Upgrade,没改版本号

如果当前版本文件已经存在,升级器不会再跑。
所以:
新增升级逻辑通常意味着新版本
主程序版本要和你的升级内容对应

12.2 把数据修复写进 migration

少量简单数据修复可以接受,但涉及复杂业务逻辑时应该拆到Upgrade

12.3Upgrade写得不可重复执行

一旦第一次跑到一半失败,第二次重启可能继续撞错。

12.4 写了代码但没构建

升级器读的是构建产物,不是代码目录。

13. 推荐开发流程

当你给主程序新增一个版本升级逻辑时,推荐顺序:
1.
先确认这次改动是 schema 变化、数据变化,还是两者都有
2.
schema 变化先创建 migration
3.
数据变化再手工补Upgrade
4.
把版本号提升到目标版本
5.
pnpm build
6.
在测试库启动验证
7.
确认data/versions/<version>已写入

14. 你能够直接照抄的判断标准

如果你只记一段,记下面这段就够了:
改表结构:写 migration
改历史数据:写Upgrade
两者都有:两个都写,先 migration 后Upgrade
纯前端或纯运行时逻辑:通常都不用写
migration 有生成命令,Upgrade没有,需手工创建

319 篇文档 · 内容同步自官方帮助中心