"""中心服务存量上报用例。""" from __future__ import annotations from collections.abc import Awaitable, Callable from typing import Any, Optional from app.schemas.media import resolve_media_identity class ServerReportService: """协调本地订阅、插件清单和中心服务统计上报。""" SUBSCRIBE_FIELDS = frozenset({ "name", "year", "type", "media_source", "media_id", "music_type", "total_tracks", "genre_ids", "season", "poster", "backdrop", "vote", "description", }) def __init__( self, *, config_reader: Callable[[Any], Any], config_writer: Callable[[Any, Any], Any], installed_plugins_provider: Callable[[], list[str]], subscribes_provider: Callable[[], list[Any]], async_subscribes_provider: Callable[[], Awaitable[list[Any]]] | None = None, plugin_report_sender: Callable[[list[dict]], Any], async_plugin_report_sender: Callable[[list[dict]], Awaitable[Any]], subscribe_report_sender: Callable[[list[dict]], Any], repo_url_sanitizer: Callable[[Optional[str]], Optional[str]], async_subscribe_report_sender: Callable[[list[dict]], Awaitable[Any]] | None = None, async_config_writer: Callable[[Any, Any], Awaitable[Any]] | None = None, ) -> None: """保存本地读取端口和只负责 I/O 的中心服务发送端口。""" self._config_reader = config_reader self._config_writer = config_writer self._installed_plugins_provider = installed_plugins_provider self._subscribes_provider = subscribes_provider self._async_subscribes_provider = async_subscribes_provider self._plugin_report_sender = plugin_report_sender self._async_plugin_report_sender = async_plugin_report_sender self._subscribe_report_sender = subscribe_report_sender self._async_subscribe_report_sender = async_subscribe_report_sender self._async_config_writer = async_config_writer self._repo_url_sanitizer = repo_url_sanitizer def init_report( self, *, enabled: bool, state_key: Any, reporter: Callable[[], bool], ) -> None: """首次成功上报后写入对应的完成标记。""" if enabled and not self._config_reader(state_key) and reporter(): self._config_writer(state_key, "1") async def async_init_report( self, *, enabled: bool, state_key: Any, reporter: Callable[[], Awaitable[bool]], ) -> None: """异步完成首次上报,并通过异步配置端口持久化完成标记。""" if not enabled or self._config_reader(state_key): return if not await reporter(): return if self._async_config_writer is None: raise RuntimeError("中心服务上报未配置异步配置写入端口") await self._async_config_writer(state_key, "1") def build_subscribe_payload(self, item: Optional[dict]) -> Optional[dict]: """构造中心服务订阅统计载荷并移除本地运行字段。""" if not isinstance(item, dict): return None media_source, media_id = resolve_media_identity(media=item) if not media_source or not media_id: return None payload = { key: value for key, value in item.items() if key in self.SUBSCRIBE_FIELDS } payload["media_source"] = str(media_source) payload["media_id"] = media_id return payload def build_plugin_payload( self, items: Optional[list[tuple[str, Optional[str]]]] = None, ) -> list[dict[str, Any]]: """构造插件安装统计载荷并脱敏本地仓库路径。""" if items: return [ { "plugin_id": plugin_id, "repo_url": self._repo_url_sanitizer(repo_url), } for plugin_id, repo_url in items if plugin_id ] return [ {"plugin_id": plugin_id, "repo_url": None} for plugin_id in self._installed_plugins_provider() if plugin_id ] def report_subscribes(self, *, enabled: bool) -> bool: """上报当前全部有效订阅的公开统计字段。""" if not enabled: return False subscribes = self._subscribes_provider() if not subscribes: return True payloads = [ payload for subscribe in subscribes if (payload := self.build_subscribe_payload(subscribe.to_dict())) ] if not payloads: return True response = self._subscribe_report_sender(payloads) return bool(response is not None and response.status_code == 200) def report_plugins( self, *, enabled: bool, items: Optional[list[tuple[str, Optional[str]]]] = None, ) -> bool: """同步上报当前插件安装清单。""" if not enabled: return False payload = self.build_plugin_payload(items) if not payload: return False response = self._plugin_report_sender(payload) return bool(response is not None and response.status_code == 200) async def async_report_plugins( self, *, enabled: bool, items: Optional[list[tuple[str, Optional[str]]]] = None, ) -> bool: """异步上报当前插件安装清单。""" if not enabled: return False payload = self.build_plugin_payload(items) if not payload: return False response = await self._async_plugin_report_sender(payload) return bool(response is not None and response.status_code == 200) async def async_report_subscribes(self, *, enabled: bool) -> bool: """异步上报当前全部有效订阅的公开统计字段。""" if not enabled: return False if self._async_subscribe_report_sender is None: raise RuntimeError("中心服务上报未配置异步订阅发送端口") if self._async_subscribes_provider is None: raise RuntimeError("中心服务未配置异步订阅读取端口") subscribes = await self._async_subscribes_provider() if not subscribes: return True payloads = [ payload for subscribe in subscribes if (payload := self.build_subscribe_payload(subscribe.to_dict())) ] if not payloads: return True response = await self._async_subscribe_report_sender(payloads) return bool(response is not None and response.status_code == 200)