文档对齐 React 技术栈,并支持渐进式数据库迁移

将 README 与部署文档从仅 init 改为 001 基线 + 增量迁移约定,并补充 create/status 脚手架与序号校验。
This commit is contained in:
yml2213
2026-07-10 12:36:36 +08:00
parent ca5433ada4
commit 1155b3c608
9 changed files with 592 additions and 231 deletions
+53
View File
@@ -1,3 +1,14 @@
/**
* 数据库迁移入口。
*
* 策略:
* - 001_init.sql:空库基线,建立完整结构
* - 002_xxx.sql 起:渐进增量迁移,禁止改已应用历史文件
* - 启动时自动 up;也可用 npm run db:migrate 手动执行
* - 新建迁移:npm run db:migrate:create -- <name>
* - 查看状态:npm run db:migrate:status
*/
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
@@ -12,10 +23,14 @@ const CURRENT_DIR = path.dirname(fileURLToPath(import.meta.url))
const MIGRATIONS_DIR = path.join(CURRENT_DIR, 'migrations')
const MIGRATIONS_TABLE = 'schema_migrations'
const SHORT_NUMERIC_PREFIX_MESSAGE = /^Can't determine timestamp for \d+$/
const MIGRATION_FILE_PATTERN = /^(\d{3})_.+\.sql$/i
export async function runDatabaseMigrations() {
assertMigrationFilesOrdered()
await ensureNodePgMigrateMetadataCompatibility()
console.info(`[db:migrate] 迁移目录: ${MIGRATIONS_DIR}`)
await runner({
databaseUrl: createMigrationDatabaseConfig(),
dir: MIGRATIONS_DIR,
@@ -45,6 +60,44 @@ export async function runDatabaseMigrations() {
await syncLegacyMigrationColumns()
}
function assertMigrationFilesOrdered() {
if (!fs.existsSync(MIGRATIONS_DIR)) {
throw new Error(`迁移目录不存在: ${MIGRATIONS_DIR}`)
}
const files = fs
.readdirSync(MIGRATIONS_DIR)
.filter((name) => name.endsWith('.sql'))
.sort((a, b) => a.localeCompare(b, 'en'))
if (files.length === 0) {
throw new Error(`迁移目录为空: ${MIGRATIONS_DIR}`)
}
const invalid = files.filter((name) => !MIGRATION_FILE_PATTERN.test(name))
if (invalid.length > 0) {
throw new Error(
`迁移文件命名必须为 NNN_name.sql(三位序号),非法文件: ${invalid.join(', ')}`,
)
}
const versions = files.map((name) => Number(name.slice(0, 3)))
for (let index = 1; index < versions.length; index += 1) {
if (versions[index] < versions[index - 1]) {
throw new Error(`迁移序号乱序: ${files[index - 1]} 之后出现 ${files[index]}`)
}
if (versions[index] === versions[index - 1]) {
throw new Error(`迁移序号重复: ${files[index - 1]}${files[index]}`)
}
}
if (files[0] !== '001_init.sql') {
console.warn(
`[db:migrate] 警告: 首个迁移不是 001_init.sql(当前为 ${files[0]}),请确认是否刻意调整基线`,
)
}
}
async function ensureNodePgMigrateMetadataCompatibility() {
await query(`
CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (
+7 -2
View File
@@ -1,5 +1,10 @@
-- 初始化完整业务库结构。
-- 本项目当前不维护历史测试库的逐步升级链路;新环境只需要执行这一份 init 迁移。
-- 001_init.sql —— 空库基线迁移(完整业务库结构
--
-- 约定:
-- 1. 仅在空库首次迁移时执行本文件,建立全量基线结构。
-- 2. 后续结构变更不要改本文件,改为新增 002_xxx.sql / 003_xxx.sql 等渐进迁移。
-- 3. 新环境按文件名顺序执行:001_init → 后续增量;已有环境只应用尚未记录的增量。
-- 4. 已应用到任何共享/生产环境的迁移文件禁止修改内容。
CREATE EXTENSION IF NOT EXISTS pgcrypto;