// ===================================================== // 資料庫連接配置 (整合備援功能) // ===================================================== import mysql from 'mysql2/promise'; import { dbFailover } from './database-failover'; import { dbSync } from './database-sync'; // 資料庫配置 const dbConfig = { host: process.env.DB_HOST || 'mysql.theaken.com', port: parseInt(process.env.DB_PORT || '33306'), user: process.env.DB_USER || 'AI_Platform', password: process.env.DB_PASSWORD || 'Aa123456', database: process.env.DB_NAME || 'db_AI_Platform', charset: 'utf8mb4', timezone: '+08:00', acquireTimeout: 60000, timeout: 60000, reconnect: true, connectionLimit: 10, queueLimit: 0, // 添加連接重試和錯誤處理配置 retryDelay: 2000, maxRetries: 3, // 添加連接池配置 idleTimeout: 300000, maxIdle: 10, // 添加 SSL 配置(如果需要) ssl: false as any, }; // 創建連接池 const pool = mysql.createPool(dbConfig); // 資料庫連接類 (整合備援功能) export class Database { private static instance: Database; private pool: mysql.Pool; private useFailover: boolean; private constructor() { this.pool = pool; this.useFailover = process.env.DB_FAILOVER_ENABLED === 'true'; } public static getInstance(): Database { if (!Database.instance) { Database.instance = new Database(); } return Database.instance; } // 獲取連接 public async getConnection(): Promise { if (this.useFailover) { return await dbFailover.getConnection(); } return await this.pool.getConnection(); } // 執行查詢 public async query(sql: string, params?: any[]): Promise { if (this.useFailover) { return await dbFailover.query(sql, params); } let connection; let retries = 0; const maxRetries = 3; while (retries < maxRetries) { try { connection = await this.getConnection(); const [rows] = await connection.execute(sql, params); return rows as T[]; } catch (error: any) { console.error(`資料庫查詢錯誤 (嘗試 ${retries + 1}/${maxRetries}):`, error.message); if (connection) { connection.release(); } if (error.code === 'ECONNRESET' || error.code === 'PROTOCOL_CONNECTION_LOST') { retries++; if (retries < maxRetries) { console.log(`等待 ${2000 * retries}ms 後重試...`); await new Promise(resolve => setTimeout(resolve, 2000 * retries)); continue; } } throw error; } finally { if (connection) { connection.release(); } } } throw new Error('資料庫連接失敗,已達到最大重試次數'); } // 執行單一查詢 public async queryOne(sql: string, params?: any[]): Promise { if (this.useFailover) { return await dbFailover.queryOne(sql, params); } try { const results = await this.query(sql, params); return results.length > 0 ? results[0] : null; } catch (error) { console.error('資料庫單一查詢錯誤:', error); throw error; } } // 執行插入(支援雙寫) public async insert(sql: string, params?: any[]): Promise { if (this.useFailover) { // 檢查是否啟用雙寫 const syncStatus = await dbSync.getSyncStatus(); if (syncStatus.enabled) { const result = await dbSync.dualInsert(sql, params); if (result.success) { // 返回主機的結果(如果主機成功) if (result.masterSuccess) { return await dbFailover.insert(sql, params); } else if (result.slaveSuccess) { // 如果只有備機成功,返回備機結果 return await dbFailover.insert(sql, params); } } throw new Error(`雙寫失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`); } else { return await dbFailover.insert(sql, params); } } const connection = await this.getConnection(); try { const [result] = await connection.execute(sql, params); return result as mysql.ResultSetHeader; } finally { connection.release(); } } // 執行更新(支援雙寫) public async update(sql: string, params?: any[]): Promise { if (this.useFailover) { // 檢查是否啟用雙寫 const syncStatus = await dbSync.getSyncStatus(); if (syncStatus.enabled) { const result = await dbSync.dualUpdate(sql, params); if (result.success) { // 返回主機的結果(如果主機成功) if (result.masterSuccess) { return await dbFailover.update(sql, params); } else if (result.slaveSuccess) { // 如果只有備機成功,返回備機結果 return await dbFailover.update(sql, params); } } throw new Error(`雙寫失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`); } else { return await dbFailover.update(sql, params); } } const connection = await this.getConnection(); try { const [result] = await connection.execute(sql, params); return result as mysql.ResultSetHeader; } finally { connection.release(); } } // 執行刪除(支援雙寫) public async delete(sql: string, params?: any[]): Promise { if (this.useFailover) { // 檢查是否啟用雙寫 const syncStatus = await dbSync.getSyncStatus(); if (syncStatus.enabled) { const result = await dbSync.dualDelete(sql, params); if (result.success) { // 返回主機的結果(如果主機成功) if (result.masterSuccess) { return await dbFailover.delete(sql, params); } else if (result.slaveSuccess) { // 如果只有備機成功,返回備機結果 return await dbFailover.delete(sql, params); } } throw new Error(`雙寫失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`); } else { return await dbFailover.delete(sql, params); } } const connection = await this.getConnection(); try { const [result] = await connection.execute(sql, params); return result as mysql.ResultSetHeader; } finally { connection.release(); } } // 開始事務 public async beginTransaction(): Promise { if (this.useFailover) { return await dbFailover.beginTransaction(); } const connection = await this.getConnection(); await connection.beginTransaction(); return connection; } // 提交事務 public async commit(connection: mysql.PoolConnection): Promise { if (this.useFailover) { return await dbFailover.commit(connection); } await connection.commit(); connection.release(); } // 回滾事務 public async rollback(connection: mysql.PoolConnection): Promise { if (this.useFailover) { return await dbFailover.rollback(connection); } await connection.rollback(); connection.release(); } // 獲取備援狀態 public getFailoverStatus() { if (this.useFailover) { return dbFailover.getStatus(); } return null; } // 切換資料庫 public async switchDatabase(database: 'master' | 'slave'): Promise { if (this.useFailover) { return await dbFailover.switchToDatabase(database); } return false; } // 關閉連接池 public async close(): Promise { if (this.useFailover) { await dbFailover.close(); } await this.pool.end(); } } // 導出單例實例 export const db = Database.getInstance(); // 導出類型 export type { PoolConnection } from 'mysql2/promise';