chore: sync database worker branch with upstream v3

This commit is contained in:
InfinityPacer
2026-08-23 04:48:50 +08:00
111 changed files with 605 additions and 391 deletions
+8
View File
@@ -43,6 +43,14 @@ def run_legacy_sync_query(operation: Callable[[Session], _R]) -> _R:
except Exception as close_err: # noqa: BLE001 兼容查询释放失败不改变返回语义
logger.error(f"释放数据库会话失败:{close_err}")
async def run_legacy_async_query(
operation: Callable[[AsyncSession], Awaitable[_R]],
) -> _R:
"""为移除异步查询装饰器的旧 Model ABI 提供一次性异步会话。"""
async with async_session_scope() as db:
return await operation(db)
def _get_args_db(
args: tuple[Any, ...],
kwargs: dict[str, Any],
+53 -13
View File
@@ -4,7 +4,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, Session, mapped_column
from app.db.base import get_id_column, Base
from app.db.decorators import db_query, async_db_query
from app.db.decorators import run_legacy_async_query, run_legacy_sync_query
class PluginData(Base):
@@ -21,28 +21,54 @@ class PluginData(Base):
)
@classmethod
@db_query
def get_plugin_data(cls, db: Session, plugin_id: str):
def get_plugin_data(cls, db: Session | None = None, plugin_id: str | None = None):
"""在调用方 Session 中读取插件全部数据,并兼容旧无会话入口。"""
if plugin_id is None:
raise TypeError("plugin_id is required")
if not isinstance(db, Session):
return run_legacy_sync_query(lambda session: cls.get_plugin_data(session, plugin_id))
return list(db.execute(select(cls).where(cls.plugin_id == plugin_id)).scalars().all())
@classmethod
@async_db_query
async def async_get_plugin_data(cls, db: AsyncSession, plugin_id: str):
async def async_get_plugin_data(
cls, db: AsyncSession | None = None, plugin_id: str | None = None
):
"""在调用方 AsyncSession 中读取插件全部数据,并兼容旧无会话入口。"""
if plugin_id is None:
raise TypeError("plugin_id is required")
if not isinstance(db, AsyncSession):
return await run_legacy_async_query(
lambda session: cls.async_get_plugin_data(session, plugin_id)
)
result = await db.execute(select(cls).where(cls.plugin_id == plugin_id))
return list(result.scalars().all())
@classmethod
@db_query
def get_plugin_data_by_key(cls, db: Session, plugin_id: str, key: str):
def get_plugin_data_by_key(
cls, db: Session | None = None, plugin_id: str | None = None, key: str | None = None
):
"""在调用方 Session 中按键读取插件数据,并兼容旧无会话入口。"""
if plugin_id is None or key is None:
raise TypeError("plugin_id and key are required")
if not isinstance(db, Session):
return run_legacy_sync_query(
lambda session: cls.get_plugin_data_by_key(session, plugin_id, key)
)
return db.execute(
select(cls).where(cls.plugin_id == plugin_id, cls.key == key)
).scalars().first()
@classmethod
@async_db_query
async def async_get_plugin_data_by_key(
cls, db: AsyncSession, plugin_id: str, key: str
cls, db: AsyncSession | None = None, plugin_id: str | None = None, key: str | None = None
):
"""在调用方 AsyncSession 中按键读取插件数据,并兼容旧无会话入口。"""
if plugin_id is None or key is None:
raise TypeError("plugin_id and key are required")
if not isinstance(db, AsyncSession):
return await run_legacy_async_query(
lambda session: cls.async_get_plugin_data_by_key(session, plugin_id, key)
)
result = await db.execute(
select(cls).where(cls.plugin_id == plugin_id, cls.key == key)
)
@@ -59,14 +85,28 @@ class PluginData(Base):
db.execute(delete(cls).where(cls.plugin_id == plugin_id))
@classmethod
@db_query
def get_plugin_data_by_plugin_id(cls, db: Session, plugin_id: str):
def get_plugin_data_by_plugin_id(
cls, db: Session | None = None, plugin_id: str | None = None
):
"""在调用方 Session 中按插件 ID 读取数据,并兼容旧无会话入口。"""
if plugin_id is None:
raise TypeError("plugin_id is required")
if not isinstance(db, Session):
return run_legacy_sync_query(
lambda session: cls.get_plugin_data_by_plugin_id(session, plugin_id)
)
return list(db.execute(select(cls).where(cls.plugin_id == plugin_id)).scalars().all())
@classmethod
@async_db_query
async def async_get_plugin_data_by_plugin_id(
cls, db: AsyncSession, plugin_id: str
cls, db: AsyncSession | None = None, plugin_id: str | None = None
):
"""在调用方 AsyncSession 中按插件 ID 读取数据,并兼容旧无会话入口。"""
if plugin_id is None:
raise TypeError("plugin_id is required")
if not isinstance(db, AsyncSession):
return await run_legacy_async_query(
lambda session: cls.async_get_plugin_data_by_plugin_id(session, plugin_id)
)
result = await db.execute(select(cls).where(cls.plugin_id == plugin_id))
return list(result.scalars().all())
+19 -6
View File
@@ -4,7 +4,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, Session, mapped_column
from app.db.base import Base, get_id_column
from app.db.decorators import db_query, async_db_query
from app.db.decorators import run_legacy_async_query
class SiteIcon(Base):
@@ -22,12 +22,25 @@ class SiteIcon(Base):
base64: Mapped[Optional[str]] = mapped_column(String)
@classmethod
@db_query
def get_by_domain(cls, db: Session, domain: str):
"""在调用方 Session 中查询站点图标。"""
return db.execute(select(cls).where(cls.domain == domain)).scalars().first()
@classmethod
@async_db_query
async def async_get_by_domain(cls, db: AsyncSession, domain: str):
result = await db.execute(select(cls).where(cls.domain == domain))
return result.scalar_one_or_none()
async def async_get_by_domain(
cls,
db: AsyncSession | None = None,
domain: str | None = None,
):
"""在调用方 AsyncSession 中查询站点图标。"""
if domain is None:
raise TypeError("domain is required")
async def query(session: AsyncSession):
"""在给定异步会话中执行站点图标查询。"""
result = await session.execute(select(cls).where(cls.domain == domain))
return result.scalar_one_or_none()
if isinstance(db, AsyncSession):
return await query(db)
return await run_legacy_async_query(query)
+19 -6
View File
@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, Session, mapped_column
from app.db.base import get_id_column, Base
from app.db.decorators import db_query, async_db_query
from app.db.decorators import run_legacy_async_query
class SiteStatistic(Base):
@@ -30,15 +30,28 @@ class SiteStatistic(Base):
note: Mapped[Optional[Any]] = mapped_column(JSON)
@classmethod
@db_query
def get_by_domain(cls, db: Session, domain: str):
"""在调用方 Session 中查询站点统计。"""
return db.execute(select(cls).where(cls.domain == domain)).scalars().first()
@classmethod
@async_db_query
async def async_get_by_domain(cls, db: AsyncSession, domain: str):
result = await db.execute(select(cls).where(cls.domain == domain))
return result.scalar_one_or_none()
async def async_get_by_domain(
cls,
db: AsyncSession | None = None,
domain: str | None = None,
):
"""在调用方 AsyncSession 中查询站点统计,并兼容旧无会话调用。"""
if domain is None:
raise TypeError("domain is required")
async def query(session: AsyncSession):
"""在给定异步会话中执行站点统计查询。"""
result = await session.execute(select(cls).where(cls.domain == domain))
return result.scalar_one_or_none()
if isinstance(db, AsyncSession):
return await query(db)
return await run_legacy_async_query(query)
@classmethod
def reset(cls, db: Session):
+1 -2
View File
@@ -3,7 +3,6 @@ from sqlalchemy import String, UniqueConstraint, JSON, select
from sqlalchemy.orm import Mapped, Session, mapped_column
from app.db.base import get_id_column, Base
from app.db.decorators import db_query
class UserConfig(Base):
@@ -24,8 +23,8 @@ class UserConfig(Base):
)
@classmethod
@db_query
def get_by_key(cls, db: Session, username: str, key: str):
"""在调用方 Session 中查询用户配置。"""
return db.execute(
select(cls).where(cls.username == username, cls.key == key)
).scalars().first()