refactor(workflow): enforce typed query boundary

This commit is contained in:
jxxghp
2026-08-28 01:16:08 +08:00
parent 2f41780893
commit b4f8736541
35 changed files with 733 additions and 223 deletions
-13
View File
@@ -5,7 +5,6 @@ from __future__ import annotations
from collections.abc import Callable
from typing import Any
AgentDataFactory = Callable[[], Any]
@@ -76,12 +75,6 @@ class DownloadHistoryPort(_PortProxy):
port_name = "download_history"
class WorkflowPort(_PortProxy):
"""工作流数据端口代理。"""
port_name = "workflow"
class PluginDataPort(_PortProxy):
"""插件数据端口代理。"""
@@ -110,7 +103,6 @@ def configure_agent_data_ports(**factories: AgentDataFactory) -> None:
"subscribe_history",
"transfer_history",
"download_history",
"workflow",
"plugin_data",
}
missing = sorted(required - factories.keys())
@@ -167,11 +159,6 @@ def get_agent_download_history_port() -> Any:
return get_agent_data_ports().download_history()
def get_agent_workflow_port() -> Any:
"""创建 Agent 工作流数据端口实例。"""
return get_agent_data_ports().workflow()
def get_agent_plugin_data_port() -> Any:
"""创建 Agent 插件数据端口实例。"""
return get_agent_data_ports().plugin_data()
+15 -5
View File
@@ -4,8 +4,10 @@ from __future__ import annotations
import json
from collections.abc import Awaitable, Callable
from dataclasses import asdict
from typing import Any, Optional
from app.application.workflow import WorkflowSnapshot
from app.schemas.media import resolve_media_identity
@@ -26,8 +28,10 @@ class ServerSharingService:
*,
subscribe_provider: Callable[[int], Any],
async_subscribe_provider: Callable[[int], Awaitable[Any]],
workflow_provider: Callable[[int], Any],
async_workflow_provider: Callable[[int], Awaitable[Any]],
workflow_provider: Callable[[int], Optional[WorkflowSnapshot]],
async_workflow_provider: Callable[
[int], Awaitable[Optional[WorkflowSnapshot]]
],
user_uuid_provider: Callable[[], str],
subscribe_sender: Callable[[dict], Any],
async_subscribe_sender: Callable[[dict], Awaitable[Any]],
@@ -68,9 +72,9 @@ class ServerSharingService:
return payload
@staticmethod
def prepare_workflow(workflow: Any) -> dict:
def prepare_workflow(workflow: WorkflowSnapshot) -> dict:
"""移除本地字段并把动作和流程编码为中心服务兼容格式。"""
workflow_dict = workflow.to_dict()
workflow_dict = asdict(workflow)
workflow_dict.pop("id", None)
workflow_dict.pop("context", None)
workflow_dict["actions"] = json.dumps(workflow_dict["actions"] or [])
@@ -78,7 +82,9 @@ class ServerSharingService:
return workflow_dict
@staticmethod
def validate_workflow(workflow: Any) -> tuple[bool, str]:
def validate_workflow(
workflow: Optional[WorkflowSnapshot],
) -> tuple[bool, str]:
"""验证工作流存在且同时包含动作与流程。"""
if not workflow:
return False, "工作流不存在"
@@ -160,6 +166,8 @@ class ServerSharingService:
valid, message = self.validate_workflow(workflow)
if not valid:
return False, message
if workflow is None:
return False, "工作流不存在"
payload = {
"share_title": share_title,
"share_comment": share_comment,
@@ -188,6 +196,8 @@ class ServerSharingService:
valid, message = self.validate_workflow(workflow)
if not valid:
return False, message
if workflow is None:
return False, "工作流不存在"
payload = {
"share_title": share_title,
"share_comment": share_comment,
+72 -15
View File
@@ -1,11 +1,12 @@
"""工作流状态与定义写操作应用用例。"""
from dataclasses import dataclass
import json
from collections.abc import Awaitable
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Callable, Mapping, Optional, Protocol, TypeVar
from typing import Any, Callable, List, Mapping, Optional, Protocol, TypeVar
from app.schemas.common import JsonData
WORKFLOW_TRIGGER_TIMER = "timer"
WORKFLOW_TRIGGER_EVENT = "event"
@@ -17,6 +18,30 @@ SUPPORTED_WORKFLOW_TRIGGERS = {
}
@dataclass(frozen=True, slots=True)
class WorkflowSnapshot:
"""工作流查询返回的脱离数据库会话的冻结快照。"""
id: int
name: str
description: Optional[str]
timer: Optional[str]
trigger_type: Optional[str]
event_type: Optional[str]
event_conditions: Mapping[str, JsonData]
state: str
current_action: Optional[str]
result: Optional[str]
run_count: Optional[int]
actions: tuple[Mapping[str, JsonData], ...]
flows: tuple[Mapping[str, JsonData], ...]
context: Mapping[str, JsonData]
execution_config: Mapping[str, JsonData]
execution_state: Mapping[str, JsonData]
add_time: Optional[str]
last_time: Optional[str]
class WorkflowRuntime(Protocol):
"""声明宿主入口与 Chain 消费的工作流运行时能力。"""
@@ -40,7 +65,7 @@ class WorkflowRuntime(Protocol):
"""移除全部或指定工作流的事件触发器。"""
...
def update_workflow_event(self, workflow: Any) -> None:
def update_workflow_event(self, workflow: WorkflowSnapshot) -> None:
"""按最新定义刷新工作流事件触发器。"""
...
@@ -87,15 +112,31 @@ def get_workflow_manager() -> WorkflowRuntime:
return _workflow_runtime_provider()
class AsyncWorkflowQueryRepository(Protocol):
"""工作流查询用例需要的异步读取端口。"""
class WorkflowQueryRepository(Protocol):
"""工作流查询用例需要的同步与异步快照端口。"""
async def async_list(self) -> list[Any]:
"""读取全部工作流。"""
def get(self, workflow_id: int) -> Optional[WorkflowSnapshot]:
"""按 ID 读取工作流快照"""
...
async def async_get(self, workflow_id: int) -> Optional[Any]:
"""按 ID 读取工作流。"""
def list_enabled(self) -> List[WorkflowSnapshot]:
"""读取全部启用的工作流快照"""
...
def list_timer_enabled(self) -> List[WorkflowSnapshot]:
"""读取启用的定时工作流快照。"""
...
def list_event_enabled(self) -> List[WorkflowSnapshot]:
"""读取启用的事件工作流快照。"""
...
async def async_list(self) -> List[WorkflowSnapshot]:
"""异步读取全部工作流快照。"""
...
async def async_get(self, workflow_id: int) -> Optional[WorkflowSnapshot]:
"""异步按 ID 读取工作流快照。"""
...
@@ -114,18 +155,34 @@ class WorkflowCachePort(Protocol):
class WorkflowQueryService:
"""提供工作流列表和详情查询,隔离 API 与数据库会话。"""
def __init__(self, repository: AsyncWorkflowQueryRepository) -> None:
"""保存请求级异步查询端口。"""
def __init__(self, repository: WorkflowQueryRepository) -> None:
"""保存可返回脱离会话快照的查询端口。"""
self._repository = repository
async def list(self) -> list[Any]:
"""返回全部工作流。"""
async def list(self) -> List[WorkflowSnapshot]:
"""返回全部工作流快照"""
return await self._repository.async_list()
async def get(self, workflow_id: int) -> Optional[Any]:
"""返回指定工作流。"""
async def get(self, workflow_id: int) -> Optional[WorkflowSnapshot]:
"""返回指定工作流快照"""
return await self._repository.async_get(workflow_id)
def get_sync(self, workflow_id: int) -> Optional[WorkflowSnapshot]:
"""同步返回指定工作流快照。"""
return self._repository.get(workflow_id)
def list_enabled(self) -> List[WorkflowSnapshot]:
"""同步返回全部启用的工作流快照。"""
return self._repository.list_enabled()
def list_timer_enabled(self) -> List[WorkflowSnapshot]:
"""同步返回启用的定时工作流快照。"""
return self._repository.list_timer_enabled()
def list_event_enabled(self) -> List[WorkflowSnapshot]:
"""同步返回启用的事件工作流快照。"""
return self._repository.list_event_enabled()
_configured_workflow_query: WorkflowQueryService | None = None