from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from config import settings from utils.logger import session_logger import asyncio import urllib.parse def _build_connection_string() -> str: encoded_password = urllib.parse.quote_plus(settings.db_password) return f"mysql+aiomysql://{settings.db_user}:{encoded_password}@{settings.db_host}:{settings.db_port}/{settings.db_name}?charset=utf8mb4" def _build_sync_connection_string() -> str: encoded_password = urllib.parse.quote_plus(settings.db_password) return f"mysql+pymysql://{settings.db_user}:{encoded_password}@{settings.db_host}:{settings.db_port}/{settings.db_name}?charset=utf8mb4" class DatabaseManager: _instance = None _lock = asyncio.Lock() def __init__(self): self.async_engine = None self.sync_engine = None self.async_session = None self.sync_session_maker = None self._initialized = False @classmethod async def get_instance(cls): async with cls._lock: if cls._instance is None: cls._instance = DatabaseManager() if not cls._instance._initialized: await cls._instance.initialize() return cls._instance async def initialize(self): if self._initialized: return try: connection_string = _build_connection_string() self.async_engine = create_async_engine( connection_string, echo=settings.db_echo, pool_pre_ping=True, pool_recycle=3600, ) self.async_session = sessionmaker( self.async_engine, expire_on_commit=False, class_=AsyncSession ) sync_connection_string = _build_sync_connection_string() self.sync_engine = create_engine( sync_connection_string, echo=settings.db_echo, pool_pre_ping=True, pool_recycle=3600, ) self.sync_session_maker = sessionmaker( bind=self.sync_engine, expire_on_commit=False ) self._initialized = True session_logger.info("system", "database", f"数据库连接初始化成功: {settings.db_host}:{settings.db_port}/{settings.db_name}") except Exception as e: session_logger.error("system", "database", f"数据库连接初始化失败: {str(e)}") raise async def get_session(self): """获取异步数据库会话""" if not self._initialized: await self.initialize() return self.async_session() def get_sync_session(self): """获取同步数据库会话""" if not self._initialized: raise RuntimeError("DatabaseManager not initialized. Call get_instance() first.") return self.sync_session_maker() async def close(self): """关闭数据库连接""" if self.async_engine: await self.async_engine.dispose() if self.sync_engine: self.sync_engine.dispose() self._initialized = False session_logger.info("system", "database", "数据库连接已关闭") db_manager = None async def get_db_manager(): global db_manager if db_manager is None: db_manager = await DatabaseManager.get_instance() return db_manager def get_sync_session_maker(): """获取同步 session maker(同步版本,优先复用已有 manager)""" global db_manager if db_manager is not None and db_manager._initialized: return db_manager.sync_session_maker sync_engine = create_engine( _build_sync_connection_string(), echo=settings.db_echo, pool_pre_ping=True, pool_recycle=3600, ) return sessionmaker(bind=sync_engine, expire_on_commit=False)