mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor: govern background tasks and query ownership
This commit is contained in:
+73
-48
@@ -5,7 +5,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Mapped, Session, mapped_column
|
||||
|
||||
from app.db.base import Base, execute_dml, get_id_column
|
||||
from app.db.decorators import async_db_query, db_query
|
||||
from app.db.decorators import run_legacy_async_query, run_legacy_sync_query
|
||||
|
||||
|
||||
class Message(Base):
|
||||
@@ -49,21 +49,32 @@ class Message(Base):
|
||||
return self.to_dict()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def list_by_page(cls, db: Session, page: int = 1, count: int = 30) -> List["Message"]:
|
||||
def list_by_page(
|
||||
cls,
|
||||
db: Session | None = None,
|
||||
page: int = 1,
|
||||
count: int = 30,
|
||||
) -> List["Message"]:
|
||||
"""
|
||||
分页获取消息记录。
|
||||
分页获取消息记录,兼容显式会话和旧插件无会话调用。
|
||||
"""
|
||||
return list(db.execute(
|
||||
select(cls)
|
||||
.order_by(cls.reg_time.desc(), cls.id.desc())
|
||||
.offset((page - 1) * count)
|
||||
.limit(count)
|
||||
).scalars().all())
|
||||
def query(session: Session) -> List["Message"]:
|
||||
"""在给定同步会话中执行消息分页查询。"""
|
||||
return list(session.execute(
|
||||
select(cls)
|
||||
.order_by(cls.reg_time.desc(), cls.id.desc())
|
||||
.offset((page - 1) * count)
|
||||
.limit(count)
|
||||
).scalars().all())
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def exists_by_source(cls, db: Session, source: str) -> bool:
|
||||
def exists_by_source(
|
||||
cls,
|
||||
db: Session | str | None = None,
|
||||
source: str | None = None,
|
||||
) -> bool:
|
||||
"""
|
||||
判断指定来源标识的消息记录是否存在。
|
||||
|
||||
@@ -71,31 +82,42 @@ class Message(Base):
|
||||
:param source: 消息来源唯一标识
|
||||
:return: 是否存在匹配记录
|
||||
"""
|
||||
return db.execute(
|
||||
select(cls.id).where(cls.source == source).limit(1)
|
||||
).scalars().first() is not None
|
||||
if source is None and isinstance(db, str):
|
||||
source, db = db, None
|
||||
if source is None:
|
||||
raise TypeError("source is required")
|
||||
|
||||
def query(session: Session) -> bool:
|
||||
"""在给定同步会话中执行来源存在性查询。"""
|
||||
return session.execute(
|
||||
select(cls.id).where(cls.source == source).limit(1)
|
||||
).scalars().first() is not None
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_by_page(
|
||||
cls, db: AsyncSession, page: int = 1, count: int = 30
|
||||
cls, db: AsyncSession | None = None, page: int = 1, count: int = 30
|
||||
) -> List["Message"]:
|
||||
"""
|
||||
异步分页获取消息记录。
|
||||
"""
|
||||
result = await db.execute(
|
||||
select(cls)
|
||||
.order_by(cls.reg_time.desc(), cls.id.desc())
|
||||
.offset((page - 1) * count)
|
||||
.limit(count)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
async def query(session: AsyncSession) -> List["Message"]:
|
||||
"""在给定异步会话中执行消息分页查询。"""
|
||||
result = await session.execute(
|
||||
select(cls)
|
||||
.order_by(cls.reg_time.desc(), cls.id.desc())
|
||||
.offset((page - 1) * count)
|
||||
.limit(count)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_sent_by_page(
|
||||
cls,
|
||||
db: AsyncSession,
|
||||
db: AsyncSession | None = None,
|
||||
page: int = 1,
|
||||
count: int = 30,
|
||||
all_clear_before: Optional[str] = None,
|
||||
@@ -105,32 +127,35 @@ class Message(Base):
|
||||
"""
|
||||
分页获取系统发送的通知消息。
|
||||
"""
|
||||
statement = select(cls).where(cls.action == 1)
|
||||
if all_clear_before:
|
||||
statement = statement.where(cls.reg_time > all_clear_before)
|
||||
if system_clear_before:
|
||||
statement = statement.where(
|
||||
or_(
|
||||
and_(cls.image.isnot(None), cls.image != ""),
|
||||
cls.reg_time > system_clear_before,
|
||||
async def query(session: AsyncSession) -> List["Message"]:
|
||||
"""在给定异步会话中执行通知消息分页查询。"""
|
||||
statement = select(cls).where(cls.action == 1)
|
||||
if all_clear_before:
|
||||
statement = statement.where(cls.reg_time > all_clear_before)
|
||||
if system_clear_before:
|
||||
statement = statement.where(
|
||||
or_(
|
||||
and_(cls.image.isnot(None), cls.image != ""),
|
||||
cls.reg_time > system_clear_before,
|
||||
)
|
||||
)
|
||||
)
|
||||
if media_clear_before:
|
||||
statement = statement.where(
|
||||
or_(
|
||||
cls.image.is_(None),
|
||||
cls.image == "",
|
||||
cls.reg_time > media_clear_before,
|
||||
if media_clear_before:
|
||||
statement = statement.where(
|
||||
or_(
|
||||
cls.image.is_(None),
|
||||
cls.image == "",
|
||||
cls.reg_time > media_clear_before,
|
||||
)
|
||||
)
|
||||
result = await session.execute(
|
||||
statement
|
||||
.order_by(cls.reg_time.desc(), cls.id.desc())
|
||||
.offset((page - 1) * count)
|
||||
.limit(count)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
result = await db.execute(
|
||||
statement
|
||||
.order_by(cls.reg_time.desc(), cls.id.desc())
|
||||
.offset((page - 1) * count)
|
||||
.limit(count)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
def delete_before(
|
||||
|
||||
+95
-29
@@ -6,7 +6,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, run_legacy_sync_query
|
||||
|
||||
|
||||
class Site(Base):
|
||||
@@ -58,48 +58,114 @@ class Site(Base):
|
||||
downloader: Mapped[Optional[str]] = mapped_column(String)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by_domain(cls, db: Session, domain: str):
|
||||
return db.execute(select(cls).where(cls.domain == domain)).scalars().first()
|
||||
def get_by_domain(cls, db: Session | str | None = None, domain: str | None = None):
|
||||
"""按域名查询站点,兼容显式会话和旧插件无会话调用。"""
|
||||
if domain is None and isinstance(db, str):
|
||||
domain, db = db, None
|
||||
if domain is None:
|
||||
raise TypeError("domain is required")
|
||||
|
||||
def query(session: Session):
|
||||
"""在给定同步会话中执行域名查询。"""
|
||||
return session.execute(select(cls).where(cls.domain == domain)).scalars().first()
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@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 | str | None = None,
|
||||
domain: str | None = None,
|
||||
):
|
||||
"""异步按域名查询站点,兼容显式会话和旧插件无会话调用。"""
|
||||
if domain is None and isinstance(db, str):
|
||||
domain, db = db, None
|
||||
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()
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_by_name(cls, db: AsyncSession, name: str):
|
||||
result = await db.execute(select(cls).where(cls.name == name))
|
||||
return result.scalar_one_or_none()
|
||||
async def async_get_by_name(
|
||||
cls,
|
||||
db: AsyncSession | str | None = None,
|
||||
name: str | None = None,
|
||||
):
|
||||
"""异步按站点名称查询,兼容显式会话和旧插件无会话调用。"""
|
||||
if name is None and isinstance(db, str):
|
||||
name, db = db, None
|
||||
if name is None:
|
||||
raise TypeError("name is required")
|
||||
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行名称查询。"""
|
||||
result = await session.execute(select(cls).where(cls.name == name))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_actives(cls, db: Session):
|
||||
return list(db.execute(select(cls).where(cls.is_active.is_(True))).scalars().all())
|
||||
def get_actives(cls, db: Session | None = None):
|
||||
"""查询启用站点,兼容显式会话和旧插件无会话调用。"""
|
||||
def query(session: Session):
|
||||
"""在给定同步会话中执行启用站点查询。"""
|
||||
return list(session.execute(select(cls).where(cls.is_active.is_(True))).scalars().all())
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_actives(cls, db: AsyncSession):
|
||||
result = await db.execute(select(cls).where(cls.is_active.is_(True)))
|
||||
return list(result.scalars().all())
|
||||
async def async_get_actives(cls, db: AsyncSession | None = None):
|
||||
"""异步查询启用站点,兼容显式会话和旧插件无会话调用。"""
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行启用站点查询。"""
|
||||
result = await session.execute(select(cls).where(cls.is_active.is_(True)))
|
||||
return list(result.scalars().all())
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def list_order_by_pri(cls, db: Session):
|
||||
return list(db.execute(select(cls).order_by(cls.pri)).scalars().all())
|
||||
def list_order_by_pri(cls, db: Session | None = None):
|
||||
"""按优先级升序查询站点,兼容显式会话和旧插件无会话调用。"""
|
||||
def query(session: Session):
|
||||
"""在给定同步会话中执行优先级查询。"""
|
||||
return list(session.execute(select(cls).order_by(cls.pri)).scalars().all())
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_order_by_pri(cls, db: AsyncSession):
|
||||
result = await db.execute(select(cls).order_by(cls.pri))
|
||||
return list(result.scalars().all())
|
||||
async def async_list_order_by_pri(cls, db: AsyncSession | None = None):
|
||||
"""异步按优先级升序查询站点,兼容显式会话和旧插件无会话调用。"""
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行优先级查询。"""
|
||||
result = await session.execute(select(cls).order_by(cls.pri))
|
||||
return list(result.scalars().all())
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_domains_by_ids(cls, db: Session, ids: list):
|
||||
return list(db.execute(select(cls.domain).where(cls.id.in_(ids))).scalars().all())
|
||||
def get_domains_by_ids(
|
||||
cls,
|
||||
db: Session | list[int] | None = None,
|
||||
ids: list[int] | None = None,
|
||||
):
|
||||
"""按 ID 查询域名,兼容显式会话和旧插件无会话调用。"""
|
||||
if ids is None and isinstance(db, list):
|
||||
ids, db = db, None
|
||||
if ids is None:
|
||||
raise TypeError("ids is required")
|
||||
if not ids:
|
||||
return []
|
||||
|
||||
def query(session: Session):
|
||||
"""在给定同步会话中执行域名投影查询。"""
|
||||
return list(session.execute(select(cls.domain).where(cls.id.in_(ids))).scalars().all())
|
||||
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
def reset(cls, db: Session):
|
||||
|
||||
+221
-140
@@ -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 async_db_query, db_query
|
||||
from app.db.decorators import run_legacy_async_query, run_legacy_sync_query
|
||||
from app.db.models._constraints import media_identity_constraint
|
||||
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaSource
|
||||
|
||||
@@ -140,50 +140,66 @@ class Subscribe(Base):
|
||||
return condition
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def exists(
|
||||
cls, db: Session, media_source: MediaSource, media_id: str,
|
||||
cls, db: Session | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
season: Optional[int] = None,
|
||||
episode_group: Optional[str] = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""按媒体身份、季号与剧集组查询已有订阅。"""
|
||||
if db is not None and not isinstance(db, Session):
|
||||
media_source, media_id, db = db, media_source, None
|
||||
condition = cls._identity_condition(
|
||||
media_source, media_id, music_type
|
||||
)
|
||||
if condition is None:
|
||||
return None
|
||||
statement = select(cls).where(condition)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
statement = statement.where(cls.episode_group == episode_group)
|
||||
return db.execute(statement).scalars().first()
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行订阅身份查询。"""
|
||||
statement = select(cls).where(condition)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
return session.execute(
|
||||
statement.where(cls.episode_group == episode_group)
|
||||
).scalars().first()
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_exists(
|
||||
cls, db: AsyncSession, media_source: MediaSource, media_id: str,
|
||||
cls, db: AsyncSession | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
season: Optional[int] = None,
|
||||
episode_group: Optional[str] = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""异步按媒体身份、季号与剧集组查询已有订阅。"""
|
||||
if db is not None and not isinstance(db, AsyncSession):
|
||||
media_source, media_id, db = db, media_source, None
|
||||
condition = cls._identity_condition(
|
||||
media_source, media_id, music_type
|
||||
)
|
||||
if condition is None:
|
||||
return None
|
||||
query = select(cls).filter(condition)
|
||||
if season is not None:
|
||||
query = query.filter(cls.season == season)
|
||||
query = query.filter(cls.episode_group == episode_group)
|
||||
result = await db.execute(query)
|
||||
return result.scalars().first()
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行订阅身份查询。"""
|
||||
statement = select(cls).where(condition)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
result = await session.execute(
|
||||
statement.where(cls.episode_group == episode_group)
|
||||
)
|
||||
return result.scalars().first()
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def exists_by_username(
|
||||
cls, db: Session, username: str, media_source: MediaSource, media_id: str,
|
||||
cls, db: Session | str | None = None,
|
||||
username: str | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
season: Optional[int] = None,
|
||||
episode_group: Optional[str] = None,
|
||||
music_type: Optional[str] = None,
|
||||
@@ -191,6 +207,8 @@ class Subscribe(Base):
|
||||
"""
|
||||
按订阅 owner、媒体身份、季号与剧集组查询订阅行。
|
||||
"""
|
||||
if db is not None and not isinstance(db, Session):
|
||||
username, media_source, media_id, db = db, username, media_source, None
|
||||
if not username:
|
||||
return None
|
||||
condition = cls._identity_condition(
|
||||
@@ -198,23 +216,30 @@ class Subscribe(Base):
|
||||
)
|
||||
if condition is None:
|
||||
return None
|
||||
statement = select(cls).where(cls.username == username, condition)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
statement = statement.where(cls.episode_group == episode_group)
|
||||
return db.execute(statement).scalars().first()
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行订阅 owner 查询。"""
|
||||
statement = select(cls).where(cls.username == username, condition)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
return session.execute(
|
||||
statement.where(cls.episode_group == episode_group)
|
||||
).scalars().first()
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_exists_by_username(
|
||||
cls, db: AsyncSession, username: str, media_source: MediaSource,
|
||||
media_id: str, season: Optional[int] = None,
|
||||
cls, db: AsyncSession | str | None = None,
|
||||
username: str | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None, season: Optional[int] = None,
|
||||
episode_group: Optional[str] = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
异步按订阅 owner、媒体身份、季号与剧集组查询订阅行。
|
||||
"""
|
||||
if db is not None and not isinstance(db, AsyncSession):
|
||||
username, media_source, media_id, db = db, username, media_source, None
|
||||
if not username:
|
||||
return None
|
||||
condition = cls._identity_condition(
|
||||
@@ -222,80 +247,106 @@ class Subscribe(Base):
|
||||
)
|
||||
if condition is None:
|
||||
return None
|
||||
query = select(cls).filter(cls.username == username, condition)
|
||||
if season is not None:
|
||||
query = query.filter(cls.season == season)
|
||||
query = query.filter(cls.episode_group == episode_group)
|
||||
result = await db.execute(query)
|
||||
return result.scalars().first()
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by_state(cls, db: Session, state: str):
|
||||
# 如果 state 为空或 None,返回所有订阅
|
||||
statement = select(cls)
|
||||
if state:
|
||||
# 如果传入的状态不为空,拆分成多个状态
|
||||
statement = statement.where(cls.state.in_(state.split(',')))
|
||||
return list(db.execute(statement).scalars().all())
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_by_state(cls, db: AsyncSession, state: str):
|
||||
# 如果 state 为空或 None,返回所有订阅
|
||||
if not state:
|
||||
result = await db.execute(select(cls))
|
||||
else:
|
||||
# 如果传入的状态不为空,拆分成多个状态
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.state.in_(state.split(',')))
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行订阅 owner 查询。"""
|
||||
statement = select(cls).where(cls.username == username, condition)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
result = await session.execute(
|
||||
statement.where(cls.episode_group == episode_group)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
return result.scalars().first()
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by_title(cls, db: Session, title: str, season: Optional[int] = None):
|
||||
statement = select(cls).where(cls.name == title)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
return db.execute(statement).scalars().first()
|
||||
def get_by_state(cls, db: Session | str | None = None, state: str | None = None):
|
||||
"""按状态列表查询订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, Session):
|
||||
state, db = db if state is None else state, None
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行状态查询。"""
|
||||
statement = select(cls)
|
||||
if state:
|
||||
statement = statement.where(cls.state.in_(state.split(',')))
|
||||
return list(session.execute(statement).scalars().all())
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_by_title(cls, db: AsyncSession, title: str, season: Optional[int] = None):
|
||||
if season is not None:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.name == title, cls.season == season)
|
||||
)
|
||||
else:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.name == title)
|
||||
)
|
||||
return result.scalars().first()
|
||||
async def async_get_by_state(
|
||||
cls, db: AsyncSession | str | None = None, state: str | None = None
|
||||
):
|
||||
"""异步按状态列表查询订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, AsyncSession):
|
||||
state, db = db if state is None else state, None
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行状态查询。"""
|
||||
statement = select(cls)
|
||||
if state:
|
||||
statement = statement.where(cls.state.in_(state.split(',')))
|
||||
result = await session.execute(statement)
|
||||
return list(result.scalars().all())
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_by_title(cls, db: AsyncSession, title: str, season: Optional[int] = None):
|
||||
"""
|
||||
异步按标题查询候选订阅列表。
|
||||
"""
|
||||
if season is not None:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.name == title, cls.season == season)
|
||||
)
|
||||
else:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.name == title)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
def get_by_title(
|
||||
cls, db: Session | str | None = None, title: str | None = None,
|
||||
season: Optional[int] = None,
|
||||
):
|
||||
"""按标题查询订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, Session):
|
||||
title, db = db if title is None else title, None
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行标题查询。"""
|
||||
statement = select(cls).where(cls.name == title)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
return session.execute(statement).scalars().first()
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
async def async_get_by_title(
|
||||
cls, db: AsyncSession | str | None = None, title: str | None = None,
|
||||
season: Optional[int] = None,
|
||||
):
|
||||
"""异步按标题查询订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, AsyncSession):
|
||||
title, db = db if title is None else title, None
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行标题查询。"""
|
||||
statement = select(cls).where(cls.name == title)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
result = await session.execute(statement)
|
||||
return result.scalars().first()
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
async def async_list_by_title(
|
||||
cls, db: AsyncSession | str | None = None, title: str | None = None,
|
||||
season: Optional[int] = None,
|
||||
):
|
||||
"""异步按标题查询候选订阅列表,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, AsyncSession):
|
||||
title, db = db if title is None else title, None
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行标题列表查询。"""
|
||||
statement = select(cls).where(cls.name == title)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
result = await session.execute(statement)
|
||||
return list(result.scalars().all())
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def list_by_media_identity(
|
||||
cls, db: Session, media_source: MediaSource, media_id: str,
|
||||
cls, db: Session | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""同步按统一媒体身份查询候选订阅列表。"""
|
||||
if db is not None and not isinstance(db, Session):
|
||||
media_source, media_id, db = db, media_source, None
|
||||
condition = cls._identity_condition(
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
@@ -303,15 +354,21 @@ class Subscribe(Base):
|
||||
)
|
||||
if condition is None:
|
||||
return []
|
||||
return list(db.execute(select(cls).where(condition)).scalars().all())
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行媒体身份列表查询。"""
|
||||
return list(session.execute(select(cls).where(condition)).scalars().all())
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_by_media_identity(
|
||||
cls, db: AsyncSession, media_source: MediaSource, media_id: str,
|
||||
cls, db: AsyncSession | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""异步按统一媒体身份查询候选订阅列表。"""
|
||||
if db is not None and not isinstance(db, AsyncSession):
|
||||
media_source, media_id, db = db, media_source, None
|
||||
condition = cls._identity_condition(
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
@@ -319,19 +376,26 @@ class Subscribe(Base):
|
||||
)
|
||||
if condition is None:
|
||||
return []
|
||||
result = await db.execute(select(cls).filter(condition))
|
||||
return list(result.scalars().all())
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行媒体身份列表查询。"""
|
||||
result = await session.execute(select(cls).where(condition))
|
||||
return list(result.scalars().all())
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def get_by(
|
||||
cls, db: Session, type: str, media_source: MediaSource, media_id: str,
|
||||
cls, db: Session | str | None = None,
|
||||
type: str | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
season: Optional[str] = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
根据条件查询订阅
|
||||
"""
|
||||
if db is not None and not isinstance(db, Session):
|
||||
type, media_source, media_id, db = db, type, media_source, None
|
||||
condition = cls._identity_condition(
|
||||
media_source, media_id, music_type
|
||||
)
|
||||
@@ -340,18 +404,25 @@ class Subscribe(Base):
|
||||
statement = select(cls).where(condition, cls.type == type)
|
||||
if season is not None:
|
||||
statement = statement.where(cls.season == season)
|
||||
return db.execute(statement).scalars().first()
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行类型媒体查询。"""
|
||||
return session.execute(statement).scalars().first()
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_by(
|
||||
cls, db: AsyncSession, type: str, media_source: MediaSource, media_id: str,
|
||||
cls, db: AsyncSession | str | None = None,
|
||||
type: str | MediaSource | None = None,
|
||||
media_source: MediaSource | str | None = None,
|
||||
media_id: str | None = None,
|
||||
season: Optional[str] = None,
|
||||
music_type: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
根据条件查询订阅
|
||||
"""
|
||||
if db is not None and not isinstance(db, AsyncSession):
|
||||
type, media_source, media_id, db = db, type, media_source, None
|
||||
condition = cls._identity_condition(
|
||||
media_source, media_id, music_type
|
||||
)
|
||||
@@ -360,62 +431,72 @@ class Subscribe(Base):
|
||||
query = select(cls).filter(condition, cls.type == type)
|
||||
if season is not None:
|
||||
query = query.filter(cls.season == season)
|
||||
result = await db.execute(query)
|
||||
return result.scalars().first()
|
||||
async def execute_query(session: AsyncSession):
|
||||
"""在给定异步会话中执行类型媒体查询。"""
|
||||
result = await session.execute(query)
|
||||
return result.scalars().first()
|
||||
return await execute_query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(execute_query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def list_by_username(cls, db: Session, username: str, state: Optional[str] = None, mtype: Optional[str] = None):
|
||||
statement = select(cls).where(cls.username == username)
|
||||
if state:
|
||||
statement = statement.where(cls.state == state)
|
||||
if mtype:
|
||||
statement = statement.where(cls.type == mtype)
|
||||
return list(db.execute(statement).scalars().all())
|
||||
def list_by_username(cls, db: Session | str | None = None, username: str | None = None,
|
||||
state: Optional[str] = None, mtype: Optional[str] = None):
|
||||
"""按用户筛选订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, Session):
|
||||
username, db = db if username is None else username, None
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行用户筛选查询。"""
|
||||
statement = select(cls).where(cls.username == username)
|
||||
if state:
|
||||
statement = statement.where(cls.state == state)
|
||||
if mtype:
|
||||
statement = statement.where(cls.type == mtype)
|
||||
return list(session.execute(statement).scalars().all())
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_by_username(cls, db: AsyncSession, username: str, state: Optional[str] = None,
|
||||
async def async_list_by_username(cls, db: AsyncSession | str | None = None,
|
||||
username: str | None = None, state: Optional[str] = None,
|
||||
mtype: Optional[str] = None):
|
||||
if mtype:
|
||||
"""异步按用户筛选订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, AsyncSession):
|
||||
username, db = db if username is None else username, None
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行用户筛选查询。"""
|
||||
statement = select(cls).where(cls.username == username)
|
||||
if state:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.state == state, cls.username == username, cls.type == mtype)
|
||||
)
|
||||
else:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.username == username, cls.type == mtype)
|
||||
)
|
||||
else:
|
||||
if state:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.state == state, cls.username == username)
|
||||
)
|
||||
else:
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.username == username)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
statement = statement.where(cls.state == state)
|
||||
if mtype:
|
||||
statement = statement.where(cls.type == mtype)
|
||||
result = await session.execute(statement)
|
||||
return list(result.scalars().all())
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
@db_query
|
||||
def list_by_type(cls, db: Session, mtype: str, days: int):
|
||||
return list(db.execute(
|
||||
select(cls).where(
|
||||
def list_by_type(cls, db: Session | str | None = None, mtype: str | None = None, days: int = 7):
|
||||
"""按类型查询最近时间窗内的订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, Session):
|
||||
mtype, db = db if mtype is None else mtype, None
|
||||
def query(session: Session):
|
||||
"""在给定会话中执行时间窗订阅查询。"""
|
||||
return list(session.execute(select(cls).where(
|
||||
cls.type == mtype,
|
||||
cls.date >= time.strftime("%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(time.time() - 86400 * int(days)))
|
||||
)
|
||||
).scalars().all())
|
||||
)).scalars().all())
|
||||
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_list_by_type(cls, db: AsyncSession, mtype: str, days: int):
|
||||
result = await db.execute(
|
||||
select(cls).filter(
|
||||
async def async_list_by_type(cls, db: AsyncSession | str | None = None,
|
||||
mtype: str | None = None, days: int = 7):
|
||||
"""异步按类型查询最近时间窗内的订阅,兼容显式会话和旧插件无会话调用。"""
|
||||
if not isinstance(db, AsyncSession):
|
||||
mtype, db = db if mtype is None else mtype, None
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行时间窗订阅查询。"""
|
||||
result = await session.execute(select(cls).where(
|
||||
cls.type == mtype,
|
||||
cls.date >= time.strftime("%Y-%m-%d %H:%M:%S",
|
||||
time.localtime(time.time() - 86400 * int(days)))
|
||||
)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
))
|
||||
return list(result.scalars().all())
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
+38
-13
@@ -4,7 +4,10 @@ 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 async_db_query, run_legacy_sync_query
|
||||
from app.db.decorators import (
|
||||
run_legacy_async_query,
|
||||
run_legacy_sync_query,
|
||||
)
|
||||
|
||||
|
||||
class User(Base):
|
||||
@@ -55,12 +58,23 @@ class User(Base):
|
||||
return run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_by_name(cls, db: AsyncSession, name: str):
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.name == name)
|
||||
)
|
||||
return result.scalars().first()
|
||||
async def async_get_by_name(
|
||||
cls,
|
||||
db: AsyncSession | str | None = None,
|
||||
name: str | None = None,
|
||||
):
|
||||
"""异步按用户名查询,兼容显式会话和旧插件无会话调用。"""
|
||||
if name is None and isinstance(db, str):
|
||||
name, db = db, None
|
||||
if name is None:
|
||||
raise TypeError("name is required")
|
||||
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行用户名查询。"""
|
||||
result = await session.execute(select(cls).filter(cls.name == name))
|
||||
return result.scalars().first()
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, db: Session | int | None = None, user_id: int | None = None):
|
||||
@@ -79,12 +93,23 @@ class User(Base):
|
||||
return run_legacy_sync_query(query)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get_by_id(cls, db: AsyncSession, user_id: int):
|
||||
result = await db.execute(
|
||||
select(cls).filter(cls.id == user_id)
|
||||
)
|
||||
return result.scalars().first()
|
||||
async def async_get_by_id(
|
||||
cls,
|
||||
db: AsyncSession | int | None = None,
|
||||
user_id: int | None = None,
|
||||
):
|
||||
"""异步按用户 ID 查询,兼容显式会话和旧插件无会话调用。"""
|
||||
if user_id is None and isinstance(db, int):
|
||||
user_id, db = db, None
|
||||
if user_id is None:
|
||||
raise TypeError("user_id is required")
|
||||
|
||||
async def query(session: AsyncSession):
|
||||
"""在给定异步会话中执行用户 ID 查询。"""
|
||||
result = await session.execute(select(cls).filter(cls.id == user_id))
|
||||
return result.scalars().first()
|
||||
|
||||
return await query(db) if isinstance(db, AsyncSession) else await run_legacy_async_query(query)
|
||||
|
||||
def delete_by_name(self, db: Session, name: str):
|
||||
user = self.get_by_name(db, name)
|
||||
|
||||
Reference in New Issue
Block a user