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
+140
View File
@@ -0,0 +1,140 @@
"""插件市场同步运行时用例。"""
from __future__ import annotations
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import Any, Callable, Optional
from app.runtime.extensions.plugin.system import PluginSystemServices
class PluginSyncService:
"""根据已安装清单同步缺失或过期插件,不参与插件实例生命周期。"""
def __init__(
self,
*,
frozen: Callable[[], bool],
installed_plugins: Callable[[], list[str]],
online_plugins: Callable[[], list[Any]],
local_plugins: Callable[[], list[Any]],
merge_plugins: Callable[[list[Any], list[Any], list[Any]], list[Any]],
plugin_exists: Callable[[str, Optional[str]], bool],
install: Callable[[str, Optional[str], bool], tuple[bool, str]],
report: Callable[..., Any],
log: Any,
) -> None:
"""保存目录读取、包安装和持久化报告端口。"""
self._frozen = frozen
self._installed_plugins = installed_plugins
self._online_plugins = online_plugins
self._local_plugins = local_plugins
self._merge_plugins = merge_plugins
self._plugin_exists = plugin_exists
self._install = install
self._report = report
self._logger = log
def sync(self) -> list[str]:
"""并发安装本地缺失或需要更新的已安装插件。"""
if self._frozen():
return []
installed = self._installed_plugins()
online = self._online_plugins()
local = self._local_plugins()
candidates = self._merge_plugins(online + local, [], []) if online or local else []
targets = [
plugin
for plugin in candidates
if plugin.id in installed
and plugin.system_version_compatible is not False
and not self._plugin_exists(plugin.id, plugin.plugin_version)
]
if not targets:
return []
self._logger.info("开始安装第三方插件...")
synced: list[str] = []
failed: list[str] = []
def install_one(plugin: Any) -> None:
"""安装一个插件并记录结果。"""
started = time.time()
state, message = self._install(plugin.id, plugin.repo_url, True)
elapsed = time.time() - started
if state:
self._report(plugin_id=plugin.id, repo_url=plugin.repo_url)
self._logger.info(
f"插件 {plugin.plugin_name} 安装成功,版本:{plugin.plugin_version}"
f"耗时:{elapsed:.2f}"
)
synced.append(plugin.id)
else:
self._logger.error(
f"插件 {plugin.plugin_name} v{plugin.plugin_version} 安装失败:"
f"{message},耗时:{elapsed:.2f}"
)
failed.append(plugin.id)
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(install_one, plugin): plugin for plugin in targets}
for future in as_completed(futures):
plugin = futures[future]
try:
future.result()
except Exception as error: # noqa: BLE001
self._logger.error(
f"插件 {plugin.plugin_name} 安装过程中出现异常: {error}"
)
self._logger.info(
f"第三方插件安装完成,成功:{len(synced)} 个,失败:{len(failed)}"
)
return synced
class LocalPluginSyncService:
"""同步本地插件仓源码到运行目录,并记录热重载抑制窗口。"""
def __init__(
self,
*,
installed_plugins: Callable[[], list[str]],
candidate: Callable[[str], Optional[dict]],
system: Callable[[], PluginSystemServices],
recent_sync: dict[str, float],
log: Any,
) -> None:
"""保存本地候选、包同步和运行态监控端口。"""
self._installed_plugins = installed_plugins
self._candidate = candidate
self._system = system
self._recent_sync = recent_sync
self._logger = log
def sync(self, plugin_id: str, candidate: Optional[dict] = None) -> bool:
"""同步已安装且兼容的本地插件,成功后记录短时事件抑制标记。"""
if plugin_id not in self._installed_plugins():
self._logger.info(f"本地插件 {plugin_id} 尚未安装,跳过自动同步和热重载")
return False
candidate = candidate or self._candidate(plugin_id)
if not candidate or candidate.get("compatible") is False:
if candidate:
self._logger.info(
f"本地插件 {plugin_id} 不满足同步条件,跳过同步:"
f"{candidate.get('skip_reason')}"
)
return False
source_dir = Path(candidate.get("path"))
try:
if not self._system().package.sync_local(plugin_id, source_dir):
return False
self._recent_sync[plugin_id] = time.time()
self._logger.info(f"已同步本地插件 {plugin_id}{source_dir}")
return True
except Exception as error:
self._logger.error(f"同步本地插件 {plugin_id} 失败:{error}")
return False