主要功能: - 图片上传时 OCR 内容去重(3个上传端点统一使用公共函数 _check_ocr_duplicate) - 图片管理 Tab:展示所有图片、手动删除、一键去重 - 搜索结果详情弹窗增加删除按钮(带确认弹窗) - 图片管理卡片点击查看详情(复用 showOcrDetailModal) - 搜索限制和 LLM 批量判断数量可通过网站设置 - MiniMax API 调用添加 reasoning_split=True - 企业微信机器人:WebSocket 长连接、图片搜索、配置化搜索数量 - 版本号升级至 1.2.0
108 lines
3.5 KiB
Python
108 lines
3.5 KiB
Python
"""
|
||
数据库连接模块
|
||
支持 PostgreSQL(生产)和 SQLite(本地开发)两种后端
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import AsyncGenerator
|
||
from pathlib import Path
|
||
|
||
from sqlalchemy import text
|
||
from sqlalchemy.ext.asyncio import (
|
||
AsyncSession,
|
||
async_sessionmaker,
|
||
create_async_engine,
|
||
)
|
||
from sqlalchemy.pool import NullPool
|
||
|
||
from app.config import settings
|
||
|
||
# ──────────────────────────── 判断数据库类型 ────────────────────────────
|
||
|
||
DB_URL = settings.DATABASE_URL
|
||
IS_SQLITE = DB_URL.startswith("sqlite")
|
||
|
||
# ──────────────────────────── 异步引擎 ────────────────────────────
|
||
|
||
if IS_SQLITE:
|
||
# SQLite 配置
|
||
sqlite_path = DB_URL.replace("sqlite+aiosqlite:///", "")
|
||
if not sqlite_path.startswith("/"):
|
||
sqlite_path = str(Path(__file__).parent.parent / sqlite_path)
|
||
|
||
engine = create_async_engine(
|
||
f"sqlite+aiosqlite:///{sqlite_path}",
|
||
echo=settings.DEBUG,
|
||
)
|
||
else:
|
||
# PostgreSQL 配置
|
||
engine = create_async_engine(
|
||
DB_URL,
|
||
echo=settings.DEBUG,
|
||
poolclass=NullPool,
|
||
pool_pre_ping=True,
|
||
)
|
||
|
||
# ──────────────────────────── 会话工厂 ────────────────────────────
|
||
|
||
async_session_factory = async_sessionmaker(
|
||
bind=engine,
|
||
class_=AsyncSession,
|
||
expire_on_commit=False,
|
||
autoflush=False,
|
||
)
|
||
|
||
|
||
# ──────────────────────────── 依赖注入 ────────────────────────────
|
||
|
||
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
||
"""
|
||
FastAPI 依赖项:获取异步数据库会话。
|
||
"""
|
||
async with async_session_factory() as session:
|
||
try:
|
||
yield session
|
||
await session.commit()
|
||
except Exception:
|
||
await session.rollback()
|
||
raise
|
||
|
||
|
||
# ──────────────────────────── 生命周期辅助 ────────────────────────────
|
||
|
||
async def init_db() -> None:
|
||
"""初始化数据库:建表"""
|
||
from app.models.base import Base
|
||
|
||
async with engine.begin() as conn:
|
||
if IS_SQLITE:
|
||
# SQLite 启用 WAL 模式和外键约束
|
||
await conn.execute(text("PRAGMA journal_mode=WAL"))
|
||
await conn.execute(text("PRAGMA foreign_keys=ON"))
|
||
# 创建所有表
|
||
await conn.run_sync(Base.metadata.create_all)
|
||
|
||
if not IS_SQLITE:
|
||
# PostgreSQL 需要创建扩展
|
||
async with engine.begin() as conn:
|
||
try:
|
||
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
|
||
except Exception:
|
||
pass
|
||
try:
|
||
await conn.execute(text("CREATE EXTENSION IF NOT EXISTS zhparser"))
|
||
except Exception:
|
||
pass
|
||
try:
|
||
await conn.execute(text(
|
||
"CREATE TEXT SEARCH CONFIGURATION IF NOT EXISTS chinese_zh (PARSER = zhparser)"
|
||
))
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
async def close_db() -> None:
|
||
"""关闭数据库引擎"""
|
||
await engine.dispose()
|