refactor: isolate transfer and download history queries

This commit is contained in:
jxxghp
2026-08-23 13:59:12 +08:00
parent 03c03fa27d
commit 4074fa4e42
10 changed files with 505 additions and 348 deletions
+17 -17
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 Base, execute_dml, get_id_column
from app.db.decorators import async_db_query, db_query
from app.db.decorators import legacy_async_db_query, legacy_db_query
from app.db.models._constraints import media_identity_constraint
from app.schemas.types import MediaSource
@@ -77,7 +77,7 @@ class DownloadHistory(Base):
)
@classmethod
@db_query
@legacy_db_query
def get_by_hash(cls, db: Session, download_hash: str):
return db.execute(
select(DownloadHistory)
@@ -86,7 +86,7 @@ class DownloadHistory(Base):
).scalars().first()
@classmethod
@db_query
@legacy_db_query
def get_by_hashes(cls, db: Session, download_hashes: List[str]):
"""
批量查询多个下载任务的最新历史记录,避免在上层形成 N+1 查询。
@@ -119,7 +119,7 @@ class DownloadHistory(Base):
]
@classmethod
@db_query
@legacy_db_query
def get_by_media_identity(
cls, db: Session, media_source: MediaSource, media_id: str,
music_type: Optional[str] = None,
@@ -136,7 +136,7 @@ class DownloadHistory(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@db_query
@legacy_db_query
def list_by_page(
cls, db: Session, page: int = 1, count: int = 30
):
@@ -148,7 +148,7 @@ class DownloadHistory(Base):
).scalars().all())
@classmethod
@async_db_query
@legacy_async_db_query
async def async_list_by_page(
cls, db: AsyncSession, page: int = 1, count: int = 30
):
@@ -161,7 +161,7 @@ class DownloadHistory(Base):
return list(result.scalars().all())
@classmethod
@async_db_query
@legacy_async_db_query
async def async_list_by_title(
cls,
db: AsyncSession,
@@ -177,13 +177,13 @@ class DownloadHistory(Base):
return list(result.scalars().all())
@classmethod
@async_db_query
@legacy_async_db_query
async def async_count(cls, db: AsyncSession):
result = await db.execute(select(func.count(cls.id)))
return result.scalar()
@classmethod
@async_db_query
@legacy_async_db_query
async def async_count_by_title(cls, db: AsyncSession, title: str):
result = await db.execute(
select(func.count(cls.id)).filter(_title_like(cls.title, title))
@@ -191,14 +191,14 @@ class DownloadHistory(Base):
return result.scalar()
@classmethod
@db_query
@legacy_db_query
def get_by_path(cls, db: Session, path: str):
return db.execute(
select(DownloadHistory).where(DownloadHistory.path == path)
).scalars().first()
@classmethod
@db_query
@legacy_db_query
def get_last_by(
cls,
db: Session,
@@ -237,7 +237,7 @@ class DownloadHistory(Base):
@classmethod
@db_query
@legacy_db_query
def list_by_user_date(cls, db: Session, date: str, username: Optional[str] = None):
"""
查询某用户某时间之前的下载历史。
@@ -256,7 +256,7 @@ class DownloadHistory(Base):
).scalars().all())
@classmethod
@db_query
@legacy_db_query
def list_by_date(
cls,
db: Session,
@@ -282,7 +282,7 @@ class DownloadHistory(Base):
).scalars().all())
@classmethod
@db_query
@legacy_db_query
def list_by_type(cls, db: Session, mtype: str, days: int):
return list(db.execute(
select(DownloadHistory).where(
@@ -345,7 +345,7 @@ class DownloadFiles(Base):
)
@classmethod
@db_query
@legacy_db_query
def get_by_hash(cls, db: Session, download_hash: str, state: Optional[int] = None):
statement = select(cls).where(cls.download_hash == download_hash)
if state is not None:
@@ -353,7 +353,7 @@ class DownloadFiles(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@db_query
@legacy_db_query
def get_by_fullpath(cls, db: Session, fullpath: str, all_files: bool = False):
result = db.execute(
select(cls).where(cls.fullpath == fullpath).order_by(cls.id.desc())
@@ -361,7 +361,7 @@ class DownloadFiles(Base):
return list(result.all()) if all_files else result.first()
@classmethod
@db_query
@legacy_db_query
def get_by_savepath(cls, db: Session, savepath: str):
return list(db.execute(select(cls).where(cls.savepath == savepath)).scalars().all())
+91 -47
View File
@@ -8,7 +8,11 @@ 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 (
legacy_async_db_query,
legacy_db_query,
run_legacy_sync_query,
)
from app.db.models._constraints import media_identity_constraint
from app.schemas.types import MUSIC_ENTITY_ALBUM, MUSIC_ENTITY_RECORDING, MediaSource, MediaType
@@ -94,7 +98,7 @@ class TransferHistory(Base):
)
@classmethod
@db_query
@legacy_db_query
def list_by_title(cls, db: Session, title: str, page: int = 1, count: int = 30,
status: Optional[bool] = None, wildcard: bool = False):
if wildcard:
@@ -121,7 +125,7 @@ class TransferHistory(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@async_db_query
@legacy_async_db_query
async def async_list_by_title(cls, db: AsyncSession, title: str, page: int = 1, count: int = 30,
status: Optional[bool] = None, wildcard: bool = False):
if wildcard:
@@ -149,7 +153,7 @@ class TransferHistory(Base):
return list(result.scalars().all())
@classmethod
@db_query
@legacy_db_query
def list_by_page(cls, db: Session, page: int = 1, count: int = 30, status: Optional[bool] = None):
statement = select(cls)
if status is not None:
@@ -163,7 +167,7 @@ class TransferHistory(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@async_db_query
@legacy_async_db_query
async def async_list_by_page(cls, db: AsyncSession, page: int = 1, count: int = 30,
status: Optional[bool] = None):
if status is not None:
@@ -185,16 +189,29 @@ class TransferHistory(Base):
return list(result.scalars().all())
@classmethod
@db_query
def get_by_hash(cls, db: Session, download_hash: str):
return db.execute(
select(cls).where(cls.download_hash == download_hash)
).scalars().first()
def get_by_hash(
cls,
db: Session | str | None = None,
download_hash: str | None = None,
):
"""按下载哈希查询最新记录,兼容旧插件无会话调用。"""
if download_hash is None and isinstance(db, str):
download_hash, db = db, None
if download_hash is None:
raise TypeError("download_hash is required")
def query(session: Session):
"""在调用方提供的同步会话中执行哈希查询。"""
return session.execute(
select(cls).where(cls.download_hash == download_hash)
).scalars().first()
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
@classmethod
@db_query
def get_by_src(
cls, db: Session, src: str, storage: Optional[str] = None
cls, db: Session | str | None = None, src: str | None = None,
storage: Optional[str] = None
) -> Optional["TransferHistory"]:
"""
按源路径和存储查询单条整理记录。
@@ -204,17 +221,26 @@ class TransferHistory(Base):
:param storage: 源存储类型
:return: 命中的整理记录,未命中时返回 None
"""
statement = select(cls).where(cls.src == src)
if storage:
statement = statement.where(cls.src_storage == storage)
return db.execute(
statement.order_by(cls.id.desc())
).scalars().first()
if src is None and isinstance(db, str):
src, db = db, None
if src is None:
raise TypeError("src is required")
def query(session: Session):
"""在调用方提供的同步会话中执行源路径查询。"""
statement = select(cls).where(cls.src == src)
if storage:
statement = statement.where(cls.src_storage == storage)
return session.execute(
statement.order_by(cls.id.desc())
).scalars().first()
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
@classmethod
@db_query
def get_success_by_src(
cls, db: Session, src: str, storage: Optional[str] = None
cls, db: Session | str | None = None, src: str | None = None,
storage: Optional[str] = None
) -> Optional["TransferHistory"]:
"""
按源路径和存储查询成功的整理记录,源路径原样精确匹配。
@@ -226,17 +252,26 @@ class TransferHistory(Base):
:param storage: 源存储类型
:return: 命中的成功整理记录,未命中时返回 None
"""
statement = select(cls).where(cls.src == src, cls.status.is_(True))
if storage:
statement = statement.where(cls.src_storage == storage)
return db.execute(
statement.order_by(cls.id.desc())
).scalars().first()
if src is None and isinstance(db, str):
src, db = db, None
if src is None:
raise TypeError("src is required")
def query(session: Session):
"""在调用方提供的同步会话中执行成功源路径查询。"""
statement = select(cls).where(cls.src == src, cls.status.is_(True))
if storage:
statement = statement.where(cls.src_storage == storage)
return session.execute(
statement.order_by(cls.id.desc())
).scalars().first()
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
@classmethod
@db_query
def get_by_dest(
cls, db: Session, dest: str, storage: Optional[str] = None
cls, db: Session | str | None = None, dest: str | None = None,
storage: Optional[str] = None
) -> Optional["TransferHistory"]:
"""
按目标路径和存储查询单条整理记录。
@@ -246,15 +281,24 @@ class TransferHistory(Base):
:param storage: 目标存储类型
:return: 命中的整理记录,未命中时返回 None
"""
statement = select(cls).where(cls.dest == dest)
if storage:
statement = statement.where(cls.dest_storage == storage)
return db.execute(
statement.order_by(cls.id.desc())
).scalars().first()
if dest is None and isinstance(db, str):
dest, db = db, None
if dest is None:
raise TypeError("dest is required")
def query(session: Session):
"""在调用方提供的同步会话中执行目标路径查询。"""
statement = select(cls).where(cls.dest == dest)
if storage:
statement = statement.where(cls.dest_storage == storage)
return session.execute(
statement.order_by(cls.id.desc())
).scalars().first()
return query(db) if isinstance(db, Session) else run_legacy_sync_query(query)
@classmethod
@db_query
@legacy_db_query
def list_success_by_src(
cls,
db: Session,
@@ -294,7 +338,7 @@ class TransferHistory(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@db_query
@legacy_db_query
def list_success_move_by_dest(
cls,
db: Session,
@@ -337,14 +381,14 @@ class TransferHistory(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@db_query
@legacy_db_query
def list_by_hash(cls, db: Session, download_hash: str):
return list(db.execute(
select(cls).where(cls.download_hash == download_hash)
).scalars().all())
@classmethod
@db_query
@legacy_db_query
def statistic(cls, db: Session, days: int = 7):
"""
统计最近days天的下载历史数量,按日期分组返回每日数量
@@ -361,7 +405,7 @@ class TransferHistory(Base):
).all())
@classmethod
@db_query
@legacy_db_query
def monthly_media_statistics(cls, db: Session):
"""
统计当月成功整理的电影、电视剧、剧集和音乐数量。
@@ -427,7 +471,7 @@ class TransferHistory(Base):
return 1
@classmethod
@async_db_query
@legacy_async_db_query
async def async_statistic(cls, db: AsyncSession, days: int = 7):
"""
统计最近days天的下载历史数量,按日期分组返回每日数量
@@ -442,7 +486,7 @@ class TransferHistory(Base):
return result.all()
@classmethod
@db_query
@legacy_db_query
def count(cls, db: Session, status: Optional[bool] = None):
statement = select(func.count(cls.id))
if status is not None:
@@ -450,7 +494,7 @@ class TransferHistory(Base):
return db.execute(statement).scalar()
@classmethod
@async_db_query
@legacy_async_db_query
async def async_count(cls, db: AsyncSession, status: Optional[bool] = None):
if status is not None:
result = await db.execute(
@@ -463,7 +507,7 @@ class TransferHistory(Base):
return result.scalar()
@classmethod
@db_query
@legacy_db_query
def count_by_title(cls, db: Session, title: str, status: Optional[bool] = None, wildcard: bool = False):
if wildcard:
text_filter = or_(
@@ -483,7 +527,7 @@ class TransferHistory(Base):
return db.execute(statement).scalar()
@classmethod
@async_db_query
@legacy_async_db_query
async def async_count_by_title(cls, db: AsyncSession, title: str, status: Optional[bool] = None, wildcard: bool = False):
if wildcard:
text_filter = or_(
@@ -504,7 +548,7 @@ class TransferHistory(Base):
return result.scalar()
@classmethod
@db_query
@legacy_db_query
def list_by(cls, db: Session, mtype: Optional[str] = None, title: Optional[str] = None, year: Optional[str] = None,
season: Optional[str] = None,
episode: Optional[str] = None,
@@ -542,7 +586,7 @@ class TransferHistory(Base):
return list(db.execute(statement).scalars().all())
@classmethod
@db_query
@legacy_db_query
def get_by_media_identity(
cls, db: Session, media_source: MediaSource, media_id: str,
mtype: Optional[str] = None,
@@ -589,7 +633,7 @@ class TransferHistory(Base):
return history
@classmethod
@db_query
@legacy_db_query
def list_by_date(cls, db: Session, date: str):
"""
查询某时间之后的转移历史