ForcePilot/backend/package/yuxi/external_systems/infrastructure/container.py
Kris 9e095ee5ec feat: 完成多批次功能迭代与优化
本次提交包含大量功能完善与修复:
1. 新增多类操作的操作人审计字段与环境校验逻辑
2. 修复多集成的认证头生成逻辑,支持动态token类型
3. 新增通知派发适配器框架与多渠道实现
4. 完善健康检查返回结果与指标监控项
5. 优化JQL校验逻辑与资产导入规则
6. 补充各类DTO字段与持久层映射关系
7. 修复并优化测试用例、配额、密钥轮转等服务逻辑
2026-07-11 05:39:41 +08:00

407 lines
16 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 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.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,
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 类通知渠道派发。
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)
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_dispatcher,
)
notification_service = NotificationService(
repos,
runtime,
uow,
event_publisher,
notification_dispatcher,
)
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,
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) → 执行结果。
# 回调签名与执行器不直接兼容(回调收 slug执行器需 ExecutableTool
# 需通过 repos.tool.get_by_slug 解析 slug对齐 tool_service 执行路径。
# 通过 execution_context 传入 operation="test",使测试用例执行记录
# 与真实业务调用operation="execute")区分开。
# 当测试用例指定 timeout 时,使用 dataclasses.replace 临时覆盖工具级 timeout。
async def _execute_callback(
tool_slug: str,
parameters: dict[str, Any],
env_key: str | None,
timeout: int | 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": "sync_job",
"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_adapter_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)