# ============================================================================= # 企微IT智能服务台 — 数据库连接与 Session 管理 # ============================================================================= # 说明:使用 SQLAlchemy 2.0 的异步引擎和会话管理,负责: # 1. 创建异步数据库引擎(懒加载,支持 PostgreSQL 和 SQLite) # 2. 创建异步会话工厂 # 3. 提供 get_db 依赖注入函数(FastAPI 路由中使用) # 4. 自动建表(SQLite 本地开发时自动创建所有表) # ============================================================================= import logging from typing import AsyncGenerator, Optional from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from sqlalchemy.orm import DeclarativeBase # 导入配置(读取数据库连接地址) from app.config import settings from app.utils.response import AppException logger = logging.getLogger(__name__) # ---------------------------------------------------------------------- # 声明式基类(单独定义,不依赖引擎创建) # ---------------------------------------------------------------------- # 所有模型类都继承自 Base,SQLAlchemy 通过它来检测所有模型定义 # Alembic 也通过 Base.metadata 来生成迁移脚本 # ---------------------------------------------------------------------- class Base(DeclarativeBase): """SQLAlchemy 声明式基类。 所有模型类都继承此类,SQLAlchemy 通过它管理所有表的元数据。 """ pass # ---------------------------------------------------------------------- # 懒加载引擎和会话工厂 # ---------------------------------------------------------------------- _engine: Optional[object] = None _async_session_factory: Optional[async_sessionmaker] = None _tables_created: bool = False # 标记是否已自动建表 def _is_sqlite() -> bool: """判断当前数据库 URL 是否为 SQLite。""" return "sqlite" in settings.database_url.lower() def _get_engine(): """懒加载获取数据库引擎。 支持 PostgreSQL 和 SQLite 两种后端: - PostgreSQL: 使用 asyncpg 异步驱动,带连接池 - SQLite: 使用 aiosqlite 异步驱动,无需连接池 """ global _engine if _engine is None: db_url = settings.database_url if _is_sqlite(): # SQLite 异步驱动:aiosqlite # 不需要连接池,SQLite 是单文件数据库 _engine = create_async_engine( db_url, echo=False, ) logger.info(f"使用 SQLite 数据库: {db_url}") else: # PostgreSQL 异步驱动:asyncpg _engine = create_async_engine( db_url.replace("postgresql://", "postgresql+asyncpg://"), echo=False, pool_size=5, max_overflow=10, pool_pre_ping=True, ) logger.info(f"使用 PostgreSQL 数据库: {db_url.split('@')[-1]}") return _engine def _get_session_factory() -> async_sessionmaker: """懒加载获取会话工厂。""" global _async_session_factory if _async_session_factory is None: _async_session_factory = async_sessionmaker( _get_engine(), class_=AsyncSession, expire_on_commit=False, ) return _async_session_factory async def _ensure_tables(): """自动建表(仅 SQLite 本地开发时使用)。 PostgreSQL 环境应使用 Alembic 迁移管理表结构。 SQLite 环境下直接用 metadata.create_all 创建所有表,省去迁移步骤。 """ global _tables_created if _tables_created: return _tables_created = True if _is_sqlite(): # 导入所有模型,确保 Base.metadata 知道所有表 import app.models # noqa: F401 engine = _get_engine() async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) logger.info("SQLite 自动建表完成") async def get_db() -> AsyncGenerator[AsyncSession, None]: """获取数据库会话的依赖注入函数。 在 FastAPI 路由中通过 Depends(get_db) 注入数据库会话。 使用 async with 确保会话在使用后正确关闭。 使用 try/finally 确保异常时也能回滚和关闭。 Yields: AsyncSession: 异步数据库会话 """ # 首次调用时自动建表(SQLite) await _ensure_tables() # 创建一个新的数据库会话(懒加载会话工厂) try: session_factory = _get_session_factory() except Exception as e: logger.error(f"数据库连接失败(无法创建会话工厂): {e}") raise AppException(1006, f"数据库连接失败: {str(e)}") async with session_factory() as session: try: # 将会话交给路由函数使用 yield session # 路由函数执行成功后提交事务 await session.commit() except Exception: # 路由函数执行失败时回滚事务 await session.rollback() # 重新抛出异常,让 FastAPI 的异常处理器处理 raise