mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 03:56:43 +08:00
118 lines
4.1 KiB
Python
118 lines
4.1 KiB
Python
import copy
|
|
import threading
|
|
from typing import Any, Union, Dict, Optional
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.db.base import DbOper
|
|
from app.db.models.userconfig import UserConfig
|
|
from app.schemas.types import UserConfigKey
|
|
from app.foundation.singleton import Singleton
|
|
|
|
|
|
class UserConfigOper(DbOper, metaclass=Singleton):
|
|
"""
|
|
用户配置管理
|
|
"""
|
|
def __init__(self):
|
|
"""初始化空快照,数据库加载由启动组合根显式执行。"""
|
|
super().__init__()
|
|
self.__USERCONF = {}
|
|
self._snapshot_lock = threading.RLock()
|
|
self._write_lock = threading.RLock()
|
|
self._loaded = False
|
|
|
|
def load_snapshot(self, db: Optional[Session] = None) -> None:
|
|
"""从显式会话或 Oper 事务边界加载用户配置并发布内存快照。"""
|
|
with self._write_lock:
|
|
snapshot: dict[str, dict[str, Any]] = {}
|
|
items = UserConfig.list(db) if db is not None else self._execute_sync_query(
|
|
UserConfig.list
|
|
)
|
|
for item in items:
|
|
if item.username and item.key:
|
|
snapshot.setdefault(item.username, {})[item.key] = copy.deepcopy(
|
|
item.value
|
|
)
|
|
with self._snapshot_lock:
|
|
self.__USERCONF = snapshot
|
|
self._loaded = True
|
|
|
|
def _require_loaded(self) -> None:
|
|
"""阻止消费者读取尚未完成启动加载的半成品快照。"""
|
|
if not self._loaded:
|
|
raise RuntimeError("用户配置快照尚未加载")
|
|
|
|
def set(self, username: str, key: Union[str, UserConfigKey], value: Any):
|
|
"""
|
|
设置用户配置
|
|
"""
|
|
if isinstance(key, UserConfigKey):
|
|
key = key.value
|
|
self._require_loaded()
|
|
with self._write_lock:
|
|
|
|
def write(db):
|
|
"""在当前事务中按用户配置的假值规则写入记录。"""
|
|
conf = UserConfig.get_by_key(db=db, username=username, key=key)
|
|
if conf:
|
|
if value:
|
|
conf.value = copy.deepcopy(value)
|
|
else:
|
|
db.delete(conf)
|
|
else:
|
|
db.add(
|
|
UserConfig(
|
|
username=username,
|
|
key=key,
|
|
value=copy.deepcopy(value),
|
|
)
|
|
)
|
|
|
|
self._execute_sync_write(write)
|
|
# 既有运行时语义会保留刚写入的假值,即使其数据库记录被删除。
|
|
self.__set_config_cache(username=username, key=key, value=value)
|
|
|
|
def get(self, username: str, key: Optional[Union[str, UserConfigKey]] = None) -> Any:
|
|
"""
|
|
获取用户配置
|
|
"""
|
|
with self._snapshot_lock:
|
|
self._require_loaded()
|
|
if not username:
|
|
return copy.deepcopy(self.__USERCONF)
|
|
if isinstance(key, UserConfigKey):
|
|
key = key.value
|
|
if not key:
|
|
return copy.deepcopy(self.__get_config_caches(username=username))
|
|
return copy.deepcopy(self.__get_config_cache(username=username, key=key))
|
|
|
|
def __set_config_cache(self, username: str, key: str, value: Any):
|
|
"""
|
|
设置配置缓存
|
|
"""
|
|
if not username or not key:
|
|
return
|
|
with self._snapshot_lock:
|
|
user_cache = self.__USERCONF.setdefault(username, {})
|
|
user_cache[key] = copy.deepcopy(value)
|
|
|
|
def __get_config_caches(self, username: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
获取配置缓存
|
|
"""
|
|
if not username or not self.__USERCONF:
|
|
return None
|
|
return self.__USERCONF.get(username)
|
|
|
|
def __get_config_cache(self, username: str, key: str) -> Any:
|
|
"""
|
|
获取配置缓存
|
|
"""
|
|
if not username or not key or not self.__USERCONF:
|
|
return None
|
|
user_cache = self.__get_config_caches(username)
|
|
if not user_cache:
|
|
return None
|
|
return user_cache.get(key)
|