"""SQLAlchemy engine / Session 工厂与 FastAPI 依赖。 注意:本文件属于 Infrastructure 层,是唯一允许出现 engine 创建的地方之一。 业务 Service / Domain 禁止直接使用 Session —— 一律通过 Repository。 """ from __future__ import annotations from collections.abc import Generator from sqlalchemy import create_engine, event from sqlalchemy.orm import Session, sessionmaker from app.core.config import get_settings _settings = get_settings() # SQLite 写锁等待加固:全市场同步等长写事务进行时,读接口等待而非立刻 # "database is locked";另在连接上设置 busy_timeout。WAL 迁移留作后续专项 # (需在无活跃写事务时统一切换,避免混合 journal 模式)。 _connect_args: dict = {} if _settings.database_url.startswith("sqlite"): _connect_args["timeout"] = 30 engine = create_engine( _settings.database_url, echo=_settings.sqlalchemy_echo, future=True, connect_args=_connect_args, ) @event.listens_for(engine, "connect") def _set_sqlite_pragmas(dbapi_conn, _record) -> None: # pragma: no cover —— 仅 SQLite 路径 if _settings.database_url.startswith("sqlite"): cur = dbapi_conn.cursor() cur.execute("PRAGMA busy_timeout = 30000") cur.close() SessionLocal = sessionmaker( bind=engine, autoflush=False, expire_on_commit=False, class_=Session, ) def get_session() -> Generator[Session, None, None]: """FastAPI 依赖:每个请求一个 Session,请求结束自动关闭。 供 Repository 实现内部使用;业务层通过 Repository 间接访问。 """ with SessionLocal() as session: yield session