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 { 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') }