refactor: 收口 V3 分层架构与插件兼容边界

This commit is contained in:
jxxghp
2026-08-18 13:22:02 +08:00
parent cca99bd421
commit 8472bcff43
274 changed files with 10730 additions and 6130 deletions
+8
View File
@@ -140,6 +140,14 @@ class DownloadHistoryOper(DbOper):
"""
return DownloadHistory.list_by_page(self._db, page, count)
async def async_list_by_page(
self,
page: int = 1,
count: int = 30,
) -> List[DownloadHistory]:
"""异步分页查询下载历史。"""
return await DownloadHistory.async_list_by_page(self._db, page, count)
async def async_delete_history(self, historyid: int):
"""
异步删除下载记录。
+36
View File
@@ -0,0 +1,36 @@
"""PassKey 数据访问适配器。"""
from typing import Any, Optional
from app.db.base import DbOper
from app.db.models.passkey import PassKey
class PassKeyOper(DbOper):
"""封装 PassKey 查询和维护,避免 API 层直接引用模型静态方法。"""
def list_by_user_id(self, user_id: int) -> list[PassKey]:
"""读取用户启用的 PassKey。"""
return PassKey.get_by_user_id(self._db, user_id)
def list(self) -> list[PassKey]:
"""读取全部 PassKey,用于判断系统是否已配置通行密钥。"""
return PassKey.list(self._db)
def get_by_credential_id(self, credential_id: str) -> Optional[PassKey]:
"""按凭证 ID 读取启用的 PassKey。"""
return PassKey.get_by_credential_id(self._db, credential_id)
def create(self, payload: dict[str, Any]) -> PassKey:
"""创建 PassKey 凭证。"""
passkey = PassKey(**payload)
passkey.create(self._db)
return passkey
def update_last_used(self, passkey: PassKey, sign_count: int) -> bool:
"""更新凭证最后使用时间和签名计数。"""
return bool(passkey.update_last_used(self._db, sign_count))
def delete_by_id(self, passkey_id: int, user_id: int) -> bool:
"""删除指定用户的凭证。"""
return bool(PassKey.delete_by_id(self._db, passkey_id, user_id))
+31
View File
@@ -91,6 +91,10 @@ class SiteOper(DbOper):
"""
return await Site.async_list(self._db)
async def async_list_order_by_pri(self) -> List[Site]:
"""异步按优先级获取站点,供站点查询应用服务使用。"""
return await Site.async_list_order_by_pri(self._db)
def list_order_by_pri(self) -> List[Site]:
"""
获取站点列表
@@ -115,6 +119,14 @@ class SiteOper(DbOper):
"""
Site.delete(self._db, sid)
def reset(self) -> None:
"""清空站点表,保留站点模型细节在数据库适配层。"""
Site.reset(self._db)
async def stage_reset(self) -> None:
"""暂存清空站点表,由应用事务统一提交。"""
await self._db.execute(sqlalchemy_delete(Site))
def update(self, sid: int, payload: dict) -> Optional[Site]:
"""
更新站点
@@ -235,6 +247,25 @@ class SiteOper(DbOper):
self._db, domain=domain, workdate=workdate
)
async def async_get_userdata_latest(self) -> List[SiteUserData]:
"""异步获取各站点最新用户数据。"""
return await SiteUserData.async_get_latest(self._db)
async def async_get_icon_by_domain(self, domain: str) -> Optional[SiteIcon]:
"""异步按域名获取站点图标。"""
return await SiteIcon.async_get_by_domain(self._db, domain)
async def async_get_statistic_by_domain(
self,
domain: str,
) -> Optional[SiteStatistic]:
"""异步按域名获取站点统计。"""
return await SiteStatistic.async_get_by_domain(self._db, domain)
async def async_list_statistics(self) -> List[SiteStatistic]:
"""异步获取所有站点统计。"""
return await SiteStatistic.async_list(self._db)
def get_userdata_by_date(self, date: str) -> List[SiteUserData]:
"""
获取站点用户数据
+69
View File
@@ -145,6 +145,21 @@ class SubscribeOper(DbOper):
}
return bool(Subscribe.exists(self._db, **identity_params))
async def async_exists(
self, media_source: MediaSource, media_id: str,
season: Optional[int] = None, episode_group: Optional[str] = None,
music_type: Optional[str] = None,
) -> Optional[Subscribe]:
"""异步按媒体身份、季号及可选剧集组读取命中的订阅。"""
return await Subscribe.async_exists(
self._db,
media_source=media_source,
media_id=media_id,
music_type=music_type,
season=season,
episode_group=episode_group,
)
def get(self, sid: int) -> Optional[Subscribe]:
"""
获取订阅
@@ -157,6 +172,34 @@ class SubscribeOper(DbOper):
"""
return await Subscribe.async_get(self._db, rid=sid)
async def async_list_by_media_identity(
self,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> List[Subscribe]:
"""异步按规范媒体身份读取订阅。"""
return await Subscribe.async_list_by_media_identity(
self._db,
media_source=media_source,
media_id=media_id,
music_type=music_type,
)
def list_by_media_identity(
self,
media_source: MediaSource,
media_id: str,
music_type: Optional[str] = None,
) -> List[Subscribe]:
"""同步按规范媒体身份读取订阅。"""
return Subscribe.list_by_media_identity(
self._db,
media_source=media_source,
media_id=media_id,
music_type=music_type,
)
async def get_candidate(
self,
subscribe_id: int,
@@ -266,6 +309,32 @@ class SubscribeOper(DbOper):
return await Subscribe.async_get_by_state(self._db, state)
return await Subscribe.async_list(self._db)
async def async_list_by_username(
self,
username: str,
state: Optional[str] = None,
mtype: Optional[str] = None,
) -> List[Subscribe]:
"""异步按用户获取订阅。"""
return await Subscribe.async_list_by_username(
self._db,
username=username,
state=state,
mtype=mtype,
)
async def async_list_by_title(
self,
title: str,
season: Optional[int] = None,
) -> List[Subscribe]:
"""异步按标题获取订阅,供旧查询测试和迁移调用兼容。"""
return await Subscribe.async_list_by_title(
self._db,
title=title,
season=season,
)
def delete(self, sid: int):
"""
删除订阅
+25 -1
View File
@@ -1,4 +1,4 @@
from typing import List
from typing import List, Optional
from app.db.base import DbOper
from app.db.models.subscribehistory import SubscribeHistory
@@ -24,3 +24,27 @@ class SubscribeHistoryOper(DbOper):
page=page,
count=count,
)
async def async_list_by_type_and_username(
self,
mtype: str,
username: str,
page: int = 1,
count: int = 30,
) -> List[SubscribeHistory]:
"""异步按媒体类型和用户分页查询订阅历史。"""
return await SubscribeHistory.async_list_by_type_and_username(
self._db,
mtype=mtype,
username=username,
page=page,
count=count,
)
async def async_get(self, history_id: int) -> Optional[SubscribeHistory]:
"""异步按 ID 查询订阅历史。"""
return await SubscribeHistory.async_get(self._db, history_id)
async def async_delete(self, history_id: int) -> None:
"""异步删除订阅历史。"""
await SubscribeHistory.async_delete(self._db, history_id)
+20 -2
View File
@@ -32,12 +32,18 @@ class TransferHistoryOper(DbOper):
page: int = 1,
count: int = 30,
status: Optional[bool] = None,
wildcard: bool = False,
) -> List[TransferHistory]:
"""
异步按标题分页查询转移记录。
"""
return await TransferHistory.async_list_by_title(
self._db, title=title, page=page, count=count, status=status
self._db,
title=title,
page=page,
count=count,
status=status,
wildcard=wildcard,
)
async def async_list_by_page(
@@ -63,12 +69,16 @@ class TransferHistoryOper(DbOper):
self,
title: str,
status: Optional[bool] = None,
wildcard: bool = False,
) -> Optional[int]:
"""
异步按标题统计转移记录数量。
"""
return await TransferHistory.async_count_by_title(
self._db, title=title, status=status
self._db,
title=title,
status=status,
wildcard=wildcard,
)
def get_by_title(self, title: str) -> List[TransferHistory]:
@@ -174,6 +184,14 @@ class TransferHistoryOper(DbOper):
"""
return TransferHistory.statistic(self._db, days)
async def async_statistic(self, days: int = 7) -> List[Any]:
"""异步统计最近若干天的整理历史数量。"""
return await TransferHistory.async_statistic(self._db, days)
def monthly_media_statistics(self) -> tuple[int, int, int, int]:
"""统计本月成功整理的电影、剧集、单集和音乐数量。"""
return TransferHistory.monthly_media_statistics(self._db)
def get_by(self, title: Optional[str] = None, year: Optional[str] = None, mtype: Optional[str] = None,
season: Optional[str] = None, episode: Optional[str] = None,
media_source: Optional[MediaSource] = None, media_id: Optional[str] = None,
+32
View File
@@ -39,6 +39,38 @@ class UserOper(DbOper):
"""
return User.get_by_name(self._db, name)
def get_by_id(self, user_id: int) -> Optional[User]:
"""按 ID 获取用户。"""
return User.get_by_id(self._db, user_id)
async def async_list(self) -> List[User]:
"""异步获取用户列表。"""
return await User.async_list(self._db)
async def async_create(self, payload: dict) -> Optional[User]:
"""异步创建用户。"""
return await User(**payload).async_create(self._db)
async def async_update(self, user_id: int, payload: dict) -> Optional[User]:
"""异步更新用户。"""
user = await self.async_get_by_id(user_id)
if user:
await user.async_update(self._db, payload)
return user
async def async_delete(self, user_id: int) -> None:
"""异步删除用户。"""
await User.async_delete_by_id(self._db, user_id)
async def async_update_otp_by_name(
self,
name: str,
otp: bool,
secret: str,
) -> None:
"""异步更新用户 OTP 状态。"""
await User.async_update_otp_by_name(self._db, name, otp, secret)
async def async_get_by_name(self, name: str) -> Optional[User]:
"""
异步根据用户名获取用户。