ForcePilot/backend/package/yuxi/external_systems/infrastructure/container.py
Kris 00a5577aac chore: 整理并合并近期多模块迭代变更
本次提交包含了大量跨模块的功能增强、bug修复与代码优化:
1.  清理冗余空行与导入格式,统一代码风格
2.  新增集成超时异常类,替换原生TimeoutError避免穿透核心层
3.  扩展审计日志DTO与用例,新增变更字段追踪能力
4.  完善各类仓储接口,新增批量操作、时间范围过滤支持
5.  重构健康检查返回格式,统一使用status字段替代冗余的reachable/healthy
6.  扩展仪表盘与各类服务端口,新增待办统计、系统dashboard等能力
7.  优化批量操作与事务处理,新增保存点支持隔离失败操作
8.  修复配额管理阈值计算逻辑,支持自定义告警阈值
9.  统一认证类型错误提示,优化插件注册校验逻辑
10. 扩展SOAP适配器预览能力,兼容bytes类型输入
11. 新增批量恢复、批量硬删除回收站资源的接口与DTO
12. 优化测试用例断言逻辑,兼容前端操作符别名与字段名差异
2026-07-11 21:38:08 +08:00

435 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,
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.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,
)
__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,
ssrf_validator: Any | 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。
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, 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,
ssrf_validator=ssrf_validator,
)
import_service = ImportService(repos, runtime, adapter_registry, uow, event_publisher, tool_service)
integration_tool_service = IntegrationToolService(
repos,
runtime,
uow,
event_publisher,
tool_service,
adapter_registry,
)
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_dispatcher,
)
notification_service = NotificationService(
repos,
runtime,
uow,
event_publisher,
notification_dispatcher,
)
notification_channel_service = NotificationChannelService(
repos,
runtime,
uow,
event_publisher,
notification_dispatcher,
)
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,
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,
uow,
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/uow/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/uow/event_publisher/tool_executor 等重型依赖。使用该工厂避免
每次统计/健康查询都构造完整 24-service UseCases 容器。
Args:
db: SQLAlchemy 异步会话(请求级)。
Returns:
装配完成的 AdapterStatsServicePort 实例。
"""
repos = create_repositories(db)
return AdapterStatsService(repos)