ForcePilot/backend/package/yuxi/external_systems/infrastructure/container.py

345 lines
14 KiB
Python
Raw Normal View History

"""infrastructure/container.pyuse_cases service 装配容器。
本模块是六边形架构的"唯一装配点"设计方案 §8.1负责
- persistence 仓储 / runtime 横切关注点 / framework 单例 装配为 24 use_cases service
- 对外暴露两个 factory
* ``create_use_cases``底层 factory由调用方注入全部依赖
* ``create_use_cases_from_db``请求级 factory封装三层创建repos + runtime + use_cases
边界规范
- 本模块是**唯一允许 import use_cases service 实现类**的地方
- ``UseCases`` dataclass 字段类型使用驱动端口 Protocol非实现类
- service 构造调用使用**实际构造函数签名**已逐个核对 use_cases/*.py ``__init__``
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from yuxi.external_systems.adapters.persistence import (
Repositories,
SqlAlchemyUnitOfWork,
create_repositories,
)
from yuxi.external_systems.core.contracts import (
AdapterRegistry,
EventPublisher,
ToolExecutor,
UnitOfWork,
)
from yuxi.external_systems.exceptions import EntityNotFoundError
from yuxi.external_systems.framework.runtime import (
InMemoryEventPublisher,
RepositoryPorts,
RuntimeServices,
TestCaseRunnerImpl,
create_runtime_services,
create_tool_executor,
)
from yuxi.external_systems.use_cases.context_builder import ContextBuilder
# 驱动端口类型(供 UseCases dataclass 字段标注,非实现类)
from yuxi.external_systems.use_cases.ports import (
AccessRuleServicePort,
AdapterStatsServicePort,
AlertServicePort,
AssetServicePort,
AuditLogServicePort,
ContextBuilderPort,
DashboardServicePort,
EnvironmentServicePort,
ExecutionServicePort,
HealthCheckServicePort,
ImportServicePort,
IntegrationToolServicePort,
MetricServicePort,
NotificationChannelServicePort,
NotificationServicePort,
QuotaServicePort,
SecretRotationServicePort,
SystemServicePort,
TestCaseServicePort,
TestRegressionServicePort,
TokenServicePort,
ToolServicePort,
WebhookEventHandlerPort,
WebhookSubscriptionServicePort,
)
from yuxi.external_systems.use_cases.ports.trash_service_port import TrashServicePort
# 唯一允许 import service 实现类的地方(设计方案 §8.1
from yuxi.external_systems.use_cases.services.access_rule_service import AccessRuleService
from yuxi.external_systems.use_cases.services.adapter_stats_service import AdapterStatsService
from yuxi.external_systems.use_cases.services.alert_service import AlertService
from yuxi.external_systems.use_cases.services.asset_service import AssetService
from yuxi.external_systems.use_cases.services.audit_log_service import AuditLogService
from yuxi.external_systems.use_cases.services.dashboard_service import DashboardService
from yuxi.external_systems.use_cases.services.environment_service import EnvironmentService
from yuxi.external_systems.use_cases.services.execution_service import ExecutionService
from yuxi.external_systems.use_cases.services.health_check_service import HealthCheckService
from yuxi.external_systems.use_cases.services.import_service import ImportService
from yuxi.external_systems.use_cases.services.integration_tool_service import (
IntegrationToolService,
)
from yuxi.external_systems.use_cases.services.metric_service import MetricService
from yuxi.external_systems.use_cases.services.notification_channel_service import (
NotificationChannelService,
)
from yuxi.external_systems.use_cases.services.notification_service import NotificationService
from yuxi.external_systems.use_cases.services.quota_service import QuotaService
from yuxi.external_systems.use_cases.services.secret_rotation_service import (
SecretRotationService,
)
from yuxi.external_systems.use_cases.services.system_service import SystemService
from yuxi.external_systems.use_cases.services.test_case_service import TestCaseService
from yuxi.external_systems.use_cases.services.test_regression_service import (
TestRegressionService,
)
from yuxi.external_systems.use_cases.services.token_service import TokenService
from yuxi.external_systems.use_cases.services.tool_service import ToolService
from yuxi.external_systems.use_cases.services.trash_service import TrashService
from yuxi.external_systems.use_cases.services.webhook_event_handler import (
WebhookEventHandler,
)
from yuxi.external_systems.use_cases.services.webhook_subscription_service import (
WebhookSubscriptionService,
)
from .config import default_adapter_registry
__all__ = [
"UseCases",
"create_use_cases",
"create_use_cases_from_db",
]
@dataclass
class UseCases:
"""一组 use_cases service 实例(以端口类型标注),由 factory 创建。
字段类型使用驱动端口 Protocol非实现类保证调用方只依赖端口
"""
context_builder: ContextBuilderPort
system_service: SystemServicePort
tool_service: ToolServicePort
environment_service: EnvironmentServicePort
execution_service: ExecutionServicePort
token_service: TokenServicePort
audit_log_service: AuditLogServicePort
asset_service: AssetServicePort
import_service: ImportServicePort
integration_tool_service: IntegrationToolServicePort
webhook_subscription_service: WebhookSubscriptionServicePort
webhook_event_handler: WebhookEventHandlerPort
quota_service: QuotaServicePort
alert_service: AlertServicePort
notification_service: NotificationServicePort
notification_channel_service: NotificationChannelServicePort
secret_rotation_service: SecretRotationServicePort
access_rule_service: AccessRuleServicePort
adapter_stats_service: AdapterStatsServicePort
test_case_service: TestCaseServicePort
test_regression_service: TestRegressionServicePort
metric_service: MetricServicePort
health_check_service: HealthCheckServicePort
dashboard_service: DashboardServicePort
trash_service: TrashServicePort
def create_use_cases(
repos: Repositories,
runtime: RuntimeServices,
uow: UnitOfWork,
event_publisher: EventPublisher,
*,
adapter_registry: AdapterRegistry,
tool_executor: ToolExecutor,
context_builder: ContextBuilderPort | None = None,
) -> UseCases:
"""factory 函数:创建一组 use_cases service 实例。
infrastructure 层调用注入所有依赖实现类仅在此处 import规范 §8.1
Args:
repos: 仓储聚合persistence 层创建共享 db 会话
runtime: 请求级 runtime 横切关注点集合
uow: 工作单元事务边界
event_publisher: 事件发布器
adapter_registry: 适配器注册表全局只读单例
tool_executor: 工具执行器请求级实例
context_builder: 可选上下文构造器未传入时由本函数创建
``create_use_cases_from_db`` 传入已创建的实例以复用给 ToolExecutor
Returns:
装配完成的 ``UseCases`` 实例24 service + 1 context_builder
"""
context_builder = context_builder or ContextBuilder(repos, runtime)
system_service = SystemService(repos, runtime, adapter_registry, uow, event_publisher)
tool_service = ToolService(
repos,
runtime,
adapter_registry,
tool_executor,
uow,
event_publisher,
context_builder,
)
environment_service = EnvironmentService(repos, runtime, uow, event_publisher)
execution_service = ExecutionService(
repos,
runtime,
uow,
event_publisher,
tool_executor,
)
token_service = TokenService(repos, runtime, uow, event_publisher)
audit_log_service = AuditLogService(repos, runtime)
asset_service = AssetService(repos, runtime, adapter_registry, uow, event_publisher)
import_service = ImportService(repos, runtime, adapter_registry, uow, event_publisher, tool_service)
integration_tool_service = IntegrationToolService(
repos,
runtime,
uow,
event_publisher,
tool_service,
)
webhook_event_handler = WebhookEventHandler(repos, runtime, uow, event_publisher)
webhook_subscription_service = WebhookSubscriptionService(
repos,
runtime,
uow,
event_publisher,
webhook_event_handler,
)
quota_service = QuotaService(repos, runtime, uow, event_publisher)
alert_service = AlertService(repos, runtime, uow, event_publisher)
notification_service = NotificationService(repos, runtime, uow, event_publisher)
notification_channel_service = NotificationChannelService(
repos,
runtime,
uow,
event_publisher,
)
secret_rotation_service = SecretRotationService(repos, runtime, uow, event_publisher)
access_rule_service = AccessRuleService(repos, runtime, uow, event_publisher)
test_case_service = TestCaseService(repos, runtime, uow, event_publisher)
test_regression_service = TestRegressionService(repos, runtime, uow, event_publisher)
metric_service = MetricService(repos.metric)
health_check_service = HealthCheckService(repos.health_check)
adapter_stats_service = AdapterStatsService(repos)
# 纯查询用例,仅注入 repos对齐 AdapterStatsService / MetricService / HealthCheckService
dashboard_service = DashboardService(repos)
trash_service = TrashService(repos, runtime, uow, event_publisher)
return UseCases(
context_builder=context_builder,
system_service=system_service,
tool_service=tool_service,
environment_service=environment_service,
execution_service=execution_service,
token_service=token_service,
audit_log_service=audit_log_service,
asset_service=asset_service,
import_service=import_service,
integration_tool_service=integration_tool_service,
webhook_subscription_service=webhook_subscription_service,
webhook_event_handler=webhook_event_handler,
quota_service=quota_service,
alert_service=alert_service,
notification_service=notification_service,
notification_channel_service=notification_channel_service,
secret_rotation_service=secret_rotation_service,
access_rule_service=access_rule_service,
adapter_stats_service=adapter_stats_service,
test_case_service=test_case_service,
test_regression_service=test_regression_service,
metric_service=metric_service,
health_check_service=health_check_service,
dashboard_service=dashboard_service,
trash_service=trash_service,
)
def create_use_cases_from_db(db: AsyncSession) -> UseCases:
"""请求级工厂:从 db session 创建完整的 use_cases 实例。
封装三层创建逻辑repos + runtime + use_cases对调用方隐藏内部依赖结构
router 在请求开始时调用
注意``ToolExecutor`` 是请求级实例依赖请求级 recorder + metrics_collector
在此处创建不使用全局单例
Args:
db: SQLAlchemy 异步会话请求级
Returns:
装配完成的 ``UseCases`` 实例24 service + 1 context_builder
"""
# 1. 请求级 repos + UnitOfWork
repos = create_repositories(db)
uow = SqlAlchemyUnitOfWork(db)
# 2. 请求级 runtime services先不传 test_execute_callbackToolExecutor 需
# runtime.metrics_collector而回调需 ToolExecutor存在循环依赖步骤 4 回填)
runtime_repos = RepositoryPorts(
token=repos.token,
audit_log=repos.audit_log,
metric=repos.metric,
alert=repos.alert,
quota_usage=repos.quota_usage,
tool_access_rule=repos.tool_access_rule,
webhook_subscription=repos.webhook_subscription,
webhook_event=repos.webhook_event,
tool_test_case=repos.tool_test_case,
secret_rotation_policy=repos.secret_rotation_policy,
)
runtime = create_runtime_services(runtime_repos)
event_publisher = InMemoryEventPublisher()
# 3. 请求级 ContextBuilderToolExecutor 系统级路径依赖它构造 RuntimeContext
context_builder = ContextBuilder(repos, runtime)
# 4. 请求级 ToolExecutor注入请求级 recorder + metrics_collector + context_builder
tool_executor = create_tool_executor(
recorder=repos.execution_recorder,
metrics_collector=runtime.metrics_collector,
context_builder=context_builder,
)
# 5. 回填 TestCaseRunner 执行回调(两阶段创建,打破循环依赖)
# 将 ToolExecutor.execute_external_tool 包装为 ExecuteCallback
# (tool_slug, parameters, env_key) → 执行结果。
# 回调签名与执行器不直接兼容(回调收 slug执行器需 ExecutableTool
# 需通过 repos.tool.get_by_slug 解析 slug对齐 tool_service 执行路径。
async def _execute_callback(
tool_slug: str,
parameters: dict[str, Any],
env_key: str | None,
) -> Any:
tool = await repos.tool.get_by_slug(tool_slug)
if tool is None:
raise EntityNotFoundError(f"外部工具 {tool_slug} 不存在")
env = {"YUXI_ACTIVE_ENV": env_key} if env_key else None
return await tool_executor.execute_external_tool(tool, parameters, env=env)
runtime.test_case_runner = TestCaseRunnerImpl(
repos.tool_test_case,
_execute_callback,
)
# 6. 装配 use_cases复用步骤 3 创建的 context_builder避免重复实例化
return create_use_cases(
repos,
runtime,
uow,
event_publisher,
adapter_registry=default_adapter_registry,
tool_executor=tool_executor,
context_builder=context_builder,
)