"""SQLite 引擎、Session、Base、get_db 依赖。 自包含:无需外部 MySQL,容器内单文件 SQLite 即可。对齐 server/database.py 的接口形态, 但用 SQLite(本项目独立运行、无并发写入压力)。 """ from __future__ import annotations from pathlib import Path from typing import Generator from sqlalchemy import create_engine from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker from .config import get_settings _engine = None _SessionLocal = None def _db_path() -> Path: """SQLite 文件落在 work_dir 下,跟数据一起走 volume。""" s = get_settings() p = s.work_dir() / "audio2text.db" p.parent.mkdir(parents=True, exist_ok=True) return p def get_engine(): global _engine, _SessionLocal if _engine is None: url = f"sqlite:///{_db_path()}" _engine = create_engine( url, # 后台 reaper / 缓存清理线程与请求线程并发写同一库;busy_timeout 让等待方 # 在拿锁时阻塞 5s 而非立即报 database is locked。 connect_args={"check_same_thread": False, "timeout": 30}, future=True, ) _SessionLocal = sessionmaker(bind=_engine, autoflush=False, expire_on_commit=False) return _engine def get_session_local(): get_engine() return _SessionLocal class Base(DeclarativeBase): pass def init_db_schema() -> None: """建表(幂等)。""" from .models.task import Task # noqa: F401 from .models.upload_session import UploadSession # noqa: F401 get_engine() Base.metadata.create_all(get_engine()) def get_db() -> Generator[Session, None, None]: """FastAPI 依赖:每请求一个 Session,结束自动关闭。""" db = get_session_local()() try: yield db finally: db.close()