refactor(sdk): isolate plugin query persistence

This commit is contained in:
InfinityPacer
2026-08-28 00:15:46 +08:00
parent d1d95dded7
commit 9575a523e8
18 changed files with 825 additions and 447 deletions
+41 -24
View File
@@ -1441,8 +1441,8 @@
"runtime_only": true
}
},
"edge_count": 6924,
"edge_sha256": "d5471439086bcfdd8060b77670af6f821a34e3a864ba7e2cb7230c599a1cb3bb",
"edge_count": 6940,
"edge_sha256": "8ff91be099f1230655ceeb006bd1bf604063054ddb0721b3e887c956ea1251fb",
"edges": [
"app -> app.runtime",
"app -> app.runtime.compat",
@@ -4008,10 +4008,6 @@
"app.application.configuration -> app.schemas.types",
"app.application.dashboard -> app.schemas",
"app.application.dashboard -> app.schemas.dashboard",
"app.application.data_query -> app.application",
"app.application.data_query -> app.application.database",
"app.application.data_query -> app.schemas",
"app.application.data_query -> app.schemas.query",
"app.application.directory -> app.adapters",
"app.application.directory -> app.adapters.system",
"app.application.directory -> app.adapters.system.host",
@@ -4264,6 +4260,10 @@
"app.application.plugin.transaction -> app.application.database",
"app.application.plugin.transaction -> app.application.plugin",
"app.application.plugin.transaction -> app.application.plugin.identity",
"app.application.query -> app.application",
"app.application.query -> app.application.database",
"app.application.query -> app.schemas",
"app.application.query -> app.schemas.query",
"app.application.recognition -> app.application",
"app.application.recognition -> app.application.configuration",
"app.application.recognition -> app.schemas",
@@ -5135,18 +5135,6 @@
"app.db.adapters.chain -> app.db.oper.transferpending",
"app.db.adapters.chain -> app.db.oper.transfersettlementreceipt",
"app.db.adapters.chain -> app.db.uow",
"app.db.adapters.data_query -> app.application",
"app.db.adapters.data_query -> app.application.data_query",
"app.db.adapters.data_query -> app.db",
"app.db.adapters.data_query -> app.db.base",
"app.db.adapters.data_query -> app.db.models",
"app.db.adapters.data_query -> app.db.models.downloadhistory",
"app.db.adapters.data_query -> app.db.models.subscribe",
"app.db.adapters.data_query -> app.db.models.subscribehistory",
"app.db.adapters.data_query -> app.db.models.transferhistory",
"app.db.adapters.data_query -> app.schemas",
"app.db.adapters.data_query -> app.schemas.query",
"app.db.adapters.data_query -> app.schemas.types",
"app.db.adapters.download -> app.db",
"app.db.adapters.download -> app.db.oper",
"app.db.adapters.download -> app.db.oper.downloadfailure",
@@ -5176,6 +5164,16 @@
"app.db.adapters.plugininstallation -> app.db.models",
"app.db.adapters.plugininstallation -> app.db.models.pluginidentity",
"app.db.adapters.plugininstallation -> app.db.models.plugininstallation",
"app.db.adapters.query -> app.application",
"app.db.adapters.query -> app.application.query",
"app.db.adapters.query -> app.db",
"app.db.adapters.query -> app.db.oper",
"app.db.adapters.query -> app.db.oper.downloadhistory",
"app.db.adapters.query -> app.db.oper.subscribe",
"app.db.adapters.query -> app.db.oper.subscribehistory",
"app.db.adapters.query -> app.db.oper.transferhistory",
"app.db.adapters.query -> app.schemas",
"app.db.adapters.query -> app.schemas.query",
"app.db.adapters.site -> app.db",
"app.db.adapters.site -> app.db.oper",
"app.db.adapters.site -> app.db.oper.site",
@@ -5355,7 +5353,10 @@
"app.db.oper.downloadhistory -> app.db.base",
"app.db.oper.downloadhistory -> app.db.models",
"app.db.oper.downloadhistory -> app.db.models.downloadhistory",
"app.db.oper.downloadhistory -> app.db.oper",
"app.db.oper.downloadhistory -> app.db.oper.query",
"app.db.oper.downloadhistory -> app.schemas",
"app.db.oper.downloadhistory -> app.schemas.query",
"app.db.oper.downloadhistory -> app.schemas.types",
"app.db.oper.mediaserver -> app.db",
"app.db.oper.mediaserver -> app.db.base",
@@ -5380,6 +5381,11 @@
"app.db.oper.pluginidentity -> app.db.base",
"app.db.oper.pluginidentity -> app.db.models",
"app.db.oper.pluginidentity -> app.db.models.pluginidentity",
"app.db.oper.query -> app.db",
"app.db.oper.query -> app.db.base",
"app.db.oper.query -> app.schemas",
"app.db.oper.query -> app.schemas.query",
"app.db.oper.query -> app.schemas.types",
"app.db.oper.site -> app.db",
"app.db.oper.site -> app.db.base",
"app.db.oper.site -> app.db.models",
@@ -5395,12 +5401,19 @@
"app.db.oper.subscribe -> app.db.models",
"app.db.oper.subscribe -> app.db.models.subscribe",
"app.db.oper.subscribe -> app.db.models.subscribehistory",
"app.db.oper.subscribe -> app.db.oper",
"app.db.oper.subscribe -> app.db.oper.query",
"app.db.oper.subscribe -> app.schemas",
"app.db.oper.subscribe -> app.schemas.query",
"app.db.oper.subscribe -> app.schemas.types",
"app.db.oper.subscribehistory -> app.db",
"app.db.oper.subscribehistory -> app.db.base",
"app.db.oper.subscribehistory -> app.db.models",
"app.db.oper.subscribehistory -> app.db.models.subscribehistory",
"app.db.oper.subscribehistory -> app.db.oper",
"app.db.oper.subscribehistory -> app.db.oper.query",
"app.db.oper.subscribehistory -> app.schemas",
"app.db.oper.subscribehistory -> app.schemas.query",
"app.db.oper.systemconfig -> app.db",
"app.db.oper.systemconfig -> app.db.base",
"app.db.oper.systemconfig -> app.db.models",
@@ -5417,7 +5430,10 @@
"app.db.oper.transferhistory -> app.db.base",
"app.db.oper.transferhistory -> app.db.models",
"app.db.oper.transferhistory -> app.db.models.transferhistory",
"app.db.oper.transferhistory -> app.db.oper",
"app.db.oper.transferhistory -> app.db.oper.query",
"app.db.oper.transferhistory -> app.schemas",
"app.db.oper.transferhistory -> app.schemas.query",
"app.db.oper.transferhistory -> app.schemas.types",
"app.db.oper.transferpending -> app.db",
"app.db.oper.transferpending -> app.db.base",
@@ -7769,7 +7785,7 @@
"app.sdk.plugins -> app.runtime.extensions.module_manager",
"app.sdk.plugins -> app.runtime.extensions.plugin_manager",
"app.sdk.queries -> app.application",
"app.sdk.queries -> app.application.data_query",
"app.sdk.queries -> app.application.query",
"app.sdk.queries -> app.schemas",
"app.sdk.queries -> app.schemas.query",
"app.sdk.security -> app.adapters",
@@ -7953,7 +7969,6 @@
"app.startup.initializers.modules -> app.application.chain.data",
"app.startup.initializers.modules -> app.application.chain.events",
"app.startup.initializers.modules -> app.application.configuration",
"app.startup.initializers.modules -> app.application.data_query",
"app.startup.initializers.modules -> app.application.database",
"app.startup.initializers.modules -> app.application.history",
"app.startup.initializers.modules -> app.application.image",
@@ -7966,6 +7981,7 @@
"app.startup.initializers.modules -> app.application.plugin",
"app.startup.initializers.modules -> app.application.plugin.runtime",
"app.startup.initializers.modules -> app.application.plugin.transaction",
"app.startup.initializers.modules -> app.application.query",
"app.startup.initializers.modules -> app.application.security",
"app.startup.initializers.modules -> app.application.security.auth",
"app.startup.initializers.modules -> app.application.security.passkey",
@@ -7996,11 +8012,11 @@
"app.startup.initializers.modules -> app.db",
"app.startup.initializers.modules -> app.db.adapters",
"app.startup.initializers.modules -> app.db.adapters.chain",
"app.startup.initializers.modules -> app.db.adapters.data_query",
"app.startup.initializers.modules -> app.db.adapters.download",
"app.startup.initializers.modules -> app.db.adapters.outbox",
"app.startup.initializers.modules -> app.db.adapters.pluginidentity",
"app.startup.initializers.modules -> app.db.adapters.plugininstallation",
"app.startup.initializers.modules -> app.db.adapters.query",
"app.startup.initializers.modules -> app.db.adapters.site",
"app.startup.initializers.modules -> app.db.adapters.subscription",
"app.startup.initializers.modules -> app.db.adapters.transaction",
@@ -8369,7 +8385,7 @@
"app.workflow.actions.transfer_file -> app.workflow",
"app.workflow.actions.transfer_file -> app.workflow.actions"
],
"module_count": 848,
"module_count": 849,
"modules": [
"app",
"app.adapters",
@@ -8629,7 +8645,6 @@
"app.application.commands",
"app.application.configuration",
"app.application.dashboard",
"app.application.data_query",
"app.application.database",
"app.application.directory",
"app.application.download",
@@ -8678,6 +8693,7 @@
"app.application.plugin.runtime",
"app.application.plugin.source",
"app.application.plugin.transaction",
"app.application.query",
"app.application.recognition",
"app.application.rss",
"app.application.rules",
@@ -8762,11 +8778,11 @@
"app.db",
"app.db.adapters",
"app.db.adapters.chain",
"app.db.adapters.data_query",
"app.db.adapters.download",
"app.db.adapters.outbox",
"app.db.adapters.pluginidentity",
"app.db.adapters.plugininstallation",
"app.db.adapters.query",
"app.db.adapters.site",
"app.db.adapters.subscription",
"app.db.adapters.transaction",
@@ -8819,6 +8835,7 @@
"app.db.oper.passkey",
"app.db.oper.plugindata",
"app.db.oper.pluginidentity",
"app.db.oper.query",
"app.db.oper.site",
"app.db.oper.subscribe",
"app.db.oper.subscribehistory",
-9
View File
@@ -467,18 +467,12 @@
"app/db/models/workflow.py": {
"I001": 1
},
"app/db/oper/downloadhistory.py": {
"I001": 1
},
"app/db/oper/message.py": {
"I001": 1
},
"app/db/oper/site.py": {
"I001": 1
},
"app/db/oper/subscribe.py": {
"I001": 1
},
"app/db/oper/systemconfig.py": {
"I001": 1
},
@@ -1128,9 +1122,6 @@
"tests/test_agent_lazy_runtime_boundary.py": {
"F401": 1
},
"tests/test_agent_lifecycle.py": {
"I001": 1
},
"tests/test_agent_llm_capability.py": {
"I001": 1
},
+9 -5
View File
@@ -4,18 +4,19 @@ from unittest.mock import AsyncMock, MagicMock
import pytest
import app.agent.orchestrator as agent_module
from app.application.messaging.agent import (
create_web_agent_background_task,
shutdown_web_agent_background_tasks,
)
from app.agent.memory import MemoryManager
from app.agent.orchestrator import (
AGENT_SESSION_QUEUE_MAX_SIZE,
AgentManager,
AgentManagerQueueFullError,
AgentManagerUnavailableError,
)
from app.agent.memory import MemoryManager
from app.agent.tools.base import reopen_blocking_executors
from app.application.messaging.agent import (
create_web_agent_background_task,
shutdown_web_agent_background_tasks,
)
from app.application.query import get_configured_data_query_service
from app.startup.initializers import agent as agent_initializer
from app.startup.initializers import modules as modules_initializer
@@ -202,6 +203,9 @@ async def test_agent_initialization_failure_does_not_stop_module_startup(
assert runtime.workflow.system_config() is (
modules_initializer.get_configured_system_config()
)
query_page = get_configured_data_query_service().list_subscriptions({"ids": [-1]})
assert query_page.items == []
assert query_page.total == 0
finally:
await modules_initializer.stop_database_worker()
+65 -4
View File
@@ -61,9 +61,9 @@ class _RecordingExecutor:
@pytest.fixture
def query_sdk(db, monkeypatch):
"""装配真实数据查询适配器,并把 SDK 绑定到本用例的 SQLite 服务。"""
from app.application import data_query as data_query_module
from app.application.data_query import DataQueryService
from app.db.adapters.data_query import SqlAlchemyDataQueryAdapter
from app.application import query as data_query_module
from app.application.query import DataQueryService
from app.db.adapters.query import SqlAlchemyDataQueryAdapter
from app.db.session import SessionFactory
from app.sdk import queries as sdk
@@ -180,7 +180,7 @@ def _transfer_history(
episodes: str | None = "E01",
download_hash: str | None = "hash-1",
episode_group: str | None = None,
status: bool = True,
status: bool | None = True,
date: str = "2026-08-27 10:00:00",
mtype: str = MediaType.TV.value,
) -> TransferHistoryModel:
@@ -630,6 +630,67 @@ def test_snapshots_normalize_legacy_identity_and_transfer_status():
assert snapshot.status is False
def test_transfer_failure_filter_includes_legacy_null_status(db, query_sdk):
"""失败筛选与快照语义一致,包含状态尚未回填的旧整理记录。"""
sdk, _executor = query_sdk
legacy_row = db.add(
_transfer_history(
"Legacy null status",
media_id="legacy-status",
src="/legacy/status-src.mkv",
dest="/legacy/status-dest.mkv",
status=None,
)
)
legacy_row.status = None
db.session.commit()
page = sdk.list_transfer_history(
TransferHistoryFilter(
media_source=TMDB,
media_id="legacy-status",
status=False,
)
)
assert [item.id for item in page.items] == [legacy_row.id]
assert page.items[0].status is False
def test_transfer_required_identity_excludes_unparseable_legacy_source(db, query_sdk):
"""有效身份筛选不得返回随后会被快照降级为空身份的脏来源。"""
sdk, _executor = query_sdk
valid_row = db.add(
_transfer_history(
"Valid dynamic source",
media_id="valid-dynamic",
src="/valid/source.mkv",
dest="/valid/dest.mkv",
)
)
invalid_row = db.add(
_transfer_history(
"Invalid legacy source",
media_id="invalid-source",
src="/invalid/source.mkv",
dest="/invalid/dest.mkv",
)
)
valid_row.media_source = "plugin-source"
invalid_row.media_source = "invalid!"
db.session.commit()
page = sdk.list_transfer_history(
TransferHistoryFilter(
ids=(valid_row.id, invalid_row.id),
require_media_identity=True,
)
)
assert [item.id for item in page.items] == [valid_row.id]
assert page.items[0].media_source == MediaSource("plugin-source")
def test_query_snapshots_are_owned_by_the_sdk_contract_module():
"""公开查询返回值由独立快照定义,不复用宿主写入或 API 响应模型。"""
snapshots = (