feat: v1.2.0 - 图片去重与管理、微信机器人优化、搜索设置可配置

主要功能:
- 图片上传时 OCR 内容去重(3个上传端点统一使用公共函数 _check_ocr_duplicate)
- 图片管理 Tab:展示所有图片、手动删除、一键去重
- 搜索结果详情弹窗增加删除按钮(带确认弹窗)
- 图片管理卡片点击查看详情(复用 showOcrDetailModal)
- 搜索限制和 LLM 批量判断数量可通过网站设置
- MiniMax API 调用添加 reasoning_split=True
- 企业微信机器人:WebSocket 长连接、图片搜索、配置化搜索数量
- 版本号升级至 1.2.0
This commit is contained in:
EduBrain Dev
2026-04-13 22:25:08 +08:00
commit b17786b57b
56 changed files with 9300 additions and 0 deletions

107
app/database.py Normal file
View File

@@ -0,0 +1,107 @@
"""
数据库连接模块
支持 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()