问题:runMigrations 在 try 块内,如果 seed 失败则迁移不执行 修复:将迁移逻辑移到独立的 try-catch 块,确保始终执行 添加详细日志帮助调试
95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
import mysql from 'mysql2/promise'
|
||
import pool from './connection'
|
||
import { readFileSync } from 'fs'
|
||
import { join } from 'path'
|
||
|
||
async function columnExists(conn: mysql.Connection, table: string, column: string): Promise<boolean> {
|
||
const [rows] = await conn.query(
|
||
`SELECT COUNT(*) as cnt FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?`,
|
||
[table, column]
|
||
)
|
||
return (rows as any[])[0].cnt > 0
|
||
}
|
||
|
||
async function runMigrations(conn: mysql.Connection) {
|
||
console.log('[DB] Checking migrations...')
|
||
|
||
// transactions 表添加 user_id
|
||
const hasTxUserId = await columnExists(conn, 'transactions', 'user_id')
|
||
console.log(`[DB] transactions.user_id exists: ${hasTxUserId}`)
|
||
if (!hasTxUserId) {
|
||
console.log('[DB] Migrating: adding user_id to transactions')
|
||
await conn.query('ALTER TABLE transactions ADD COLUMN user_id INT NOT NULL DEFAULT 1 AFTER id')
|
||
await conn.query('ALTER TABLE transactions ADD INDEX idx_user_date (user_id, date)')
|
||
await conn.query('ALTER TABLE transactions ADD INDEX idx_user_type_date (user_id, type, date)')
|
||
console.log('[DB] transactions.user_id added')
|
||
}
|
||
|
||
// budgets 表添加 user_id
|
||
const hasBudgetUserId = await columnExists(conn, 'budgets', 'user_id')
|
||
console.log(`[DB] budgets.user_id exists: ${hasBudgetUserId}`)
|
||
if (!hasBudgetUserId) {
|
||
console.log('[DB] Migrating: adding user_id to budgets')
|
||
await conn.query('ALTER TABLE budgets ADD COLUMN user_id INT NOT NULL DEFAULT 1 AFTER id')
|
||
await conn.query('ALTER TABLE budgets ADD UNIQUE KEY uk_user_month (user_id, month)')
|
||
console.log('[DB] budgets.user_id added')
|
||
}
|
||
}
|
||
|
||
export async function initDatabase() {
|
||
const dbName = process.env.DB_NAME || 'xiaocai'
|
||
|
||
// 先用不指定数据库的连接,确保数据库存在
|
||
const bootstrap = await mysql.createConnection({
|
||
host: process.env.DB_HOST || 'localhost',
|
||
user: process.env.DB_USER || 'xiaocai',
|
||
password: process.env.DB_PASSWORD || 'xiaocai123',
|
||
multipleStatements: true,
|
||
})
|
||
|
||
await bootstrap.query(`CREATE DATABASE IF NOT EXISTS \`${dbName}\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`)
|
||
await bootstrap.end()
|
||
|
||
// 用独立连接执行建表和填充(需要 multipleStatements)
|
||
const initConn = await mysql.createConnection({
|
||
host: process.env.DB_HOST || 'localhost',
|
||
user: process.env.DB_USER || 'xiaocai',
|
||
password: process.env.DB_PASSWORD || 'xiaocai123',
|
||
database: dbName,
|
||
multipleStatements: true,
|
||
})
|
||
|
||
try {
|
||
const schemaPath = join(__dirname, 'schema.sql')
|
||
const seedPath = join(__dirname, 'seed.sql')
|
||
|
||
const schema = readFileSync(schemaPath, 'utf-8')
|
||
const cleanSchema = schema
|
||
.replace(/CREATE DATABASE.*?;/gi, '')
|
||
.replace(/USE\s+\w+;/gi, '')
|
||
await initConn.query(cleanSchema)
|
||
|
||
const seed = readFileSync(seedPath, 'utf-8')
|
||
await initConn.query(seed)
|
||
|
||
console.log('[DB] Schema and seed applied')
|
||
} catch (err: any) {
|
||
if (err.code === 'ER_DUP_ENTRY' || err.errno === 1062) {
|
||
console.log('[DB] Already initialized, skipping seed')
|
||
} else {
|
||
console.error('[DB] Init warning:', err.message)
|
||
}
|
||
}
|
||
|
||
// 始终运行迁移(独立于 schema/seed)
|
||
try {
|
||
await runMigrations(initConn)
|
||
console.log('[DB] Migrations complete')
|
||
} catch (err: any) {
|
||
console.error('[DB] Migration error:', err.message)
|
||
}
|
||
|
||
await initConn.end()
|
||
console.log('[DB] Database ready')
|
||
}
|