ForcePilot/backend/package/yuxi/external_systems/infrastructure/container.py
Kris 08617091dc refactor: 整理项目包结构与导入路径
- 新增多个业务域的__init__.py模块文件,规范包导出结构
- 调整多个DTO文件的导入路径,统一模块组织方式
- 移除测试文件中多余的空行与导入语句
- 优化部分业务模块的包层级划分
2026-07-18 02:04:03 +08:00

437 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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, replace
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from yuxi.config.app import config as app_config
from yuxi.external_systems.adapters.persistence import (
Repositories,
create_repositories,
)
from yuxi.external_systems.core.contracts import (
AdapterRegistry,
EventPublisher,
ToolExecutor,
)
from yuxi.external_systems.exceptions import EntityNotFoundError
from yuxi.external_systems.framework.adapters.notifications import (
create_notification_dispatcher,
)
from yuxi.external_systems.framework.registry import default_registry
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 yuxi.storage.transactions import (
SqlAlchemyTransactionAdapter,
TransactionPort,
)
__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,
tx_port: TransactionPort,
event_publisher: EventPublisher,
*,
adapter_registry: AdapterRegistry,
tool_executor: ToolExecutor,
context_builder: ContextBuilderPort | None = None,
ssrf_validator: Any | None = None,
) -> UseCases:
"""factory 函数:创建一组 use_cases service 实例。
由 infrastructure 层调用,注入所有依赖。实现类仅在此处 import规范 §8.1)。
Args:
repos: 仓储聚合persistence 层创建,共享 db 会话)。
runtime: 请求级 runtime 横切关注点集合。
tx_port: 事务端口(事务边界)。
event_publisher: 事件发布器。
adapter_registry: 适配器注册表(全局只读单例)。
tool_executor: 工具执行器(请求级实例)。
context_builder: 可选,上下文构造器。未传入时由本函数创建。
``create_use_cases_from_db`` 传入已创建的实例以复用给 ToolExecutor。
ssrf_validator: 可选SSRF 校验器实例,用于 HTTP 类通知渠道派发与资产探测 URL 校验。
Returns:
装配完成的 ``UseCases`` 实例24 个 service + 1 个 context_builder
"""
context_builder = context_builder or ContextBuilder(repos, runtime)
notification_dispatcher = create_notification_dispatcher(
ssrf_validator=ssrf_validator,
)
system_service = SystemService(repos, runtime, adapter_registry, tx_port, event_publisher)
tool_service = ToolService(
repos,
runtime,
adapter_registry,
tool_executor,
tx_port,
event_publisher,
context_builder,
)
environment_service = EnvironmentService(repos, runtime, tx_port, event_publisher)
execution_service = ExecutionService(
repos,
runtime,
tx_port,
event_publisher,
tool_executor,
)
token_service = TokenService(repos, runtime, tx_port, event_publisher)
audit_log_service = AuditLogService(repos, runtime)
asset_service = AssetService(
repos,
runtime,
adapter_registry,
tx_port,
event_publisher,
ssrf_validator=ssrf_validator,
)
import_service = ImportService(repos, runtime, adapter_registry, tx_port, event_publisher, tool_service)
integration_tool_service = IntegrationToolService(
repos,
runtime,
tx_port,
event_publisher,
tool_service,
adapter_registry,
)
webhook_event_handler = WebhookEventHandler(repos, runtime, tx_port, event_publisher)
webhook_subscription_service = WebhookSubscriptionService(
repos,
runtime,
tx_port,
event_publisher,
webhook_event_handler,
)
quota_service = QuotaService(repos, runtime, tx_port, event_publisher)
alert_service = AlertService(
repos,
runtime,
tx_port,
event_publisher,
notification_dispatcher,
)
notification_service = NotificationService(
repos,
runtime,
tx_port,
event_publisher,
notification_dispatcher,
)
notification_channel_service = NotificationChannelService(
repos,
runtime,
tx_port,
event_publisher,
notification_dispatcher,
)
secret_rotation_service = SecretRotationService(repos, runtime, tx_port, event_publisher)
access_rule_service = AccessRuleService(repos, runtime, tx_port, event_publisher)
test_case_service = TestCaseService(repos, runtime, tx_port, event_publisher)
test_regression_service = TestRegressionService(repos, runtime, tx_port, 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, tx_port, 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 + 事务端口
repos = create_repositories(db)
tx_port = SqlAlchemyTransactionAdapter(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,
execution=repos.execution,
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,
)
ssrf_validator = None
if app_config.enable_ssrf_protection:
from yuxi.utils.net_security import SSRFValidator
ssrf_validator = SSRFValidator(app_config.get_ssrf_policy())
runtime = create_runtime_services(runtime_repos, ssrf_validator=ssrf_validator)
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,
access_controller=runtime.access_controller,
alert_manager=runtime.alert_manager,
)
# 5. 回填 TestCaseRunner 执行回调(两阶段创建,打破循环依赖)
# 将 ToolExecutor.execute_external_tool 包装为 ExecuteCallback
# (tool_slug, parameters, env_key, timeout, caller_id) → 执行结果。
# 回调签名与执行器不直接兼容(回调收 slug执行器需 ExecutableTool
# 需通过 repos.tool.get_by_slug 解析 slug对齐 tool_service 执行路径。
# 通过 execution_context 传入 operation="test",使测试用例执行记录
# 与真实业务调用operation="execute")区分开。
# caller_id 透传到 execution_context.caller使 ext_tool_executions
# 记录真实触发者(用户 uid 或定时任务标识 "system")。
# 当测试用例指定 timeout 时,使用 dataclasses.replace 临时覆盖工具级 timeout。
async def _execute_callback(
tool_slug: str,
parameters: dict[str, Any],
env_key: str | None,
timeout: int | None = None,
caller_id: str | None = None,
) -> Any:
tool = await repos.tool.get_by_slug(tool_slug)
if tool is None:
raise EntityNotFoundError(f"外部工具 {tool_slug} 不存在")
if timeout is not None and timeout > 0:
tool = replace(tool, timeout=timeout)
env = {"YUXI_ACTIVE_ENV": env_key} if env_key else None
return await tool_executor.execute_external_tool(
tool,
parameters,
env=env,
execution_context={
"caller": caller_id or "system",
"operation": "test",
"env_key": env_key or "default",
},
)
runtime.test_case_runner = TestCaseRunnerImpl(
repos.tool_test_case,
_execute_callback,
)
# 6. 装配 use_cases复用步骤 3 创建的 context_builder避免重复实例化
return create_use_cases(
repos,
runtime,
tx_port,
event_publisher,
adapter_registry=default_registry,
tool_executor=tool_executor,
context_builder=context_builder,
ssrf_validator=ssrf_validator,
)
def create_dashboard_service_from_db(db: AsyncSession) -> DashboardServicePort:
"""请求级工厂:为 Dashboard 只读查询创建轻量级 service。
Dashboard 是纯聚合查询,无需 runtime/tx_port/event_publisher/tool_executor 等
重型依赖。使用该工厂可避免每个卡片请求都构造完整的 use_cases 容器,
降低延迟与资源消耗。
Args:
db: SQLAlchemy 异步会话(请求级)。
Returns:
装配完成的 DashboardServicePort 实例。
"""
repos = create_repositories(db)
return DashboardService(repos)
def create_adapter_stats_service_from_db(db: AsyncSession) -> AdapterStatsServicePort:
"""请求级工厂:为 AdapterStats 只读查询创建轻量级 service。
AdapterStatsService 仅依赖 Repositories纯查询用例无副作用无需
runtime/tx_port/event_publisher/tool_executor 等重型依赖。使用该工厂避免
每次统计/健康查询都构造完整 24-service UseCases 容器。
Args:
db: SQLAlchemy 异步会话(请求级)。
Returns:
装配完成的 AdapterStatsServicePort 实例。
"""
repos = create_repositories(db)
return AdapterStatsService(repos)