/** * @file Drizzle ORM服务类 * @author hotok * @date 2025-06-29 * @lastEditor hotok * @lastEditTime 2025-06-29 * @description 专业的Drizzle ORM数据库连接服务类,支持连接池管理、状态跟踪和优雅关闭 */ import { drizzle } from 'drizzle-orm/mysql2'; import mysql from 'mysql2/promise'; import { dbConfig } from '@/config'; import { Logger } from '@/plugins/logger/logger.service'; import * as schema from '../../eneities'; import type { DrizzleDB, ConnectionStatus, DatabaseConnectionInfo } from '@/type/drizzle.type'; /** * Drizzle数据库服务类 * 使用单例模式管理数据库连接 */ export class DrizzleService { /** 单例实例 */ private static instance: DrizzleService | null = null; /** 数据库实例 */ private _db: DrizzleDB | null = null; /** 连接池实例 */ private _connectionPool: mysql.Pool | null = null; /** 连接状态信息 */ private _connectionInfo: DatabaseConnectionInfo; /** 初始化标志 */ private _isInitialized = false; /** 连接池配置 */ private readonly _poolConfig = { /** 最大连接数 */ connectionLimit: Number(process.env.DB_CONNECTION_LIMIT) || 10, /** 队列限制 */ queueLimit: Number(process.env.DB_QUEUE_LIMIT) || 0, /** 等待连接 */ waitForConnections: true, // 启用此选项后,MySQL驱动程序将支持大数字(big numbers),这对于存储和处理 bigint 类型的数据尤为重要。 // 如果不启用此选项,MySQL驱动程序可能无法正确处理超过 JavaScript 数字精度范围的大数值,导致数据精度丢失。 supportBigNumbers: true, // 启用此选项后,MySQL驱动程序将在接收 bigint 或其他大数值时,将其作为字符串返回,而不是作为JavaScript数字。 // 这种处理方式可以避免JavaScript本身的数值精度限制问题,确保大数值在应用程序中保持精确。 bigNumberStrings: true, }; /** * 私有构造函数,防止外部实例化 */ private constructor() { this._connectionInfo = { status: 'disconnected', host: dbConfig.host, port: dbConfig.port, database: dbConfig.database, }; } /** * 获取单例实例 */ public static getInstance(): DrizzleService { if (!DrizzleService.instance) { DrizzleService.instance = new DrizzleService(); } return DrizzleService.instance; } /** * 获取数据库实例 */ public get db(): DrizzleDB { if (!this._db) { throw new Error('数据库未初始化,请先调用 initialize() 方法'); } return this._db; } /** * 获取连接状态信息 */ public get connectionInfo(): DatabaseConnectionInfo { return { ...this._connectionInfo }; } /** * 检查是否已初始化 */ public get isInitialized(): boolean { return this._isInitialized; } /** * 验证数据库配置 */ private validateConfig(): void { const requiredFields = ['host', 'port', 'user', 'password', 'database']; for (const field of requiredFields) { if (!dbConfig[field as keyof typeof dbConfig]) { throw new Error(`数据库配置缺少必需字段: ${field}`); } } if (dbConfig.port < 1 || dbConfig.port > 65535) { throw new Error(`数据库端口号无效: ${dbConfig.port}`); } } /** * 更新连接状态 */ private updateConnectionStatus(status: ConnectionStatus, error?: string): void { this._connectionInfo.status = status; this._connectionInfo.error = error; if (status === 'connected') { this._connectionInfo.connectedAt = new Date(); this._connectionInfo.error = undefined; } } /** * 创建MySQL连接池 */ private async createConnection(): Promise { try { this.validateConfig(); this.updateConnectionStatus('connecting'); /** MySQL连接池配置 */ const connection = mysql.createPool({ host: dbConfig.host, port: dbConfig.port, user: dbConfig.user, password: dbConfig.password, database: dbConfig.database, ...this._poolConfig, }); // 测试连接 const testConnection = await connection.getConnection(); await testConnection.ping(); testConnection.release(); this.updateConnectionStatus('connected'); Logger.info({ message: 'MySQL连接池创建成功', host: dbConfig.host, port: dbConfig.port, database: dbConfig.database, connectionLimit: this._poolConfig.connectionLimit, }); return connection; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.updateConnectionStatus('error', errorMessage); Logger.error(new Error(`MySQL连接池创建失败: ${errorMessage}`)); throw new Error(`MySQL连接池创建失败: ${errorMessage}`); } } /** * 初始化数据库连接 */ public async initialize(): Promise { // 防止重复初始化 if (this._isInitialized && this._db) { Logger.info('Drizzle ORM 已初始化,返回现有实例'); return this._db; } try { this._connectionPool = await this.createConnection(); console.log(process.env.NODE_ENV, process.env.NODE_ENV === 'development'); /** Drizzle数据库实例 */ this._db = drizzle(this._connectionPool, { schema, mode: 'default', logger: process.env.NODE_ENV === 'development' ? { logQuery: (query, params) => { Logger.debug({ type: 'SQL_QUERY', query: query.replace(/\s+/g, ' ').trim(), params: params, }); }, } : false, }); this._isInitialized = true; Logger.info({ message: 'Drizzle ORM 初始化成功', schema: Object.keys(schema).length > 0 ? Object.keys(schema) : ['无schema'], loggerEnabled: process.env.NODE_ENV === 'development', }); return this._db; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.updateConnectionStatus('error', errorMessage); Logger.error(new Error(`Drizzle ORM 初始化失败: ${errorMessage}`)); throw new Error(`Drizzle ORM 初始化失败: ${errorMessage}`); } } /** * 检查数据库连接状态 */ public async checkConnection(): Promise { try { if (!this._connectionPool) { return false; } const connection = await this._connectionPool.getConnection(); await connection.ping(); connection.release(); return true; } catch (error) { Logger.error(error instanceof Error ? error : new Error('数据库连接检查失败')); return false; } } /** * 优雅关闭数据库连接 */ public async close(): Promise { try { if (this._connectionPool) { await this._connectionPool.end(); this._connectionPool = null; this._db = null; this.updateConnectionStatus('disconnected'); this._isInitialized = false; Logger.info('数据库连接已关闭'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); Logger.error(new Error(`关闭数据库连接时出错: ${errorMessage}`)); throw new Error(`关闭数据库连接失败: ${errorMessage}`); } } /** * 重新连接数据库 */ public async reconnect(): Promise { Logger.info('正在重新连接数据库...'); // 先关闭现有连接 await this.close(); // 重新初始化连接 return await this.initialize(); } /** * 获取连接池统计信息 */ public getPoolStats(): { connectionLimit: number; queueLimit: number; waitForConnections: boolean; } | null { if (!this._connectionPool) { return null; } return { connectionLimit: this._poolConfig.connectionLimit, queueLimit: this._poolConfig.queueLimit, waitForConnections: this._poolConfig.waitForConnections, }; } /** * 执行健康检查 */ public async healthCheck(): Promise<{ status: 'healthy' | 'unhealthy'; connectionInfo: DatabaseConnectionInfo; isConnected: boolean; poolStats?: ReturnType; }> { const isConnected = await this.checkConnection(); return { status: isConnected ? 'healthy' : 'unhealthy', connectionInfo: this.connectionInfo, isConnected, poolStats: this.getPoolStats(), }; } } /** * ============================================== * 主要导出 - 推荐使用的API * ============================================== */ /** * Drizzle服务单例实例 * * @description 获取DrizzleService的单例实例,推荐的使用方式 * @example * ```typescript * import { drizzleService } from '@/plugins/drizzle/drizzle.service'; * * // 初始化数据库 * await drizzleService.initialize(); * * // 获取数据库实例 * const database = drizzleService.db; * * // 检查连接状态 * const isConnected = await drizzleService.checkConnection(); * ``` */ export const drizzleService = DrizzleService.getInstance(); /** * ============================================== * 向后兼容导出 - 保持原有函数式API * ============================================== */ /** * 创建并初始化Drizzle数据库连接 * * @description 向后兼容的初始化方法,内部调用drizzleService.initialize() * @returns {Promise} 返回初始化后的Drizzle数据库实例 * * @example * ```typescript * import { createDrizzleDB } from '@/plugins/drizzle/drizzle.service'; * * const database = await createDrizzleDB(); * ``` * * @deprecated 推荐使用 drizzleService.initialize() 替代 */ export const createDrizzleDB = () => drizzleService.initialize(); /** * 获取数据库连接状态信息 * * @description 向后兼容的状态获取方法,内部调用drizzleService.connectionInfo * @returns {DatabaseConnectionInfo} 返回数据库连接状态信息 * * @example * ```typescript * import { getConnectionInfo } from '@/plugins/drizzle/drizzle.service'; * * const info = getConnectionInfo(); * console.log(`数据库状态: ${info.status}`); * ``` * * @deprecated 推荐使用 drizzleService.connectionInfo 替代 */ export const getConnectionInfo = () => drizzleService.connectionInfo; /** * 检查数据库连接状态 * * @description 向后兼容的连接检查方法,内部调用drizzleService.checkConnection() * @returns {Promise} 返回连接是否正常 * * @example * ```typescript * import { checkConnection } from '@/plugins/drizzle/drizzle.service'; * * const isConnected = await checkConnection(); * if (!isConnected) { * console.log('数据库连接异常'); * } * ``` * * @deprecated 推荐使用 drizzleService.checkConnection() 替代 */ export const checkConnection = () => drizzleService.checkConnection(); /** * 优雅关闭数据库连接 * * @description 向后兼容的连接关闭方法,内部调用drizzleService.close() * @returns {Promise} 返回关闭操作的Promise * * @example * ```typescript * import { closeDrizzleDB } from '@/plugins/drizzle/drizzle.service'; * * // 应用关闭时清理资源 * process.on('SIGTERM', async () => { * await closeDrizzleDB(); * process.exit(0); * }); * ``` * * @deprecated 推荐使用 drizzleService.close() 替代 */ export const closeDrizzleDB = () => drizzleService.close(); /** * 重新连接数据库 * * @description 向后兼容的重连方法,内部调用drizzleService.reconnect() * @returns {Promise} 返回重新连接后的数据库实例 * * @example * ```typescript * import { reconnectDB } from '@/plugins/drizzle/drizzle.service'; * * try { * const database = await reconnectDB(); * console.log('数据库重连成功'); * } catch (error) { * console.error('数据库重连失败:', error); * } * ``` * * @deprecated 推荐使用 drizzleService.reconnect() 替代 */ export const reconnectDB = () => drizzleService.reconnect(); /** * 获取数据库实例 * * @description 向后兼容的数据库实例获取方法,内部调用drizzleService.db * @returns {DrizzleDB} 返回Drizzle数据库实例 * @throws {Error} 如果数据库未初始化则抛出错误 * * @example * ```typescript * import { db } from '@/plugins/drizzle/drizzle.service'; * * // 确保先初始化 * await createDrizzleDB(); * * // 获取数据库实例 * const database = db(); * const users = await database.select().from(usersTable); * ``` * * @deprecateds 推荐使用 drizzleService.db 替代 */ export const db = () => drizzleService.db;