mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
refactor: complete runtime configuration migration
This commit is contained in:
+111
-22
@@ -21,7 +21,7 @@ T = TypeVar("T")
|
||||
|
||||
|
||||
def execute_dml(db: Session, statement: Executable,
|
||||
execution_options: Optional[dict] = None) -> int:
|
||||
execution_options: Optional[dict[str, Any]] = None) -> int:
|
||||
"""
|
||||
执行 DML 语句并返回影响行数。
|
||||
|
||||
@@ -37,7 +37,7 @@ def execute_dml(db: Session, statement: Executable,
|
||||
result = db.execute(statement)
|
||||
else:
|
||||
result = db.execute(statement, execution_options=execution_options)
|
||||
return cast(CursorResult[Any], result).rowcount
|
||||
return int(cast(CursorResult[Any], result).rowcount)
|
||||
|
||||
|
||||
def get_id_column() -> Mapped[int]:
|
||||
@@ -52,7 +52,7 @@ def get_id_column() -> Mapped[int]:
|
||||
return mapped_column(Integer, Sequence('id'), primary_key=True)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
class Base(DeclarativeBase): # type: ignore[misc] # SQLAlchemy 无 py.typed 基类
|
||||
"""
|
||||
声明式基类。
|
||||
|
||||
@@ -70,11 +70,11 @@ class Base(DeclarativeBase):
|
||||
id: Mapped[int]
|
||||
|
||||
@db_update
|
||||
def create(self, db: Session):
|
||||
def create(self, db: Session) -> None:
|
||||
db.add(self)
|
||||
|
||||
@async_db_update
|
||||
async def async_create(self, db: AsyncSession):
|
||||
async def async_create(self, db: AsyncSession) -> Self:
|
||||
db.add(self)
|
||||
await db.flush()
|
||||
return self
|
||||
@@ -82,23 +82,30 @@ class Base(DeclarativeBase):
|
||||
@classmethod
|
||||
@db_query
|
||||
def get(cls, db: Session, rid: int) -> Optional[Self]:
|
||||
return db.execute(select(cls).where(and_(cls.id == rid))).scalars().first()
|
||||
return cast(
|
||||
Optional[Self],
|
||||
db.execute(select(cls).where(and_(cls.id == rid))).scalars().first(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@async_db_query
|
||||
async def async_get(cls, db: AsyncSession, rid: int) -> Optional[Self]:
|
||||
result = await db.execute(select(cls).where(and_(cls.id == rid)))
|
||||
return result.scalars().first()
|
||||
return cast(Optional[Self], result.scalars().first())
|
||||
|
||||
@db_update
|
||||
def update(self, db: Session, payload: dict):
|
||||
def update(self, db: Session, payload: dict[str, Any]) -> None:
|
||||
for key, value in payload.items():
|
||||
setattr(self, key, value)
|
||||
if inspect(self).detached:
|
||||
db.add(self)
|
||||
|
||||
@async_db_update
|
||||
async def async_update(self, db: AsyncSession, payload: dict):
|
||||
async def async_update(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
payload: dict[str, Any],
|
||||
) -> None:
|
||||
for key, value in payload.items():
|
||||
setattr(self, key, value)
|
||||
if inspect(self).detached:
|
||||
@@ -106,12 +113,12 @@ class Base(DeclarativeBase):
|
||||
|
||||
@classmethod
|
||||
@db_update
|
||||
def delete(cls, db: Session, rid):
|
||||
def delete(cls, db: Session, rid: Any) -> None:
|
||||
db.execute(delete(cls).where(and_(cls.id == rid)))
|
||||
|
||||
@classmethod
|
||||
@async_db_update
|
||||
async def async_delete(cls, db: AsyncSession, rid):
|
||||
async def async_delete(cls, db: AsyncSession, rid: Any) -> None:
|
||||
result = await db.execute(select(cls).where(and_(cls.id == rid)))
|
||||
user = result.scalars().first()
|
||||
if user:
|
||||
@@ -119,12 +126,12 @@ class Base(DeclarativeBase):
|
||||
|
||||
@classmethod
|
||||
@db_update
|
||||
def truncate(cls, db: Session):
|
||||
def truncate(cls, db: Session) -> None:
|
||||
db.execute(delete(cls))
|
||||
|
||||
@classmethod
|
||||
@async_db_update
|
||||
async def async_truncate(cls, db: AsyncSession):
|
||||
async def async_truncate(cls, db: AsyncSession) -> None:
|
||||
await db.execute(delete(cls))
|
||||
|
||||
@classmethod
|
||||
@@ -138,12 +145,15 @@ class Base(DeclarativeBase):
|
||||
result = await db.execute(select(cls))
|
||||
return list(result.scalars().all())
|
||||
|
||||
def to_dict(self):
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return {c.name: getattr(self, c.name, None) for c in self.__table__.columns} # noqa
|
||||
|
||||
@declared_attr.directive
|
||||
@declared_attr.directive # type: ignore[misc] # SQLAlchemy decorator 缺少类型信息
|
||||
def __tablename__(cls) -> str: # noqa: N805 declared_attr 的第一个参数即类本身
|
||||
return cls.__name__.lower()
|
||||
return str(cls.__name__).lower()
|
||||
|
||||
|
||||
TModel = TypeVar("TModel", bound=Base)
|
||||
|
||||
|
||||
class DbOper:
|
||||
@@ -157,10 +167,10 @@ class DbOper:
|
||||
|
||||
def _execute_sync_write(self, operation: Callable[[Session], T]) -> T:
|
||||
"""在当前同步会话暂存,或委托组合根创建兼容事务。"""
|
||||
if self._db is None:
|
||||
if self._db is None or isinstance(self._db, AsyncSession):
|
||||
# 旧调用可能在同一 Oper 上混用同步/异步方法;跨会话类型时使用匹配的
|
||||
# 兼容事务,不能把 AsyncSession 交给同步 SQLAlchemy API。
|
||||
return run_sync_transaction(operation)
|
||||
if not isinstance(self._db, Session):
|
||||
raise TypeError("同步写操作不能使用 AsyncSession")
|
||||
return operation(self._db)
|
||||
|
||||
async def _execute_async_write(
|
||||
@@ -168,8 +178,87 @@ class DbOper:
|
||||
operation: Callable[[AsyncSession], Awaitable[T]],
|
||||
) -> T:
|
||||
"""在当前异步会话暂存,或委托组合根创建兼容事务。"""
|
||||
if self._db is None:
|
||||
if self._db is None or isinstance(self._db, Session):
|
||||
# 与查询装饰器的历史行为一致:同步会话不会被错误传入异步模型写入,
|
||||
# 而是由组合根另开匹配的异步事务。
|
||||
return await run_async_transaction(operation)
|
||||
if not isinstance(self._db, AsyncSession):
|
||||
raise TypeError("异步写操作不能使用同步 Session")
|
||||
return await operation(self._db)
|
||||
|
||||
def _stage_create(self, model: TModel) -> TModel:
|
||||
"""在显式同步事务中暂存新模型,不触发 Base 的兼容提交装饰器。"""
|
||||
def stage(session: Session) -> TModel:
|
||||
"""把模型加入当前同步会话。"""
|
||||
session.add(model)
|
||||
return model
|
||||
|
||||
return self._execute_sync_write(stage)
|
||||
|
||||
async def _stage_async_create(self, model: TModel) -> TModel:
|
||||
"""在显式异步事务中暂存新模型并刷新主键。"""
|
||||
async def stage(session: AsyncSession) -> TModel:
|
||||
"""把模型加入当前异步会话并刷新。"""
|
||||
session.add(model)
|
||||
await session.flush()
|
||||
return model
|
||||
|
||||
return await self._execute_async_write(stage)
|
||||
|
||||
def _stage_update(self, model: TModel, payload: dict[str, Any]) -> TModel:
|
||||
"""在显式同步事务中更新模型字段,必要时重新附加游离对象。"""
|
||||
def stage(session: Session) -> TModel:
|
||||
"""应用字段并把游离模型重新加入会话。"""
|
||||
for key, value in payload.items():
|
||||
setattr(model, key, value)
|
||||
model_state = inspect(model, raiseerr=False)
|
||||
if model_state is not None and model_state.detached:
|
||||
session.add(model)
|
||||
return model
|
||||
|
||||
return self._execute_sync_write(stage)
|
||||
|
||||
async def _stage_async_update(
|
||||
self,
|
||||
model: TModel,
|
||||
payload: dict[str, Any],
|
||||
) -> TModel:
|
||||
"""在显式异步事务中更新模型字段,必要时重新附加游离对象。"""
|
||||
async def stage(session: AsyncSession) -> TModel:
|
||||
"""应用字段并把游离模型重新加入会话。"""
|
||||
for key, value in payload.items():
|
||||
setattr(model, key, value)
|
||||
model_state = inspect(model, raiseerr=False)
|
||||
if model_state is not None and model_state.detached:
|
||||
session.add(model)
|
||||
return model
|
||||
|
||||
return await self._execute_async_write(stage)
|
||||
|
||||
def _stage_delete(self, model_type: type[Base], rid: Any) -> None:
|
||||
"""在显式同步事务中按主键删除模型。"""
|
||||
self._execute_sync_write(
|
||||
lambda session: session.execute(
|
||||
delete(model_type).where(model_type.id == rid)
|
||||
)
|
||||
)
|
||||
|
||||
async def _stage_async_delete(self, model_type: type[Base], rid: Any) -> None:
|
||||
"""在显式异步事务中按主键删除模型。"""
|
||||
async def stage(session: AsyncSession) -> None:
|
||||
"""执行当前异步事务内的按主键删除。"""
|
||||
await session.execute(delete(model_type).where(model_type.id == rid))
|
||||
|
||||
await self._execute_async_write(stage)
|
||||
|
||||
def _stage_truncate(self, model_type: type[Base]) -> None:
|
||||
"""在显式同步事务中删除模型表的全部记录。"""
|
||||
self._execute_sync_write(
|
||||
lambda session: session.execute(delete(model_type))
|
||||
)
|
||||
|
||||
async def _stage_async_truncate(self, model_type: type[Base]) -> None:
|
||||
"""在显式异步事务中删除模型表的全部记录。"""
|
||||
async def stage(session: AsyncSession) -> None:
|
||||
"""执行当前异步事务内的全表删除。"""
|
||||
await session.execute(delete(model_type))
|
||||
|
||||
await self._execute_async_write(stage)
|
||||
|
||||
+19
-5
@@ -16,7 +16,7 @@
|
||||
SQLAlchemy 归还连接时已在池层吞掉异常并 invalidate 坏连接,再把释放故障升级成调用方
|
||||
的异常,只会让一次已经落库的写入看起来像失败,诱发重复提交。
|
||||
"""
|
||||
from typing import Any, Awaitable, Callable, Optional, Tuple, TypeVar
|
||||
from typing import Any, Awaitable, Callable, Optional, TypeVar
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -31,7 +31,10 @@ _R = TypeVar("_R")
|
||||
# 接管、返回值原样透传」。否则调用方传 None 或传异步会话都会被判成类型不符,而这恰恰是
|
||||
# 装饰器存在的理由(各 Oper 的 self._db 常态就是 None)。
|
||||
|
||||
def _get_args_db(args: tuple, kwargs: dict) -> Optional[Session]:
|
||||
def _get_args_db(
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
) -> Optional[Session]:
|
||||
"""
|
||||
从参数中获取数据库Session对象
|
||||
"""
|
||||
@@ -49,7 +52,10 @@ def _get_args_db(args: tuple, kwargs: dict) -> Optional[Session]:
|
||||
return db
|
||||
|
||||
|
||||
def _get_args_async_db(args: tuple, kwargs: dict) -> Optional[AsyncSession]:
|
||||
def _get_args_async_db(
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
) -> Optional[AsyncSession]:
|
||||
"""
|
||||
从参数中获取异步数据库AsyncSession对象
|
||||
"""
|
||||
@@ -67,7 +73,11 @@ def _get_args_async_db(args: tuple, kwargs: dict) -> Optional[AsyncSession]:
|
||||
return db
|
||||
|
||||
|
||||
def _update_args_db(args: tuple, kwargs: dict, db: Session) -> Tuple[tuple, dict]:
|
||||
def _update_args_db(
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
db: Session,
|
||||
) -> tuple[tuple[Any, ...], dict[str, Any]]:
|
||||
"""
|
||||
更新参数中的数据库Session对象,关键字传参时更新db的值,否则更新第1或第2个参数
|
||||
"""
|
||||
@@ -81,7 +91,11 @@ def _update_args_db(args: tuple, kwargs: dict, db: Session) -> Tuple[tuple, dict
|
||||
return args, kwargs
|
||||
|
||||
|
||||
def _update_args_async_db(args: tuple, kwargs: dict, db: AsyncSession) -> Tuple[tuple, dict]:
|
||||
def _update_args_async_db(
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
db: AsyncSession,
|
||||
) -> tuple[tuple[Any, ...], dict[str, Any]]:
|
||||
"""
|
||||
更新参数中的异步数据库AsyncSession对象,关键字传参时更新db的值,否则更新第1或第2个参数
|
||||
"""
|
||||
|
||||
@@ -115,7 +115,7 @@ class AgentChatOper(DbOper):
|
||||
}
|
||||
payload = {key: value for key, value in payload.items() if value is not None}
|
||||
if chat:
|
||||
chat.update(self._db, payload)
|
||||
self._stage_update(chat, payload)
|
||||
return self.get(session_id=session_id, user_id=user_id) or self.get(session_id=session_id)
|
||||
|
||||
chat = AgentChat(
|
||||
@@ -134,7 +134,7 @@ class AgentChatOper(DbOper):
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
chat.create(self._db)
|
||||
self._stage_create(chat)
|
||||
return self.get(session_id=session_id, user_id=user_id) or self.get(session_id=session_id)
|
||||
|
||||
def save_agent_messages(
|
||||
@@ -153,8 +153,8 @@ class AgentChatOper(DbOper):
|
||||
chat = self.ensure_session(session_id=session_id, user_id=user_id)
|
||||
if not chat:
|
||||
return
|
||||
chat.update(
|
||||
self._db,
|
||||
self._stage_update(
|
||||
chat,
|
||||
{
|
||||
"agent_messages": messages or [],
|
||||
"updated_at": self._now(),
|
||||
@@ -192,8 +192,8 @@ class AgentChatOper(DbOper):
|
||||
return
|
||||
if self.has_custom_title(chat.title):
|
||||
return
|
||||
chat.update(
|
||||
self._db,
|
||||
self._stage_update(
|
||||
chat,
|
||||
{
|
||||
"title": normalized_title,
|
||||
"updated_at": self._now(),
|
||||
@@ -232,8 +232,8 @@ class AgentChatOper(DbOper):
|
||||
if self.has_custom_title(chat.title)
|
||||
else self._normalize_title(title, normalized_messages)
|
||||
)
|
||||
chat.update(
|
||||
self._db,
|
||||
self._stage_update(
|
||||
chat,
|
||||
{
|
||||
"title": normalized_title,
|
||||
"preview": self._normalize_preview(normalized_messages),
|
||||
@@ -311,7 +311,7 @@ class AgentChatOper(DbOper):
|
||||
chat = await self.async_get(session_id=session_id, user_id=user_id)
|
||||
if not chat:
|
||||
return False
|
||||
await AgentChat.async_delete(self._db, chat.id)
|
||||
await self._stage_async_delete(AgentChat, chat.id)
|
||||
return True
|
||||
|
||||
async def async_stage_delete(
|
||||
|
||||
@@ -59,7 +59,7 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
新增下载历史
|
||||
"""
|
||||
DownloadHistory(**kwargs).create(self._db)
|
||||
self._stage_create(DownloadHistory(**kwargs))
|
||||
|
||||
def stage_add(self, payload: dict) -> DownloadHistory:
|
||||
"""在调用方同步 Session 中暂存下载历史并返回已分配 ID 的记录。"""
|
||||
@@ -76,7 +76,7 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
for file_item in file_items:
|
||||
downloadfile = DownloadFiles(**file_item)
|
||||
downloadfile.create(self._db)
|
||||
self._stage_create(downloadfile)
|
||||
|
||||
def stage_add_files(self, file_items: List[dict]) -> None:
|
||||
"""在调用方事务内批量暂存下载文件,不逐条提交。"""
|
||||
@@ -89,7 +89,7 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
清空下载历史文件记录
|
||||
"""
|
||||
DownloadFiles.truncate(self._db)
|
||||
self._stage_truncate(DownloadFiles)
|
||||
|
||||
def get_files_by_hash(self, download_hash: str, state: Optional[int] = None) -> List[DownloadFiles]:
|
||||
"""
|
||||
@@ -171,13 +171,13 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
异步删除下载记录。
|
||||
"""
|
||||
await DownloadHistory.async_delete(self._db, historyid)
|
||||
await self._stage_async_delete(DownloadHistory, historyid)
|
||||
|
||||
def truncate(self):
|
||||
"""
|
||||
清空下载记录
|
||||
"""
|
||||
DownloadHistory.truncate(self._db)
|
||||
self._stage_truncate(DownloadHistory)
|
||||
|
||||
def get_last_by(self, mtype=None, title: Optional[str] = None, year: Optional[str] = None,
|
||||
season: Optional[str] = None, episode: Optional[str] = None,
|
||||
@@ -230,7 +230,7 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
删除下载记录
|
||||
"""
|
||||
DownloadHistory.delete(self._db, historyid)
|
||||
self._stage_delete(DownloadHistory, historyid)
|
||||
|
||||
def stage_delete_history(self, historyid: int) -> None:
|
||||
"""暂存下载记录删除,不由模型装饰器提交事务。"""
|
||||
@@ -244,4 +244,4 @@ class DownloadHistoryOper(DbOper):
|
||||
"""
|
||||
删除下载文件记录
|
||||
"""
|
||||
DownloadFiles.delete(self._db, downloadfileid)
|
||||
self._stage_delete(DownloadFiles, downloadfileid)
|
||||
|
||||
@@ -35,7 +35,7 @@ class MediaServerOper(DbOper):
|
||||
return False
|
||||
item = MediaServerItem(**kwargs)
|
||||
if not item.get_by_server_itemid(self._db, server, item_id):
|
||||
item.create(self._db)
|
||||
self._stage_create(item)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -51,10 +51,10 @@ class MediaServerOper(DbOper):
|
||||
|
||||
item = MediaServerItem.get_by_server_itemid(self._db, server, item_id)
|
||||
if item:
|
||||
item.update(self._db, kwargs)
|
||||
self._stage_update(item, kwargs)
|
||||
return False
|
||||
|
||||
MediaServerItem(**kwargs).create(self._db)
|
||||
self._stage_create(MediaServerItem(**kwargs))
|
||||
return True
|
||||
|
||||
def empty(self, server: Optional[str] = None):
|
||||
|
||||
@@ -99,7 +99,7 @@ class MessageOper(DbOper):
|
||||
if k not in Message.__table__.columns.keys(): # noqa
|
||||
kwargs.pop(k)
|
||||
|
||||
return await Message(**kwargs).async_create(self._db)
|
||||
return await self._stage_async_create(Message(**kwargs))
|
||||
|
||||
def list_by_page(self, page: int = 1, count: int = 30) -> list[Message]:
|
||||
"""
|
||||
|
||||
@@ -21,11 +21,11 @@ class PluginDataOper(DbOper):
|
||||
"""
|
||||
plugin = PluginData.get_plugin_data_by_key(self._db, plugin_id, key)
|
||||
if plugin:
|
||||
plugin.update(self._db, {
|
||||
self._stage_update(plugin, {
|
||||
"value": value
|
||||
})
|
||||
else:
|
||||
PluginData(plugin_id=plugin_id, key=key, value=value).create(self._db)
|
||||
self._stage_create(PluginData(plugin_id=plugin_id, key=key, value=value))
|
||||
|
||||
async def async_save(self, plugin_id: str, key: str, value: Any) -> None:
|
||||
"""
|
||||
@@ -39,11 +39,11 @@ class PluginDataOper(DbOper):
|
||||
self._db, plugin_id, key
|
||||
)
|
||||
if plugin:
|
||||
await plugin.async_update(self._db, {"value": value})
|
||||
await self._stage_async_update(plugin, {"value": value})
|
||||
else:
|
||||
await PluginData(
|
||||
plugin_id=plugin_id, key=key, value=value
|
||||
).async_create(self._db)
|
||||
await self._stage_async_create(
|
||||
PluginData(plugin_id=plugin_id, key=key, value=value)
|
||||
)
|
||||
|
||||
def get_data(self, plugin_id: str, key: Optional[str] = None) -> Any:
|
||||
"""
|
||||
@@ -102,7 +102,7 @@ class PluginDataOper(DbOper):
|
||||
"""
|
||||
清空插件数据
|
||||
"""
|
||||
PluginData.truncate(self._db)
|
||||
self._stage_truncate(PluginData)
|
||||
|
||||
def get_data_all(self, plugin_id: str) -> Any:
|
||||
"""
|
||||
|
||||
+24
-22
@@ -21,7 +21,7 @@ class SiteOper(DbOper):
|
||||
"""
|
||||
site = Site(**kwargs)
|
||||
if not site.get_by_domain(self._db, kwargs.get("domain")):
|
||||
site.create(self._db)
|
||||
self._stage_create(site)
|
||||
return True, "新增站点成功"
|
||||
return False, "站点已存在"
|
||||
|
||||
@@ -113,7 +113,7 @@ class SiteOper(DbOper):
|
||||
"""
|
||||
删除站点
|
||||
"""
|
||||
Site.delete(self._db, sid)
|
||||
self._stage_delete(Site, sid)
|
||||
|
||||
def reset(self) -> None:
|
||||
"""清空站点表;兼容入口的事务由组合根统一持有。"""
|
||||
@@ -130,7 +130,7 @@ class SiteOper(DbOper):
|
||||
site = Site.get(self._db, sid)
|
||||
if not site:
|
||||
return None
|
||||
site.update(self._db, payload)
|
||||
self._stage_update(site, payload)
|
||||
return site
|
||||
|
||||
async def async_update(self, sid: int, payload: dict) -> Optional[Site]:
|
||||
@@ -139,7 +139,7 @@ class SiteOper(DbOper):
|
||||
"""
|
||||
site = await self.async_get(sid)
|
||||
if site:
|
||||
await site.async_update(self._db, payload)
|
||||
await self._stage_async_update(site, payload)
|
||||
return site
|
||||
|
||||
def get_by_domain(self, domain: str) -> Optional[Site]:
|
||||
@@ -179,7 +179,7 @@ class SiteOper(DbOper):
|
||||
site = Site.get_by_domain(self._db, domain)
|
||||
if not site:
|
||||
return False, "站点不存在"
|
||||
site.update(self._db, {
|
||||
self._stage_update(site, {
|
||||
"cookie": cookies
|
||||
})
|
||||
return True, "更新站点Cookie成功"
|
||||
@@ -191,7 +191,7 @@ class SiteOper(DbOper):
|
||||
site = Site.get_by_domain(self._db, domain)
|
||||
if not site:
|
||||
return False, "站点不存在"
|
||||
site.update(self._db, {
|
||||
self._stage_update(site, {
|
||||
"rss": rss
|
||||
})
|
||||
return True, "更新站点RSS地址成功"
|
||||
@@ -215,10 +215,10 @@ class SiteOper(DbOper):
|
||||
if siteuserdatas:
|
||||
# 存在则更新
|
||||
if not payload.get("err_msg"):
|
||||
siteuserdatas[0].update(self._db, payload)
|
||||
self._stage_update(siteuserdatas[0], payload)
|
||||
else:
|
||||
# 不存在则插入
|
||||
SiteUserData(**payload).create(self._db)
|
||||
self._stage_create(SiteUserData(**payload))
|
||||
return True, "更新站点用户数据成功"
|
||||
|
||||
def get_userdata(self) -> List[SiteUserData]:
|
||||
@@ -287,9 +287,11 @@ class SiteOper(DbOper):
|
||||
icon_base64 = f"data:image/ico;base64,{icon_base64}" if icon_base64 else ""
|
||||
siteicon = self.get_icon_by_domain(domain)
|
||||
if not siteicon:
|
||||
SiteIcon(name=name, domain=domain, url=icon_url, base64=icon_base64).create(self._db)
|
||||
self._stage_create(
|
||||
SiteIcon(name=name, domain=domain, url=icon_url, base64=icon_base64)
|
||||
)
|
||||
elif icon_base64:
|
||||
siteicon.update(self._db, {
|
||||
self._stage_update(siteicon, {
|
||||
"url": icon_url,
|
||||
"base64": icon_base64
|
||||
})
|
||||
@@ -313,7 +315,7 @@ class SiteOper(DbOper):
|
||||
note = dict(sorted(note.items(), key=lambda x: x[0], reverse=True)[:10])
|
||||
avg_seconds = sum([v for v in note.values()]) // avg_times
|
||||
|
||||
sta.update(self._db, {
|
||||
self._stage_update(sta, {
|
||||
"success": sta.success + 1,
|
||||
"seconds": avg_seconds or sta.seconds,
|
||||
"lst_state": 0,
|
||||
@@ -326,7 +328,7 @@ class SiteOper(DbOper):
|
||||
note = {
|
||||
lst_date: seconds or 1
|
||||
}
|
||||
SiteStatistic(
|
||||
self._stage_create(SiteStatistic(
|
||||
domain=domain,
|
||||
success=1,
|
||||
fail=0,
|
||||
@@ -334,7 +336,7 @@ class SiteOper(DbOper):
|
||||
lst_state=0,
|
||||
lst_mod_date=lst_date,
|
||||
note=note
|
||||
).create(self._db)
|
||||
))
|
||||
|
||||
def fail(self, domain: str):
|
||||
"""
|
||||
@@ -343,19 +345,19 @@ class SiteOper(DbOper):
|
||||
lst_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
sta = SiteStatistic.get_by_domain(self._db, domain)
|
||||
if sta:
|
||||
sta.update(self._db, {
|
||||
self._stage_update(sta, {
|
||||
"fail": sta.fail + 1,
|
||||
"lst_state": 1,
|
||||
"lst_mod_date": lst_date
|
||||
})
|
||||
else:
|
||||
SiteStatistic(
|
||||
self._stage_create(SiteStatistic(
|
||||
domain=domain,
|
||||
success=0,
|
||||
fail=1,
|
||||
lst_state=1,
|
||||
lst_mod_date=lst_date
|
||||
).create(self._db)
|
||||
))
|
||||
|
||||
async def async_success(self, domain: str, seconds: Optional[int] = None):
|
||||
"""
|
||||
@@ -375,7 +377,7 @@ class SiteOper(DbOper):
|
||||
note = dict(sorted(note.items(), key=lambda x: x[0], reverse=True)[:10])
|
||||
avg_seconds = sum([v for v in note.values()]) // avg_times
|
||||
|
||||
await sta.async_update(self._db, {
|
||||
await self._stage_async_update(sta, {
|
||||
"success": sta.success + 1,
|
||||
"seconds": avg_seconds or sta.seconds,
|
||||
"lst_state": 0,
|
||||
@@ -388,7 +390,7 @@ class SiteOper(DbOper):
|
||||
note = {
|
||||
lst_date: seconds or 1
|
||||
}
|
||||
await SiteStatistic(
|
||||
await self._stage_async_create(SiteStatistic(
|
||||
domain=domain,
|
||||
success=1,
|
||||
fail=0,
|
||||
@@ -396,7 +398,7 @@ class SiteOper(DbOper):
|
||||
lst_state=0,
|
||||
lst_mod_date=lst_date,
|
||||
note=note
|
||||
).async_create(self._db)
|
||||
))
|
||||
|
||||
async def async_fail(self, domain: str):
|
||||
"""
|
||||
@@ -405,16 +407,16 @@ class SiteOper(DbOper):
|
||||
lst_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
sta = await SiteStatistic.async_get_by_domain(self._db, domain)
|
||||
if sta:
|
||||
await sta.async_update(self._db, {
|
||||
await self._stage_async_update(sta, {
|
||||
"fail": sta.fail + 1,
|
||||
"lst_state": 1,
|
||||
"lst_mod_date": lst_date
|
||||
})
|
||||
else:
|
||||
await SiteStatistic(
|
||||
await self._stage_async_create(SiteStatistic(
|
||||
domain=domain,
|
||||
success=0,
|
||||
fail=1,
|
||||
lst_state=1,
|
||||
lst_mod_date=lst_date
|
||||
).async_create(self._db)
|
||||
))
|
||||
|
||||
@@ -227,7 +227,7 @@ class SubscribeOper(DbOper):
|
||||
if after_commit:
|
||||
after_commit(subscribe.id)
|
||||
return subscribe.id, "订阅已存在"
|
||||
Subscribe(**_persistable(payload)).create(self._db)
|
||||
self._stage_create(Subscribe(**_persistable(payload)))
|
||||
subscribe = self._exists(identity, username)
|
||||
if not subscribe:
|
||||
return 0, "新增订阅失败"
|
||||
@@ -251,7 +251,7 @@ class SubscribeOper(DbOper):
|
||||
if after_commit:
|
||||
await after_commit(subscribe.id)
|
||||
return subscribe.id, "订阅已存在"
|
||||
await Subscribe(**_persistable(payload)).async_create(self._db)
|
||||
await self._stage_async_create(Subscribe(**_persistable(payload)))
|
||||
subscribe = await self._async_exists(identity, username)
|
||||
if not subscribe:
|
||||
return 0, "新增订阅失败"
|
||||
@@ -472,13 +472,13 @@ class SubscribeOper(DbOper):
|
||||
"""
|
||||
删除订阅
|
||||
"""
|
||||
Subscribe.delete(self._db, rid=sid)
|
||||
self._stage_delete(Subscribe, sid)
|
||||
|
||||
async def async_delete(self, sid: int):
|
||||
"""
|
||||
异步删除订阅。
|
||||
"""
|
||||
await Subscribe.async_delete(self._db, rid=sid)
|
||||
await self._stage_async_delete(Subscribe, sid)
|
||||
|
||||
async def stage_delete(self, sid: int) -> None:
|
||||
"""登记订阅删除但不提交,由 Application UnitOfWork 控制事务边界。"""
|
||||
@@ -493,7 +493,7 @@ class SubscribeOper(DbOper):
|
||||
subscribe = await self.async_get(sid)
|
||||
if subscribe:
|
||||
payload = _normalize_integer_flags(payload)
|
||||
await subscribe.async_update(self._db, payload)
|
||||
await self._stage_async_update(subscribe, payload)
|
||||
return subscribe
|
||||
|
||||
async def async_stage_update(
|
||||
@@ -527,7 +527,7 @@ class SubscribeOper(DbOper):
|
||||
subscribe = self.get(sid)
|
||||
if subscribe:
|
||||
payload = _normalize_integer_flags(payload)
|
||||
subscribe.update(self._db, payload)
|
||||
self._stage_update(subscribe, payload)
|
||||
return subscribe
|
||||
|
||||
def list_by_username(self, username: str, state: Optional[str] = None,
|
||||
@@ -556,7 +556,7 @@ class SubscribeOper(DbOper):
|
||||
if "id" in kwargs:
|
||||
kwargs.pop("id")
|
||||
subscribe = SubscribeHistory(**kwargs)
|
||||
subscribe.create(self._db)
|
||||
self._stage_create(subscribe)
|
||||
|
||||
def exist_history(
|
||||
self, media_source: MediaSource, media_id: str,
|
||||
|
||||
@@ -47,4 +47,4 @@ class SubscribeHistoryOper(DbOper):
|
||||
|
||||
async def async_delete(self, history_id: int) -> None:
|
||||
"""异步删除订阅历史。"""
|
||||
await SubscribeHistory.async_delete(self._db, history_id)
|
||||
await self._stage_async_delete(SubscribeHistory, history_id)
|
||||
|
||||
@@ -43,12 +43,12 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
||||
if old_value != value:
|
||||
# 假值(False/0/None/空容器)同样落库而不是删除记录:
|
||||
# 读取端以「无记录」表示未配置并回落默认值,删除会使布尔开关的关闭态无法持久化
|
||||
conf.update(self._db, {"value": value})
|
||||
self._stage_update(conf, {"value": value})
|
||||
return True
|
||||
return None
|
||||
else:
|
||||
conf = SystemConfig(key=key, value=value)
|
||||
conf.create(self._db)
|
||||
self._stage_create(conf)
|
||||
return True
|
||||
|
||||
async def async_set(self, key: Union[str, SystemConfigKey], value: Any) -> Optional[bool]:
|
||||
@@ -78,10 +78,10 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
||||
if conf:
|
||||
# 假值(False/0/None/空容器)同样落库而不是删除记录:
|
||||
# 读取端以「无记录」表示未配置并回落默认值,删除会使布尔开关的关闭态无法持久化
|
||||
await conf.async_update(self._db, {"value": value})
|
||||
await self._stage_async_update(conf, {"value": value})
|
||||
else:
|
||||
conf = SystemConfig(key=key, value=value)
|
||||
await conf.async_create(self._db)
|
||||
await self._stage_async_create(conf)
|
||||
# 数据库更新成功后,再更新缓存
|
||||
with self._rlock:
|
||||
self.__SYSTEMCONF[key] = copy.deepcopy(value)
|
||||
@@ -132,5 +132,5 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
||||
# 写入数据库
|
||||
conf = SystemConfig.get_by_key(self._db, key)
|
||||
if conf:
|
||||
conf.delete(self._db, conf.id)
|
||||
self._stage_delete(SystemConfig, conf.id)
|
||||
return True
|
||||
|
||||
@@ -177,7 +177,7 @@ class TransferHistoryOper(DbOper):
|
||||
kwargs.update({
|
||||
"date": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
})
|
||||
TransferHistory(**kwargs).create(self._db)
|
||||
self._stage_create(TransferHistory(**kwargs))
|
||||
|
||||
def statistic(self, days: int = 7) -> List[Any]:
|
||||
"""
|
||||
@@ -226,7 +226,7 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
删除转移记录
|
||||
"""
|
||||
TransferHistory.delete(self._db, historyid)
|
||||
self._stage_delete(TransferHistory, historyid)
|
||||
|
||||
def stage_delete(self, historyid: int) -> None:
|
||||
"""暂存整理记录删除,不由模型装饰器提交事务。"""
|
||||
@@ -244,13 +244,13 @@ class TransferHistoryOper(DbOper):
|
||||
"""
|
||||
异步删除转移记录。
|
||||
"""
|
||||
await TransferHistory.async_delete(self._db, historyid)
|
||||
await self._stage_async_delete(TransferHistory, historyid)
|
||||
|
||||
def truncate(self):
|
||||
"""
|
||||
清空转移记录
|
||||
"""
|
||||
TransferHistory.truncate(self._db)
|
||||
self._stage_truncate(TransferHistory)
|
||||
|
||||
def add_force(self, **kwargs) -> Optional[TransferHistory]:
|
||||
"""
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class UserOper(DbOper):
|
||||
新增用户
|
||||
"""
|
||||
user = User(**kwargs)
|
||||
user.create(self._db)
|
||||
self._stage_create(user)
|
||||
|
||||
def get_by_name(self, name: str) -> Optional[User]:
|
||||
"""
|
||||
|
||||
@@ -31,12 +31,12 @@ class UserConfigOper(DbOper, metaclass=Singleton):
|
||||
conf = UserConfig.get_by_key(db=self._db, username=username, key=key)
|
||||
if conf:
|
||||
if value:
|
||||
conf.update(self._db, {"value": value})
|
||||
self._stage_update(conf, {"value": value})
|
||||
else:
|
||||
conf.delete(self._db, conf.id)
|
||||
self._stage_delete(UserConfig, conf.id)
|
||||
else:
|
||||
conf = UserConfig(username=username, key=key, value=value)
|
||||
conf.create(self._db)
|
||||
self._stage_create(conf)
|
||||
|
||||
def get(self, username: str, key: Optional[Union[str, UserConfigKey]] = None) -> Any:
|
||||
"""
|
||||
|
||||
@@ -67,7 +67,7 @@ class WorkflowOper(DbOper):
|
||||
"""
|
||||
wf = Workflow(**kwargs)
|
||||
if not wf.get_by_name(self._db, kwargs.get("name")):
|
||||
wf.create(self._db)
|
||||
self._stage_create(wf)
|
||||
return True, "新增工作流成功"
|
||||
return False, "工作流已存在"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user