包含以下变更: 1. 重构微信WOC账户模型,移除固定DEFAULT_ACCOUNT 2. 新增路由绑定管理、目录搜索导出能力 3. 扩展消息查询与出站管理过滤条件 4. 新增SSE事件广播、敏感字段/配置作用域注册表 5. 新增审计日志与配对过期定时任务 6. 优化会话处理与参数校验逻辑 7. 修复sender_id校验与outbox序列化问题
1239 lines
59 KiB
Python
1239 lines
59 KiB
Python
"""渠道用例服务聚合与请求级 factory。
|
||
|
||
本模块是编排层的用例装配点,负责将请求级被驱动适配器、
|
||
应用级单例(注册中心等)与领域服务装配为 ``ChannelUseCases`` 聚合
|
||
(含 6 个用例服务实例与 18 个类型化领域端口绑定),对齐
|
||
external_systems 的 ``UseCases`` + ``create_use_cases_from_db(db)`` 模式。
|
||
|
||
装配顺序:
|
||
1. ``create_driven_adapters(db)`` 创建被驱动适配器聚合
|
||
2. 从 DI 容器获取应用级单例(注册中心等)
|
||
3. 按依赖图拓扑顺序构造领域服务
|
||
4. 通过 ``Pipeline.create()`` 创建管道
|
||
5. 实例化用例服务并注入依赖
|
||
"""
|
||
|
||
# ruff: noqa: I001
|
||
# 本文件为组合根,200+ 行导入按装配语义分组(管道/用例/DTO/适配器/注册表/领域服务/DI),
|
||
# 与 isort 的字母序合并规则冲突,故豁免 I001 以保留可读性。
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import Iterable
|
||
from dataclasses import dataclass
|
||
from typing import Any
|
||
|
||
from arq import ArqRedis
|
||
from redis.asyncio import Redis
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from yuxi.channels.adapters import DrivenAdapters, create_driven_adapters
|
||
from yuxi.channels.adapters.default_content_moderation_adapter import (
|
||
DefaultContentModerationAdapter,
|
||
)
|
||
from yuxi.channels.adapters.inmemory_realtime_metrics_adapter import (
|
||
InmemoryRealtimeMetricsAdapter,
|
||
)
|
||
from yuxi.channels.application.circuit_breaker.channel_circuit_breaker import (
|
||
ChannelCircuitBreaker,
|
||
)
|
||
from yuxi.channels.application.executor.channel_tool_executor import ChannelToolExecutor
|
||
from yuxi.channels.application.executor.message_operation_executor import (
|
||
MessageOperationExecutor,
|
||
)
|
||
from yuxi.channels.application.health.channel_probe import ChannelProbe
|
||
from yuxi.channels.application.health.health_aggregator import HealthAggregator
|
||
from yuxi.channels.application.lifecycle.plugin_lifecycle_manager import (
|
||
PluginLifecycleManager,
|
||
)
|
||
from yuxi.channels.application.lifecycle.plugin_loader import PluginLoader
|
||
from yuxi.channels.application.lifecycle.config_scope_registry import (
|
||
ConfigScopeRegistry,
|
||
)
|
||
from yuxi.channels.application.lifecycle.sensitive_field_registry import (
|
||
SensitiveFieldRegistry,
|
||
)
|
||
from yuxi.channels.application.pipeline.control_plane.audit_context_builder import (
|
||
AuditContextBuilder,
|
||
)
|
||
from yuxi.channels.application.pipeline.control_plane.control_plane_pipeline import (
|
||
ControlPlanePipeline,
|
||
)
|
||
|
||
# 管道
|
||
from yuxi.channels.application.pipeline.inbound.inbound_pipeline import InboundPipeline
|
||
from yuxi.channels.application.pipeline.outbound.outbound_pipeline import (
|
||
OutboundPipeline,
|
||
)
|
||
|
||
# framework 层应用级单例(健康聚合器、插件生命周期管理器)
|
||
from yuxi.channels.application.executor.pre_tool_call_trusted_injector import (
|
||
PreToolCallTrustedInjector,
|
||
)
|
||
|
||
# 用例服务实现(唯一允许 import 用例实现类的地方)
|
||
from yuxi.channels.application.usecase.admin_message_service import AdminMessageService
|
||
from yuxi.channels.application.usecase.channel_control_service import (
|
||
ChannelControlService,
|
||
)
|
||
from yuxi.channels.application.usecase.content_review_query_service import (
|
||
ContentReviewQueryService,
|
||
)
|
||
from yuxi.channels.application.usecase.health_check_service import HealthCheckService
|
||
from yuxi.channels.application.usecase.inbound_message_service import (
|
||
InboundMessageService,
|
||
)
|
||
from yuxi.channels.application.usecase.plugin_lifecycle_service import (
|
||
PluginLifecycleService,
|
||
)
|
||
|
||
# DTO
|
||
from yuxi.channels.contract.dtos.channel import ChannelType
|
||
from yuxi.channels.contract.dtos.outbox import OutboxConfig, OutboxConfigHolder
|
||
|
||
# 错误类型(装配期冲突校验,INV-7 错误显式化)
|
||
from yuxi.channels.contract.errors import RuleViolationError
|
||
|
||
# 适配器协议(管道注册表键类型)
|
||
from yuxi.channels.contract.plugin.adapters.attachment_upload_adapter import (
|
||
AttachmentUploadAdapter,
|
||
)
|
||
from yuxi.channels.contract.plugin.adapters.command_adapter import CommandAdapter
|
||
from yuxi.channels.contract.plugin.adapters.content_moderation_adapter import (
|
||
ContentModerationAdapter,
|
||
)
|
||
from yuxi.channels.contract.plugin.adapters.directory_adapter import DirectoryAdapter
|
||
from yuxi.channels.contract.plugin.adapters.doctor_adapter import DoctorAdapter
|
||
from yuxi.channels.contract.plugin.adapters.inbound_adapter import InboundAdapter
|
||
from yuxi.channels.contract.plugin.adapters.lifecycle_adapter import (
|
||
LifecycleAdapter,
|
||
)
|
||
from yuxi.channels.contract.plugin.adapters.login_adapter import LoginAdapter
|
||
from yuxi.channels.contract.plugin.adapters.mention_adapter import MentionAdapter
|
||
from yuxi.channels.contract.plugin.adapters.message_ops_adapter import MessageOpsAdapter
|
||
from yuxi.channels.contract.plugin.adapters.outbound_adapter import OutboundAdapter
|
||
from yuxi.channels.contract.plugin.adapters.rich_message_adapter import RichMessageAdapter
|
||
from yuxi.channels.contract.plugin.adapters.session_adapter import SessionAdapter
|
||
from yuxi.channels.contract.plugin.adapters.status_adapter import StatusAdapter
|
||
from yuxi.channels.contract.plugin.adapters.streaming_adapter import (
|
||
StreamingAdapter,
|
||
)
|
||
from yuxi.channels.contract.plugin.adapters.tools_adapter import ToolsAdapter
|
||
from yuxi.channels.contract.plugin.adapters.webhook_test_adapter import WebhookTestAdapter
|
||
from yuxi.channels.contract.plugin.adapters.whitelist_adapter import WhitelistAdapter
|
||
from yuxi.channels.contract.plugin.adapters.wizard_adapter import WizardAdapter
|
||
from yuxi.channels.adapters import MessageOpsPortAdapter
|
||
|
||
# 适配器类型(管道注册表值类型)
|
||
from yuxi.channels.contract.ports.driven.agent_run_execution_port import (
|
||
AgentRunExecutionPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.channel_context_provider_port import (
|
||
ChannelContextProviderPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.channel_session_repository_port import (
|
||
ChannelSessionRepositoryPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.channel_tool_executor_port import (
|
||
ChannelToolExecutorPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.content_review_repository_port import (
|
||
ContentReviewRepositoryPort,
|
||
)
|
||
|
||
# EventPublisherPort(应用层通过此端口契约依赖事件发布能力,组合根绑定到 EventBus)
|
||
from yuxi.channels.contract.ports.driven.audit_log_repository_port import (
|
||
AuditLogRepositoryPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.event_publisher_port import EventPublisherPort
|
||
from yuxi.channels.contract.ports.driven.outbox_repository_port import (
|
||
OutboxRepositoryPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.pairing_repository_port import (
|
||
PairingRepositoryPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driven.queue_port import QueuePort
|
||
from yuxi.channels.contract.ports.driving.account_management_port import (
|
||
AccountManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.admin_message_port import AdminMessagePort
|
||
from yuxi.channels.contract.ports.driving.analytics_view_port import (
|
||
AnalyticsViewPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.audit_query_port import AuditQueryPort
|
||
from yuxi.channels.contract.ports.driving.capability_query_port import (
|
||
CapabilityQueryPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.channel_login_port import (
|
||
ChannelLoginPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.config_management_port import (
|
||
ConfigManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.content_review_port import ContentReviewPort
|
||
from yuxi.channels.contract.ports.driving.content_review_query_port import (
|
||
ContentReviewQueryPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.dashboard_view_port import (
|
||
DashboardViewPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.directory_port import DirectoryPort
|
||
from yuxi.channels.contract.ports.driving.doctor_port import DoctorPort
|
||
from yuxi.channels.contract.ports.driving.health_check_port import HealthCheckPort
|
||
from yuxi.channels.contract.ports.driving.inbound_message_port import (
|
||
InboundMessagePort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.message_management_port import (
|
||
MessageManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.outbox_management_port import (
|
||
OutboxManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.pairing_management_port import (
|
||
PairingManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.plugin_lifecycle_port import (
|
||
PluginLifecyclePort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.plugin_management_port import (
|
||
PluginManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.route_binding_management_port import (
|
||
RouteBindingManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.session_management_port import (
|
||
SessionManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.user_binding_port import (
|
||
UserBindingPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.whitelist_management_port import (
|
||
WhitelistManagementPort,
|
||
)
|
||
from yuxi.channels.contract.ports.driving.wizard_port import WizardPort
|
||
|
||
# 注册表(应用级单例)
|
||
from yuxi.channels.core.registry.capability_registry import CapabilityRegistry
|
||
from yuxi.channels.core.registry.command_registry import CommandRegistryTable
|
||
from yuxi.channels.core.registry.identity_resolver_registry import (
|
||
IdentityResolverRegistry,
|
||
)
|
||
from yuxi.channels.core.registry.plugin_registry import PluginRegistry
|
||
from yuxi.channels.core.registry.route_match_registry import RouteMatchRegistry
|
||
from yuxi.channels.core.registry.whitelist_registry import WhitelistRegistry
|
||
|
||
# 领域服务
|
||
from yuxi.channels.core.service.account_creation_service import (
|
||
AccountCreationService,
|
||
)
|
||
from yuxi.channels.core.service.account_lifecycle_service import (
|
||
AccountLifecycleService,
|
||
)
|
||
from yuxi.channels.core.service.ack_decision_maker import AckDecisionMaker
|
||
from yuxi.channels.core.service.bot_loop_budget_guard import BotLoopBudgetGuard
|
||
from yuxi.channels.core.service.capability_verifier import CapabilityVerifier
|
||
from yuxi.channels.core.service.command_registry import CommandRegistry
|
||
from yuxi.channels.core.service.common_command_adapter import CommonCommandAdapter
|
||
from yuxi.channels.core.service.config_manager import ConfigManager
|
||
from yuxi.channels.core.service.credential_service import CredentialService
|
||
from yuxi.channels.core.service.degradation_manager import DegradationManager
|
||
from yuxi.channels.core.service.dm_security_guard import DmSecurityGuard
|
||
from yuxi.channels.core.service.doctor_service import DoctorService
|
||
from yuxi.channels.core.service.fence_guard import FenceGuard
|
||
from yuxi.channels.core.service.identity_resolver import IdentityResolver
|
||
from yuxi.channels.core.service.mention_evaluator import MentionEvaluator
|
||
from yuxi.channels.core.service.qr_login_service import QrLoginService
|
||
from yuxi.channels.core.service.route_resolver import RouteResolver
|
||
from yuxi.channels.core.service.session_merger import SessionMerger
|
||
from yuxi.channels.core.service.status_writeback_handler import StatusWritebackHandler
|
||
from yuxi.channels.core.service.truncation_detector import TruncationDetector
|
||
from yuxi.channels.core.service.trusted_message_guard import TrustedMessageGuard
|
||
from yuxi.channels.core.service.wizard_service import WizardService
|
||
|
||
# DI 容器
|
||
from yuxi.channels.infrastructure.dependency_injection import (
|
||
DependencyInjectionContainer,
|
||
)
|
||
|
||
|
||
# 全局配置(组合根读取配置源,构造 OutboxConfig 注入应用层,§6.1)
|
||
from yuxi.config.app import config as app_config
|
||
|
||
__all__ = [
|
||
"ChannelUseCases",
|
||
"create_channel_use_cases",
|
||
]
|
||
|
||
|
||
@dataclass
|
||
class ChannelUseCases:
|
||
"""渠道用例服务聚合。
|
||
|
||
串联 ``create_driven_adapters(db)`` → 领域服务 → 管道 → 用例服务。
|
||
请求级创建,共享同一 ``db`` session。
|
||
|
||
对齐 external_systems 的 ``UseCases`` dataclass 模式。字段类型使用驱动
|
||
端口 Protocol(非实现类),保证调用方只依赖端口。
|
||
|
||
字段:
|
||
inbound_message: 入站消息用例(实现 InboundMessagePort)。
|
||
admin_message: 管理员消息用例(实现 AdminMessagePort)。
|
||
plugin_lifecycle: 插件生命周期用例(实现 PluginLifecyclePort)。
|
||
health_check: 健康检查用例(实现 HealthCheckPort)。
|
||
channel_tool_executor: 渠道工具执行器(实现 ChannelToolExecutorPort),
|
||
由 Agent 后端通过依赖注入显式获取,调用 ``executeChannelTool``
|
||
路由到对应插件的 ``executeTool``(FR-11)。
|
||
wizard: 安装向导用例(实现 WizardPort),覆盖向导步骤查询 / 校验 /
|
||
应用 / 完成与 OAuth 回调(FR-16)。
|
||
doctor: 配置诊断用例(实现 DoctorPort),覆盖诊断检查项查询 / 全量
|
||
与单项检查 / 自动修复(FR-17)。
|
||
whitelist_management: 白名单管理用例(实现 WhitelistManagementPort),
|
||
覆盖白名单查询 / 添加 / 移除(FR-18)。
|
||
directory: 目录查询用例(实现 DirectoryPort),覆盖用户与群组
|
||
搜索 / 资料与成员查询(FR-14)。
|
||
pairing_management: 配对审批管理用例(实现 PairingManagementPort),
|
||
覆盖配对记录查询 / 批准 / 拒绝 / 撤销(FR-33)。
|
||
config_management: 配置热更新管理用例(实现 ConfigManagementPort),
|
||
覆盖配置查询 / 更新 / 回滚 / 版本历史(FR-37)。
|
||
session_management: 会话管理用例(实现 SessionManagementPort),覆盖
|
||
会话合并、所有者转移、关闭与统计(FR-07 / FR-26)。
|
||
user_binding: 用户绑定用例(实现 UserBindingPort),覆盖渠道会话
|
||
统一身份的绑定 / 解绑 / 状态查询(P3 渐进式绑定,不走控制面管道)。
|
||
audit_query: 审计查询用例(实现 AuditQueryPort),覆盖审计日志查询
|
||
(FR-34)。
|
||
account_management: 账户生命周期管理用例(实现 AccountManagementPort),
|
||
覆盖账户创建 / 更新 / 删除 / 启用 / 禁用 / 查询 / 列表(AL-01~AL-04)。
|
||
login: 扫码登录用例(实现 ChannelLoginPort),覆盖二维码生成 / 状态
|
||
轮询 / 登录确认回调 / 登录态查询(QR-03~QR-05 + 运维排查)。
|
||
message_management: 消息管理用例(实现 MessageManagementPort),覆盖
|
||
会话消息查询、全文搜索、状态查询与消息运维操作(撤回 / 重发 /
|
||
附件上传,运维排查 / 审计用例)。
|
||
plugin_management: 插件生命周期管理用例(实现 PluginManagementPort),
|
||
覆盖插件加载 / 启动 / 暂停 / 恢复 / 停止 / 卸载 / 重载(FR-32),
|
||
通过控制面管道统一执行认证 / 权限 / 限流 / 分派 / 审计五阶段。
|
||
outbox_management: 发件箱管理用例(实现 OutboxManagementPort),覆盖
|
||
outbox 条目查询 / 统计 / 重投 / 死信管理与重试策略查询(OBX-01~OBX-08)。
|
||
capability_query: 能力查询用例(实现 CapabilityQueryPort),覆盖渠道
|
||
能力列表、单渠道能力详情与渠道能力画像矩阵查询
|
||
(CAP-01 / CAP-02 / CAP-03),绑定到 ``ChannelControlService``
|
||
同一实例(第 15 个类型化端口),聚合 PluginRegistry 中插件的
|
||
manifest.capabilities 声明与运行时可用性。
|
||
content_review: 内容审核用例(实现 ContentReviewPort),覆盖管理员
|
||
主动触发的 dry-run 预审核(CR-01),绑定到 ``ChannelControlService``
|
||
同一实例(第 16 个类型化端口),经控制面管道执行
|
||
``content_review/preview`` 操作。
|
||
content_review_query: 内容审核历史查询用例(实现 ContentReviewQueryPort),
|
||
覆盖审核历史列表与详情查询(CR-02 / CR-03),由独立的
|
||
``ContentReviewQueryService`` 实现,不经控制面管道(数据面语义)。
|
||
dashboard_view: 聚合视图用例(实现 DashboardViewPort),覆盖全局总览 /
|
||
账户总览 / 消息总览 / 会话总览 / 投递总览 / 实时监控指标查询
|
||
(DSB-01~DSB-04 / DSB-DELIVERY / DSB-REALTIME),绑定到
|
||
``ChannelControlService`` 同一实例(第 17 个类型化端口),跨子域
|
||
KPI 聚合,只读无副作用。
|
||
analytics_view: 分析视图用例(实现 AnalyticsViewPort),覆盖消息深度
|
||
分析 / 消息类型分布 / 会话分析 / 投递链路分析 / 投递延迟分布 /
|
||
投递漏斗分析(ANL-01 / ANL-02 / ANL-04 ~ ANL-07),绑定到
|
||
``ChannelControlService`` 同一实例(第 19 个类型化端口),经控制
|
||
面管道五阶段处理。
|
||
route_binding_management: 路由绑定管理用例(实现
|
||
RouteBindingManagementPort),覆盖路由绑定规则列表 / 创建 /
|
||
更新 / 删除 / 解析测试(route_binding/list / create / update /
|
||
delete / resolve),绑定到 ``ChannelControlService`` 同一实例
|
||
(第 20 个类型化端口),经控制面管道执行路由绑定管理操作。
|
||
"""
|
||
|
||
inbound_message: InboundMessagePort
|
||
admin_message: AdminMessagePort
|
||
plugin_lifecycle: PluginLifecyclePort
|
||
health_check: HealthCheckPort
|
||
channel_tool_executor: ChannelToolExecutorPort
|
||
|
||
# 控制面健康子集端口(ChannelControlService 实现 HealthCheckPort 的
|
||
# getSingleHealth / probeChannelDeep 控制面方法,组合根将同一实例
|
||
# 绑定到此字段,与数据面 health_check 字段分离以避免模板混淆)。
|
||
health_check_control: HealthCheckPort
|
||
|
||
# 类型化领域端口(ChannelControlService 同时实现这些端口,组合根将同一
|
||
# 实例绑定到各字段,调用方按窄端口依赖)
|
||
wizard: WizardPort
|
||
doctor: DoctorPort
|
||
whitelist_management: WhitelistManagementPort
|
||
directory: DirectoryPort
|
||
pairing_management: PairingManagementPort
|
||
config_management: ConfigManagementPort
|
||
session_management: SessionManagementPort
|
||
user_binding: UserBindingPort
|
||
audit_query: AuditQueryPort
|
||
account_management: AccountManagementPort
|
||
login: ChannelLoginPort
|
||
message_management: MessageManagementPort
|
||
plugin_management: PluginManagementPort
|
||
outbox_management: OutboxManagementPort
|
||
capability_query: CapabilityQueryPort
|
||
content_review: ContentReviewPort
|
||
content_review_query: ContentReviewQueryPort
|
||
dashboard_view: DashboardViewPort
|
||
analytics_view: AnalyticsViewPort
|
||
route_binding_management: RouteBindingManagementPort
|
||
|
||
|
||
def _fill_adapter_registry[T](
|
||
registry: dict[ChannelType, T],
|
||
adapters: Iterable[T],
|
||
channel_type: ChannelType,
|
||
*,
|
||
duplicate_rule: str | None = None,
|
||
) -> None:
|
||
"""将插件适配器收集到注册表,最后注册者覆盖。
|
||
|
||
每类适配器每个 ChannelType 仅允许一个实例。``duplicate_rule`` 设置时,
|
||
重复注册抛 ``RuleViolationError``(INV-7 错误显式化,不静默兜底)。
|
||
|
||
Args:
|
||
registry: 目标注册表(按 ChannelType 索引)。
|
||
adapters: 插件注册的适配器列表。
|
||
channel_type: 当前渠道类型。
|
||
duplicate_rule: 若设置,重复注册时抛错,rule 为
|
||
``{duplicate_rule}:{channel_type}``。
|
||
"""
|
||
for adapter in adapters:
|
||
if duplicate_rule is not None and channel_type in registry:
|
||
raise RuleViolationError(
|
||
rule=f"{duplicate_rule}:{channel_type}",
|
||
)
|
||
registry[channel_type] = adapter
|
||
|
||
|
||
def create_channel_use_cases(
|
||
db: AsyncSession,
|
||
di_container: DependencyInjectionContainer,
|
||
) -> ChannelUseCases:
|
||
"""请求级 factory,创建用例服务聚合。
|
||
|
||
装配顺序(对齐 external_systems 的 ``create_use_cases_from_db`` 模式):
|
||
1. ``create_driven_adapters(db)`` 创建被驱动适配器聚合
|
||
2. 从 DI 容器获取应用级单例(注册中心等)
|
||
3. 创建领域服务(按依赖图拓扑顺序)
|
||
4. 通过 ``Pipeline.create()`` 创建管道
|
||
5. 实例化用例服务并注入依赖
|
||
|
||
Args:
|
||
db: SQLAlchemy 异步会话(请求级),被驱动适配器共享此会话。
|
||
di_container: 依赖注入容器,提供应用级单例。
|
||
|
||
Returns:
|
||
装配完成的 ``ChannelUseCases`` 实例(6 个独立用例服务实例 +
|
||
18 个类型化领域端口绑定,详见 ``ChannelUseCases`` 字段文档)。
|
||
"""
|
||
# 组合根从配置源构造 OutboxConfig 注入应用层,消除应用层对 yuxi.config.app
|
||
# 的直接依赖(§6.1 应用服务层禁止依赖具体技术适配器)。
|
||
# 同时构造 OutboxConfigHolder 支持 updateRetryPolicy 热更新(S2 修复):
|
||
# holder 在本次请求范围内共享给 OutboxHandler,使 updateRetryPolicy 的
|
||
# 内存更新对 retry_policy 查询立即生效;ConfigPort 持久化保证跨请求最终
|
||
# 一致性。
|
||
outbox_config = OutboxConfig(
|
||
ttl_seconds=app_config.channel_outbox_ttl_seconds,
|
||
retry_backoff_schedule=tuple(app_config.channel_outbox_retry_backoff_schedule),
|
||
max_retry=app_config.channel_outbox_max_retry,
|
||
)
|
||
outbox_config_holder = OutboxConfigHolder(outbox_config)
|
||
|
||
# 1. 被驱动适配器(请求级,共享 db)
|
||
# Redis 客户端、ARQ 连接池、AgentRunExecutionPort 从 DI 容器解析
|
||
# (由 create_host_bootstrap 注册为单例),注入到对应适配器构造函数
|
||
# (构造注入,INV-5 / ADP-001 / ADP-005 / ADP-022)。
|
||
# EventPublisherPort 同样从 DI 容器解析,注入到 ConfigManager 用于
|
||
# 配置变更后发布领域事件(CON-030 / ADP-029,事件由应用层发布)。
|
||
# F-02:ConfigScopeRegistry 从 DI 容器解析,提取 key_to_scope_map
|
||
# 注入到请求级 RedisConfigAdapter 供 _build_key 校验作用域一致性。
|
||
redis_client: Redis = di_container.resolve(Redis)
|
||
arq_pool: ArqRedis = di_container.resolve(ArqRedis)
|
||
execution_port: AgentRunExecutionPort = di_container.resolve(AgentRunExecutionPort)
|
||
event_publisher: EventPublisherPort = di_container.resolve(EventPublisherPort)
|
||
queue_port: QueuePort = di_container.resolve(QueuePort)
|
||
config_scope_registry: ConfigScopeRegistry = di_container.resolve(ConfigScopeRegistry)
|
||
adapters: DrivenAdapters = create_driven_adapters(
|
||
db,
|
||
outbox_config=outbox_config,
|
||
redis_client=redis_client,
|
||
arq_pool=arq_pool,
|
||
execution_port=execution_port,
|
||
key_to_scope_map=config_scope_registry.key_to_scope_map,
|
||
)
|
||
|
||
# 2. 应用级单例(从 DI 容器获取)
|
||
plugin_registry: PluginRegistry = di_container.resolve(PluginRegistry)
|
||
capability_registry: CapabilityRegistry = di_container.resolve(CapabilityRegistry)
|
||
command_registry_table: CommandRegistryTable = di_container.resolve(CommandRegistryTable)
|
||
identity_resolver_registry: IdentityResolverRegistry = di_container.resolve(IdentityResolverRegistry)
|
||
route_match_registry: RouteMatchRegistry = di_container.resolve(RouteMatchRegistry)
|
||
whitelist_registry: WhitelistRegistry = di_container.resolve(WhitelistRegistry)
|
||
plugin_lifecycle_manager: PluginLifecycleManager = di_container.resolve(PluginLifecycleManager)
|
||
health_aggregator: HealthAggregator = di_container.resolve(HealthAggregator)
|
||
channel_circuit_breaker: ChannelCircuitBreaker = di_container.resolve(ChannelCircuitBreaker)
|
||
# 内容审核域应用级单例(由 create_host_bootstrap 注册到 DI 容器):
|
||
# - DefaultContentModerationAdapter:未注册插件渠道时的兜底审核适配器,
|
||
# 注入 DispatchStage 供 content_review/preview 选择适配器。
|
||
# - ContentReviewRepositoryPort:审核历史仓储端口绑定,注入 DispatchStage
|
||
# (写路径)与 ContentReviewQueryService(读路径)。
|
||
default_moderation_adapter: DefaultContentModerationAdapter = di_container.resolve(DefaultContentModerationAdapter)
|
||
review_repository: ContentReviewRepositoryPort = di_container.resolve(ContentReviewRepositoryPort)
|
||
# PluginLoader 应用级单例(P1 PLG-INSTALL/UNINSTALL 文件操作),由
|
||
# create_host_bootstrap 注册到 DI 容器,注入 DispatchStage。
|
||
plugin_loader: PluginLoader = di_container.resolve(PluginLoader)
|
||
# F-01:敏感字段注册表应用级单例,由 create_host_bootstrap 在
|
||
# _loadPlugins discover 后调用 register(manifests) 合并插件声明的
|
||
# 敏感字段。注入到 ControlPlanePipeline.create(config/export 过滤)
|
||
# 与独立 AuditContextBuilder(params_summary 脱敏)。
|
||
sensitive_registry: SensitiveFieldRegistry = di_container.resolve(SensitiveFieldRegistry)
|
||
|
||
# 3. 领域服务(按依赖图拓扑顺序构造)
|
||
# 3.1 纯函数服务(无端口依赖)
|
||
mention_evaluator = MentionEvaluator()
|
||
truncation_detector = TruncationDetector()
|
||
|
||
# 3.2 单端口服务
|
||
fence_guard = FenceGuard(
|
||
cache_port=adapters.cache,
|
||
config_port=adapters.config,
|
||
logger=adapters.logger,
|
||
)
|
||
trusted_message_guard = TrustedMessageGuard(
|
||
conversation_port=adapters.conversation,
|
||
logger=adapters.logger,
|
||
)
|
||
config_manager = ConfigManager(
|
||
config_port=adapters.config,
|
||
event_publisher=event_publisher,
|
||
logger=adapters.logger,
|
||
)
|
||
ack_decision_maker = AckDecisionMaker(
|
||
config_port=adapters.config,
|
||
cache_port=adapters.cache,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 3.3 端口 + 注册中心
|
||
capability_verifier = CapabilityVerifier(
|
||
cache_port=adapters.cache,
|
||
registry=capability_registry,
|
||
logger=adapters.logger,
|
||
)
|
||
degradation_manager = DegradationManager(
|
||
cache_port=adapters.cache,
|
||
plugin_registry=plugin_registry,
|
||
logger=adapters.logger,
|
||
)
|
||
# 通用命令与内置身份解析器由 bootstrap 一次性注册到应用级单例注册表
|
||
# (见 ``create_host_bootstrap``),此处仅构造领域服务封装。
|
||
command_registry = CommandRegistry(registry=command_registry_table)
|
||
route_resolver = RouteResolver(
|
||
registry=route_match_registry,
|
||
config_port=adapters.config,
|
||
cache_port=adapters.cache,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 3.4 嵌套依赖(关键:BotLoopBudgetGuard 必须先于 DmSecurityGuard 构造)
|
||
bot_loop_budget_guard = BotLoopBudgetGuard(
|
||
cache_port=adapters.cache,
|
||
config_port=adapters.config,
|
||
logger=adapters.logger,
|
||
)
|
||
dm_security_guard = DmSecurityGuard(
|
||
persistence_port=adapters.persistence,
|
||
whitelist_registry=whitelist_registry,
|
||
bot_loop_guard=bot_loop_budget_guard,
|
||
logger=adapters.logger,
|
||
)
|
||
identity_resolver = IdentityResolver(
|
||
registry=identity_resolver_registry,
|
||
config_port=adapters.config,
|
||
cache_port=adapters.cache,
|
||
logger=adapters.logger,
|
||
)
|
||
session_merger = SessionMerger(
|
||
conversation_port=adapters.conversation,
|
||
persistence_port=adapters.persistence,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 状态回写处理器(FR-09):协调 ConversationPort 与 PersistencePort
|
||
# 完成渠道侧状态事件回写,含"已发未确认"推进与持久化记录反查。
|
||
status_writeback_handler = StatusWritebackHandler(
|
||
conversation_port=adapters.conversation,
|
||
persistence_port=adapters.persistence,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 4. 管道(请求级,注入适配器与领域服务)
|
||
# 4.1 适配器注册表(从 PluginRegistry 读取插件注册的适配器填充)
|
||
# 约束:每类适配器每个 ChannelType 仅允许一个实例(最后注册者覆盖)。
|
||
# 插件注册时由 PluginRegistry 保证唯一性,此处取最后值填充注册表。
|
||
status_adapter_registry: dict[ChannelType, StatusAdapter] = {}
|
||
session_adapter_registry: dict[ChannelType, SessionAdapter] = {}
|
||
inbound_adapter_registry: dict[ChannelType, InboundAdapter] = {}
|
||
mention_adapter_registry: dict[ChannelType, MentionAdapter] = {}
|
||
streaming_adapter_registry: dict[ChannelType, StreamingAdapter] = {}
|
||
outbound_adapter_registry: dict[ChannelType, OutboundAdapter] = {}
|
||
rich_message_adapter_registry: dict[ChannelType, RichMessageAdapter] = {}
|
||
channel_context_provider_registry: dict[ChannelType, ChannelContextProviderPort] = {}
|
||
command_adapter_registry: dict[ChannelType, CommandAdapter] = {}
|
||
tools_adapter_registry: dict[ChannelType, ToolsAdapter] = {}
|
||
message_ops_adapter_registry: dict[ChannelType, MessageOpsAdapter] = {}
|
||
directory_adapter_registry: dict[ChannelType, DirectoryAdapter] = {}
|
||
wizard_adapter_registry: dict[ChannelType, WizardAdapter] = {}
|
||
doctor_adapter_registry: dict[ChannelType, DoctorAdapter] = {}
|
||
whitelist_adapter_registry: dict[ChannelType, WhitelistAdapter] = {}
|
||
lifecycle_adapter_registry: dict[ChannelType, LifecycleAdapter] = {}
|
||
login_adapter_registry: dict[ChannelType, LoginAdapter] = {}
|
||
content_moderation_adapter_registry: dict[ChannelType, ContentModerationAdapter] = {}
|
||
webhook_test_adapter_registry: dict[ChannelType, WebhookTestAdapter] = {}
|
||
attachment_upload_adapter_registry: dict[ChannelType, AttachmentUploadAdapter] = {}
|
||
|
||
for channel_type, plugin_da in plugin_registry.listPluginAdapters():
|
||
_fill_adapter_registry(status_adapter_registry, plugin_da.status_adapters, channel_type)
|
||
_fill_adapter_registry(session_adapter_registry, plugin_da.session_adapters, channel_type)
|
||
_fill_adapter_registry(inbound_adapter_registry, plugin_da.inbound_adapters, channel_type)
|
||
_fill_adapter_registry(mention_adapter_registry, plugin_da.mention_adapters, channel_type)
|
||
_fill_adapter_registry(streaming_adapter_registry, plugin_da.streaming_adapters, channel_type)
|
||
_fill_adapter_registry(outbound_adapter_registry, plugin_da.outbound_adapters, channel_type)
|
||
_fill_adapter_registry(rich_message_adapter_registry, plugin_da.rich_message_adapters, channel_type)
|
||
_fill_adapter_registry(channel_context_provider_registry, plugin_da.channel_context_providers, channel_type)
|
||
_fill_adapter_registry(command_adapter_registry, plugin_da.command_adapters, channel_type)
|
||
_fill_adapter_registry(tools_adapter_registry, plugin_da.tools_adapters, channel_type)
|
||
_fill_adapter_registry(message_ops_adapter_registry, plugin_da.message_ops_adapters, channel_type)
|
||
_fill_adapter_registry(directory_adapter_registry, plugin_da.directory_adapters, channel_type)
|
||
_fill_adapter_registry(wizard_adapter_registry, plugin_da.wizard_adapters, channel_type)
|
||
_fill_adapter_registry(doctor_adapter_registry, plugin_da.doctor_adapters, channel_type)
|
||
_fill_adapter_registry(whitelist_adapter_registry, plugin_da.whitelist_adapters, channel_type)
|
||
_fill_adapter_registry(lifecycle_adapter_registry, plugin_da.lifecycle_adapters, channel_type)
|
||
_fill_adapter_registry(login_adapter_registry, plugin_da.login_adapters, channel_type)
|
||
_fill_adapter_registry(
|
||
content_moderation_adapter_registry,
|
||
plugin_da.content_moderation_adapters,
|
||
channel_type,
|
||
duplicate_rule="content_moderation_adapter_duplicate",
|
||
)
|
||
_fill_adapter_registry(webhook_test_adapter_registry, plugin_da.webhook_test_adapters, channel_type)
|
||
_fill_adapter_registry(attachment_upload_adapter_registry, plugin_da.attachment_upload_adapters, channel_type)
|
||
|
||
# 4.2 通用命令适配器(内置,跨渠道可用,FR-15)
|
||
# whitelist_command_port 延迟回填:ChannelControlService 同时实现
|
||
# WhitelistManagementPort 与 WhitelistCommandPort,在 ControlPlanePipeline
|
||
# 之后构造。此处先注入 logger,待 ChannelControlService 构造后通过
|
||
# setWhitelistCommandPort 回填(与 ChannelToolExecutor.setMessageOperationExecutor
|
||
# 同模式)。
|
||
common_command_adapter = CommonCommandAdapter(logger=adapters.logger)
|
||
|
||
# 实时指标采集器(单例,数据面管道采集 + 控制面 dashboard 查询共享)
|
||
realtime_metrics = InmemoryRealtimeMetricsAdapter()
|
||
|
||
# 4.3 入站管道
|
||
# 服务账号端口单例:由 create_driven_adapters 创建,共享注入到入站管道
|
||
# ServiceAccountResolveStage、控制面管道 AccountHandler 与 AgentRunAdapter。
|
||
service_account_port = adapters.service_account
|
||
# Agent 渠道访问配置端口单例:共享注入到入站管道 agent-run-enqueue 阶段
|
||
agent_access_config_port = adapters.agent_access_config
|
||
inbound_pipeline = InboundPipeline.create(
|
||
status_adapter_registry=status_adapter_registry,
|
||
session_adapter_registry=session_adapter_registry,
|
||
inbound_adapter_registry=inbound_adapter_registry,
|
||
mention_adapter_registry=mention_adapter_registry,
|
||
dm_security_guard=dm_security_guard,
|
||
bot_loop_budget_guard=bot_loop_budget_guard,
|
||
mention_evaluator=mention_evaluator,
|
||
command_registry=command_registry,
|
||
identity_resolver=identity_resolver,
|
||
route_resolver=route_resolver,
|
||
ack_decision_maker=ack_decision_maker,
|
||
fence_guard=fence_guard,
|
||
plugin_registry=plugin_registry,
|
||
config_port=adapters.config,
|
||
persistence_port=adapters.persistence,
|
||
conversation_port=adapters.conversation,
|
||
cache_port=adapters.cache,
|
||
agent_run_port=adapters.agent_run,
|
||
service_account_port=service_account_port,
|
||
agent_access_config_port=agent_access_config_port,
|
||
command_adapter_registry=command_adapter_registry,
|
||
common_command_adapter=common_command_adapter,
|
||
status_writeback_handler=status_writeback_handler,
|
||
channel_context_provider_registry=channel_context_provider_registry,
|
||
tools_adapter_registry=tools_adapter_registry,
|
||
message_ops_adapter_registry=message_ops_adapter_registry,
|
||
realtime_metrics=realtime_metrics,
|
||
event_publisher=event_publisher,
|
||
masking_port=adapters.masking,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 4.4 构造渠道工具执行器,作为 ``ChannelUseCases.channel_tool_executor``
|
||
# 字段返回,供 Agent 后端(``resolve_configured_runtime_tools``)通过
|
||
# 依赖注入显式获取并调用插件 executeTool(FR-11)。执行器持有
|
||
# tools_adapter_registry,按渠道类型路由到对应插件方法。同时注入
|
||
# MessageOperationExecutor 用于路由 ``渠道操作_`` 前缀工具调用(FR-12)。
|
||
# 由于 MessageOperationExecutor 依赖 message_ops_adapter_registry 与
|
||
# PreToolCallTrustedInjector(依赖 trusted_message_guard),需在
|
||
# 执行器构造后通过 setMessageOperationExecutor 回填引用,避免循环依赖。
|
||
# 不再通过模块级全局状态注册,符合 INV-3(契约显式化)与 §9.1
|
||
# (不得隐式全局注入)。
|
||
channel_tool_executor = ChannelToolExecutor(tools_adapter_registry)
|
||
pre_tool_call_trusted_injector = PreToolCallTrustedInjector(
|
||
trusted_message_guard=trusted_message_guard,
|
||
logger=adapters.logger,
|
||
)
|
||
message_operation_executor = MessageOperationExecutor(
|
||
message_ops_adapter_registry=message_ops_adapter_registry,
|
||
pre_tool_call_trusted_injector=pre_tool_call_trusted_injector,
|
||
conversation_port=adapters.conversation,
|
||
logger=adapters.logger,
|
||
)
|
||
channel_tool_executor.setMessageOperationExecutor(message_operation_executor)
|
||
|
||
# 4.5 出站管道
|
||
outbound_pipeline = OutboundPipeline.create(
|
||
agent_run_port=adapters.agent_run,
|
||
fence_guard=fence_guard,
|
||
conversation_port=adapters.conversation,
|
||
capability_verifier=capability_verifier,
|
||
plugin_registry=plugin_registry,
|
||
trusted_message_guard=trusted_message_guard,
|
||
streaming_adapter_registry=streaming_adapter_registry,
|
||
rich_message_adapter_registry=rich_message_adapter_registry,
|
||
truncation_detector=truncation_detector,
|
||
persistence_port=adapters.persistence,
|
||
outbound_adapter_registry=outbound_adapter_registry,
|
||
bot_loop_budget_guard=bot_loop_budget_guard,
|
||
transaction_port=adapters.transaction,
|
||
config_port=adapters.config,
|
||
whitelist_registry=whitelist_registry,
|
||
event_publisher=event_publisher,
|
||
outbox_config=outbox_config,
|
||
realtime_metrics=realtime_metrics,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 4.6 AdminMessageService 需先于 ControlPlanePipeline 构造
|
||
# (ControlPlanePipeline 的 dispatch 阶段依赖 AdminMessagePort)
|
||
# 非控制面管道路径:限流(cache_port)与审计(audit_log_repo)由用例
|
||
# 服务自行实现,不依赖 ControlPlanePipeline 的 RateLimitStage / AuditStage。
|
||
admin_message_service = AdminMessageService(
|
||
outbound_pipeline=outbound_pipeline,
|
||
idempotency_repo=adapters.persistence,
|
||
session_repo=adapters.persistence,
|
||
conversation_port=adapters.conversation,
|
||
audit_log_repo=adapters.persistence,
|
||
cache_port=adapters.cache,
|
||
logger=adapters.logger,
|
||
config_port=adapters.config,
|
||
plugin_registry=plugin_registry,
|
||
)
|
||
|
||
# 4.7.1 扫码登录领域服务(QR-03~QR-05)
|
||
# 协调 LoginAdapter,编排二维码生成 / 状态轮询 / 登录确认回调。
|
||
# 登录态持久化到 ConfigPort 的 ACCOUNT 作用域(key=login_status),
|
||
# 由 CredentialService 通过 setLoginStatus 写入。
|
||
# 注入到 ControlPlanePipeline 的 login_service 阶段。
|
||
# 注意:需先于 CredentialService / WizardService 构造(依赖关系)。
|
||
qr_login_service = QrLoginService(
|
||
login_adapter_registry=login_adapter_registry,
|
||
logger=adapters.logger,
|
||
config_port=adapters.config,
|
||
config_manager=config_manager,
|
||
)
|
||
|
||
# 4.7.2 凭证编排服务(Task 11 DI 装配)
|
||
# 渠道账号凭证的唯一落库入口,依赖 QrLoginService(QR 凭证获取)+
|
||
# ConfigPort(加密存储)+ CachePort(缓存失效)+ PersistencePort
|
||
# (P1 聚合根持久化)+ LoggerPort。必须在 QrLoginService 之后构造
|
||
# (强依赖),经 ControlPlanePipeline.create 注入到 AccountHandler
|
||
# (P1 enable/disable 链路 OFFLINE→ONLINE 凭证存在性校验)。
|
||
# WizardService 的 P2 finalize 凭证落库为未来规划,当前未消费。
|
||
credential_service = CredentialService(
|
||
config_port=adapters.config,
|
||
config_manager=config_manager,
|
||
persistence_port=adapters.persistence,
|
||
qr_login_service=qr_login_service,
|
||
cache_port=adapters.cache,
|
||
logger_port=adapters.logger,
|
||
)
|
||
|
||
# 4.7.4 配置诊断领域服务(FR-17)
|
||
# 协调 DoctorAdapter 与 ConfigPort,编排诊断检查项查询、单项检查、
|
||
# 全量检查与自动修复。已应用迁移列表从 CHANNEL 作用域读取(AC-64),
|
||
# 单项检查异常隔离不中断整体诊断报告。
|
||
doctor_service = DoctorService(
|
||
doctor_adapter_registry=doctor_adapter_registry,
|
||
config_port=adapters.config,
|
||
logger=adapters.logger,
|
||
whitelist_registry=whitelist_registry,
|
||
)
|
||
|
||
# 4.7.5 账户生命周期领域服务(AL-01~AL-04)
|
||
# 协调 LifecycleAdapter,编排账户 ID 解析、配置规范化与生命周期回调
|
||
# (afterAccountConfigWritten / beforeAccountDelete / onAccountEnabled /
|
||
# onAccountDisabled)。注入到 ControlPlanePipeline 的 lifecycle_service
|
||
# 阶段。
|
||
account_lifecycle_service = AccountLifecycleService(
|
||
lifecycle_adapter_registry=lifecycle_adapter_registry,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 4.7.6 账户创建领域服务(Task 6 装配)
|
||
# 装配顺序:account_lifecycle_service → account_creation_service →
|
||
# wizard_service → ControlPlanePipeline.create,避免循环依赖。
|
||
# 复用 lifecycle_service 的账户 ID 解析 / 配置规范化 / 回调能力,
|
||
# 叠加 persistence_port 与 service_account_port 完成账户幂等创建,
|
||
# 供 AccountHandler 与 WizardService.finalize 复用。
|
||
account_creation_service = AccountCreationService(
|
||
lifecycle_service=account_lifecycle_service,
|
||
persistence_port=adapters.persistence,
|
||
service_account_port=service_account_port,
|
||
event_publisher=event_publisher,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 4.7.3 安装向导领域服务(FR-16)
|
||
# 协调 WizardAdapter 与 ConfigPort,编排向导步骤查询、校验、应用、
|
||
# 配置聚合与 OAuth 回调。向导状态持久化到 ConfigPort 的 CHANNEL 作用域。
|
||
# finalize 在写入 channel_config 后调用 AccountCreationService.create_account
|
||
# 创建渠道账户(Task 6),并按 CredentialStrategy 通过 CredentialService
|
||
# 将凭证写入 ACCOUNT 作用域,供运行时客户端读取并更新登录态。
|
||
credential_strategy_registry = {
|
||
manifest.manifest.channel_type: manifest.manifest.credential_strategy
|
||
for manifest in plugin_registry.listPlugins()
|
||
if manifest.manifest.credential_strategy is not None
|
||
}
|
||
wizard_service = WizardService(
|
||
wizard_adapter_registry=wizard_adapter_registry,
|
||
login_adapter_registry=login_adapter_registry,
|
||
config_port=adapters.config,
|
||
logger=adapters.logger,
|
||
account_creation_service=account_creation_service,
|
||
credential_service=credential_service,
|
||
credential_strategy_registry=credential_strategy_registry,
|
||
)
|
||
|
||
# 4.8 控制面管道
|
||
# DispatchStage 不再依赖 AdminMessagePort / HealthAggregatorPort —— 前者
|
||
# 由 AdminMessageService 独立装配(见 4.6),后者由 HealthCheckService
|
||
# 独立持有(见 5. HealthCheckService 装配),控制面 dispatch 阶段不涉及
|
||
# 这两类职责。
|
||
# 消息操作被驱动适配器(MSG-05 撤回用例):复用 4.1 收集的
|
||
# message_ops_adapter_registry,按 channel_type 路由到对应插件适配器,
|
||
# 通过 ControlPlanePipeline.create 透传至 DispatchStage。
|
||
message_ops_port_adapter = MessageOpsPortAdapter(
|
||
message_ops_adapter_registry=message_ops_adapter_registry,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 4.7.5 内容审核域装配(CR-01~CR-03)
|
||
# content_moderation_adapter_registry 在 4.1 收集循环中已构建,
|
||
# 同 channel_type 注册多个实现时抛 RuleViolationError(INV-7 错误
|
||
# 显式化,不静默兜底)。未注册时为空 dict,使用兜底适配器。
|
||
|
||
# ContentReviewQueryService 装配(数据面,不经控制面管道):
|
||
# 直接读取 review_repository(ContentReviewRepositoryPort),覆盖 CR-02
|
||
# 历史列表与 CR-03 历史详情两个查询端点。查询无副作用,不写审计日志。
|
||
content_review_query_service = ContentReviewQueryService(
|
||
review_repository=review_repository,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
# 渠道主动探测器(FR-35):供 account/runtime_status 聚合探测状态使用。
|
||
# 依赖均为请求级端口,随请求构造以保证端口引用与当前 db session 一致。
|
||
channel_probe = ChannelProbe(
|
||
plugin_registry=plugin_registry,
|
||
persistence_port=adapters.persistence,
|
||
cache_port=adapters.cache,
|
||
queue_port=queue_port,
|
||
logger=adapters.logger,
|
||
)
|
||
|
||
control_plane_pipeline = ControlPlanePipeline.create(
|
||
persistence_port=adapters.persistence,
|
||
config_manager=config_manager,
|
||
config_port=adapters.config,
|
||
session_merger=session_merger,
|
||
degradation_manager=degradation_manager,
|
||
whitelist_registry=whitelist_registry,
|
||
cache_port=adapters.cache,
|
||
event_publisher=event_publisher,
|
||
plugin_registry=plugin_registry,
|
||
wizard_service=wizard_service,
|
||
directory_adapter_registry=directory_adapter_registry,
|
||
doctor_service=doctor_service,
|
||
whitelist_adapter_registry=whitelist_adapter_registry,
|
||
logger=adapters.logger,
|
||
lifecycle_service=account_lifecycle_service,
|
||
login_service=qr_login_service,
|
||
conversation_port=adapters.conversation,
|
||
lifecycle_manager=plugin_lifecycle_manager,
|
||
circuit_breaker=channel_circuit_breaker,
|
||
message_ops_port=message_ops_port_adapter,
|
||
outbox_repository=adapters.persistence,
|
||
outbox_config=outbox_config,
|
||
outbox_config_holder=outbox_config_holder,
|
||
queue_port=queue_port,
|
||
content_moderation_adapters=content_moderation_adapter_registry,
|
||
default_moderation_adapter=default_moderation_adapter,
|
||
review_repository=review_repository,
|
||
masking_port=adapters.masking,
|
||
transaction_port=adapters.transaction,
|
||
plugin_loader=plugin_loader,
|
||
webhook_test_adapters=webhook_test_adapter_registry,
|
||
attachment_upload_adapters=attachment_upload_adapter_registry,
|
||
channel_probe=channel_probe,
|
||
realtime_metrics=realtime_metrics,
|
||
service_account_port=service_account_port,
|
||
# Task 11: 凭证编排服务,供 AccountHandler P1 enable/disable 链路
|
||
# 凭证存在性校验(OFFLINE→ONLINE 时 getCredentials)。
|
||
credential_service=credential_service,
|
||
# Task 6: 账户创建服务,供 AccountHandler 与 WizardService.finalize
|
||
# 复用,完成渠道账户幂等创建(resolveAccountId → saveChannelAccount)。
|
||
account_creation_service=account_creation_service,
|
||
# F-01:敏感字段注册表,注入到 ConfigHandler(config/export 过滤)
|
||
# 与管道内 AuditContextBuilder(params_summary 脱敏)。
|
||
sensitive_registry=sensitive_registry,
|
||
# Task 13: 路由绑定管理依赖,注入到 RouteBindingHandler
|
||
# (route_binding/* 系列操作)。route_binding_repository 复用
|
||
# persistence 适配器(实现 RouteBindingRepositoryPort)。
|
||
route_binding_repository=adapters.persistence,
|
||
route_resolver=route_resolver,
|
||
route_match_registry=route_match_registry,
|
||
)
|
||
|
||
# 5. 用例服务(请求级,注入管道与适配器)
|
||
# PluginLifecycleManager / HealthAggregator 为应用级单例,从 DI 容器
|
||
# resolve 后注入用例服务,类型安全,不使用 Any。
|
||
# AuditContextBuilder 集中审计日志构造逻辑(D5),供 ChannelControlService
|
||
# 在管道失败时通过独立事务记录失败审计日志(AC-32)。audit_log_repo 取
|
||
# 自 persistence 聚合适名(继承 AuditLogRepositoryPort)。
|
||
audit_context_builder = AuditContextBuilder(
|
||
audit_log_repo=adapters.persistence,
|
||
logger=adapters.logger,
|
||
masking_port=adapters.masking,
|
||
# F-01:敏感字段注册表,管道失败审计日志的 params_summary 脱敏。
|
||
sensitive_registry=sensitive_registry,
|
||
)
|
||
channel_control = ChannelControlService(
|
||
control_plane_pipeline=control_plane_pipeline,
|
||
transaction_port=adapters.transaction,
|
||
audit_context_builder=audit_context_builder,
|
||
logger=adapters.logger,
|
||
user_identity_repository=adapters.persistence,
|
||
session_repository=adapters.persistence,
|
||
config_port=adapters.config,
|
||
service_account_port=adapters.service_account,
|
||
session_merger=session_merger,
|
||
)
|
||
|
||
# 5.1 注入 ChannelControlService 作为 WhitelistCommandPort 实现(FR-15 / FR-18)
|
||
# ChannelControlService 同时实现 WhitelistManagementPort 与
|
||
# WhitelistCommandPort:后者将 /allowlist 命令按 action 分派到
|
||
# listWhitelist / addWhitelistEntry / removeWhitelistEntry 类型化方法。
|
||
# CommonCommandAdapter 先于 ChannelControlService 构造,通过 setter
|
||
# 注入解决装配顺序(与 ChannelToolExecutor.setMessageOperationExecutor
|
||
# 同模式)。
|
||
common_command_adapter.setWhitelistCommandPort(channel_control)
|
||
|
||
return ChannelUseCases(
|
||
inbound_message=InboundMessageService(
|
||
inbound_pipeline=inbound_pipeline,
|
||
outbound_pipeline=outbound_pipeline,
|
||
config_port=adapters.config,
|
||
logger=adapters.logger,
|
||
tracer=adapters.tracer,
|
||
transaction_port=adapters.transaction,
|
||
account_repository=adapters.persistence,
|
||
ack_decision_maker=ack_decision_maker,
|
||
inbound_adapter_registry=inbound_adapter_registry,
|
||
),
|
||
admin_message=admin_message_service,
|
||
plugin_lifecycle=PluginLifecycleService(
|
||
lifecycle_manager=plugin_lifecycle_manager,
|
||
logger=adapters.logger,
|
||
),
|
||
health_check=HealthCheckService(
|
||
health_aggregator=health_aggregator,
|
||
cache_port=adapters.cache,
|
||
logger=adapters.logger,
|
||
tracer_port=adapters.tracer,
|
||
account_repository=adapters.persistence,
|
||
audit_log_repo=adapters.persistence,
|
||
),
|
||
channel_tool_executor=channel_tool_executor,
|
||
# ChannelControlService 实现 HealthCheckPort 的控制面子集
|
||
# (getSingleHealth / probeChannelDeep),绑定同一实例。
|
||
health_check_control=channel_control,
|
||
# ChannelControlService 同时实现以下 18 个类型化领域端口,组合根将
|
||
# 同一实例绑定到各字段(窄端口依赖)。
|
||
wizard=channel_control,
|
||
doctor=channel_control,
|
||
whitelist_management=channel_control,
|
||
directory=channel_control,
|
||
pairing_management=channel_control,
|
||
config_management=channel_control,
|
||
session_management=channel_control,
|
||
user_binding=channel_control,
|
||
audit_query=channel_control,
|
||
account_management=channel_control,
|
||
login=channel_control,
|
||
message_management=channel_control,
|
||
plugin_management=channel_control,
|
||
outbox_management=channel_control,
|
||
capability_query=channel_control,
|
||
# ChannelControlService 同时实现 ContentReviewPort(第 16 个类型化
|
||
# 端口),组合根将同一实例绑定到 content_review 字段。
|
||
content_review=channel_control,
|
||
# ContentReviewQueryService 为独立用例服务(数据面,不经控制面管道),
|
||
# 直接读取 review_repository 实现 CR-02 / CR-03 查询。
|
||
content_review_query=content_review_query_service,
|
||
dashboard_view=channel_control,
|
||
analytics_view=channel_control,
|
||
route_binding_management=channel_control,
|
||
)
|
||
|
||
|
||
def _create_worker_logger_port() -> Any:
|
||
"""构造 worker 进程级 LoggerPort 实例。
|
||
|
||
复用无状态 ``MaskingAdapter`` + ``StructuredLoggerAdapter`` 装配模式,
|
||
对齐 ``adapters/__init__.py`` 中 ``_logger_adapter`` 的构造方式
|
||
(注入 ``masking_port`` 以满足 ``LoggerPort`` 协议的脱敏要求)。
|
||
"""
|
||
from yuxi.channels.adapters.masking_adapter import MaskingAdapter
|
||
from yuxi.channels.adapters.structured_logger_adapter import (
|
||
StructuredLoggerAdapter,
|
||
)
|
||
|
||
return StructuredLoggerAdapter(masking_port=MaskingAdapter())
|
||
|
||
|
||
def _create_outbox_config() -> OutboxConfig:
|
||
"""从全局配置源构造 OutboxConfig(供 ChannelPersistenceAdapter 构造使用)。
|
||
|
||
对齐 ``create_channel_use_cases`` 中 OutboxConfig 的构造方式,消除应用层
|
||
对 ``yuxi.config.app`` 的直接依赖(§6.1 应用服务层禁止依赖具体技术适配器)。
|
||
"""
|
||
return OutboxConfig(
|
||
ttl_seconds=app_config.channel_outbox_ttl_seconds,
|
||
retry_backoff_schedule=tuple(app_config.channel_outbox_retry_backoff_schedule),
|
||
max_retry=app_config.channel_outbox_max_retry,
|
||
)
|
||
|
||
|
||
def create_channel_session_inactive_cleanup_handler_dependencies(
|
||
session_factory: Any,
|
||
) -> dict[str, Any]:
|
||
"""构造 ChannelSessionInactiveCleanupHandler 依赖字典(worker 进程装配用)。
|
||
|
||
由 ``infrastructure/scheduler.py`` 的 ``register_scheduler_handlers``
|
||
调用,传入 worker 进程级 ``session_factory``,返回
|
||
``ChannelSessionInactiveCleanupHandler.__init__`` 所需的依赖字典。
|
||
|
||
依赖方向合规(INV-1):``session_repo_factory`` 在 infrastructure 层
|
||
装配具体适配器 ``ChannelPersistenceAdapter``(fat adapter,实现
|
||
``ChannelSessionRepositoryPort``),应用层 handler 仅依赖端口抽象,
|
||
不直接 import 适配器实现。
|
||
|
||
Args:
|
||
session_factory: 调用返回 ``AsyncSession`` 的异步上下文管理器工厂,
|
||
通常为 ``pg_manager.get_async_session_context``。
|
||
|
||
Returns:
|
||
依赖字典,key 与 ``ChannelSessionInactiveCleanupHandler.__init__``
|
||
参数名一致:``{"session_factory": ..., "session_repo_factory": ...,
|
||
"logger": ...}``
|
||
"""
|
||
from yuxi.channels.adapters.channel_persistence_adapter import (
|
||
ChannelPersistenceAdapter,
|
||
)
|
||
|
||
logger_port = _create_worker_logger_port()
|
||
outbox_config = _create_outbox_config()
|
||
|
||
def session_repo_factory(db: AsyncSession) -> ChannelSessionRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
return {
|
||
"session_factory": session_factory,
|
||
"session_repo_factory": session_repo_factory,
|
||
"logger": logger_port,
|
||
}
|
||
|
||
|
||
def create_channel_outbox_terminal_cleanup_handler_dependencies(
|
||
session_factory: Any,
|
||
) -> dict[str, Any]:
|
||
"""构造 ChannelOutboxTerminalCleanupHandler 依赖字典(worker 进程装配用)。
|
||
|
||
``event_publisher`` 注入 ``None``(worker 进程未注入事件发布端口,
|
||
handler 内 best-effort 发布 ``OutboxEntryPurgedEvent``,发布失败仅记录
|
||
WARN 不影响清理主流程)。
|
||
|
||
Args:
|
||
session_factory: 调用返回 ``AsyncSession`` 的异步上下文管理器工厂。
|
||
|
||
Returns:
|
||
依赖字典,key 与 ``ChannelOutboxTerminalCleanupHandler.__init__``
|
||
参数名一致。
|
||
"""
|
||
from yuxi.channels.adapters.channel_persistence_adapter import (
|
||
ChannelPersistenceAdapter,
|
||
)
|
||
|
||
logger_port = _create_worker_logger_port()
|
||
outbox_config = _create_outbox_config()
|
||
|
||
def outbox_repo_factory(db: AsyncSession) -> OutboxRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
return {
|
||
"session_factory": session_factory,
|
||
"outbox_repo_factory": outbox_repo_factory,
|
||
"event_publisher": None, # worker 进程未注入事件发布端口,best-effort
|
||
"logger": logger_port,
|
||
}
|
||
|
||
|
||
def create_channel_pairing_terminal_cleanup_handler_dependencies(
|
||
session_factory: Any,
|
||
) -> dict[str, Any]:
|
||
"""构造 ChannelPairingTerminalCleanupHandler 依赖字典(worker 进程装配用)。
|
||
|
||
Args:
|
||
session_factory: 调用返回 ``AsyncSession`` 的异步上下文管理器工厂。
|
||
|
||
Returns:
|
||
依赖字典,key 与 ``ChannelPairingTerminalCleanupHandler.__init__``
|
||
参数名一致。
|
||
"""
|
||
from yuxi.channels.adapters.channel_persistence_adapter import (
|
||
ChannelPersistenceAdapter,
|
||
)
|
||
|
||
logger_port = _create_worker_logger_port()
|
||
outbox_config = _create_outbox_config()
|
||
|
||
def pairing_repo_factory(db: AsyncSession) -> PairingRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
return {
|
||
"session_factory": session_factory,
|
||
"pairing_repo_factory": pairing_repo_factory,
|
||
"logger": logger_port,
|
||
}
|
||
|
||
|
||
def create_channel_outbox_recovery_handler_dependencies(
|
||
session_factory: Any,
|
||
arq_pool: ArqRedis,
|
||
) -> dict[str, Any]:
|
||
"""构造 ChannelOutboxRecoveryHandler 依赖字典(worker 进程装配用)。
|
||
|
||
依赖方向合规(INV-1):``outbox_repo_factory`` / ``audit_log_repo_factory``
|
||
在 infrastructure 层装配具体适配器 ``ChannelPersistenceAdapter``(fat adapter,
|
||
分别实现 ``OutboxRepositoryPort`` / ``AuditLogRepositoryPort``),应用层 handler
|
||
仅依赖端口抽象,不直接 import 适配器实现。
|
||
|
||
Args:
|
||
session_factory: 调用返回 ``AsyncSession`` 的异步上下文管理器工厂。
|
||
arq_pool: worker 进程级 ARQ 连接池,由 ``run_worker.py`` 的
|
||
``_worker_startup`` 提前初始化后传入,用于构造 ``ARQQueueAdapter``。
|
||
|
||
Returns:
|
||
依赖字典,key 与 ``ChannelOutboxRecoveryHandler.__init__`` 参数名一致。
|
||
"""
|
||
from yuxi.channels.adapters.arq_queue_adapter import ARQQueueAdapter
|
||
from yuxi.channels.adapters.channel_persistence_adapter import (
|
||
ChannelPersistenceAdapter,
|
||
)
|
||
|
||
logger_port = _create_worker_logger_port()
|
||
outbox_config = _create_outbox_config()
|
||
|
||
def outbox_repo_factory(db: AsyncSession) -> OutboxRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
def audit_log_repo_factory(db: AsyncSession) -> AuditLogRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
queue_port = ARQQueueAdapter(arq_pool, logger_port)
|
||
|
||
return {
|
||
"session_factory": session_factory,
|
||
"outbox_repo_factory": outbox_repo_factory,
|
||
"audit_log_repo_factory": audit_log_repo_factory,
|
||
"queue_port": queue_port,
|
||
"outbox_config": outbox_config,
|
||
"logger": logger_port,
|
||
}
|
||
|
||
|
||
def create_channel_pairing_expiration_handler_dependencies(
|
||
session_factory: Any,
|
||
) -> dict[str, Any]:
|
||
"""构造 ChannelPairingExpirationHandler 依赖字典(worker 进程装配用)。
|
||
|
||
依赖方向合规(INV-1):``pairing_repo_factory`` 在 infrastructure 层装配具体
|
||
适配器 ``ChannelPersistenceAdapter``(fat adapter,实现
|
||
``PairingRepositoryPort``),应用层 handler 仅依赖端口抽象,不直接 import
|
||
适配器实现。
|
||
|
||
Args:
|
||
session_factory: 调用返回 ``AsyncSession`` 的异步上下文管理器工厂。
|
||
|
||
Returns:
|
||
依赖字典,key 与 ``ChannelPairingExpirationHandler.__init__`` 参数名一致。
|
||
"""
|
||
from yuxi.channels.adapters.channel_persistence_adapter import (
|
||
ChannelPersistenceAdapter,
|
||
)
|
||
|
||
logger_port = _create_worker_logger_port()
|
||
outbox_config = _create_outbox_config()
|
||
|
||
def pairing_repo_factory(db: AsyncSession) -> PairingRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
return {
|
||
"session_factory": session_factory,
|
||
"pairing_repo_factory": pairing_repo_factory,
|
||
"logger": logger_port,
|
||
}
|
||
|
||
|
||
def create_channel_audit_log_retention_handler_dependencies(
|
||
session_factory: Any,
|
||
) -> dict[str, Any]:
|
||
"""构造 ChannelAuditLogRetentionHandler 依赖字典(worker 进程装配用)。
|
||
|
||
依赖方向合规(INV-1):``audit_log_repo_factory`` 在 infrastructure 层装配具体
|
||
适配器 ``ChannelPersistenceAdapter``(fat adapter,实现
|
||
``AuditLogRepositoryPort``),应用层 handler 仅依赖端口抽象,不直接 import
|
||
适配器实现。
|
||
|
||
Args:
|
||
session_factory: 调用返回 ``AsyncSession`` 的异步上下文管理器工厂。
|
||
|
||
Returns:
|
||
依赖字典,key 与 ``ChannelAuditLogRetentionHandler.__init__`` 参数名一致。
|
||
"""
|
||
from yuxi.channels.adapters.channel_persistence_adapter import (
|
||
ChannelPersistenceAdapter,
|
||
)
|
||
|
||
logger_port = _create_worker_logger_port()
|
||
outbox_config = _create_outbox_config()
|
||
|
||
def audit_log_repo_factory(db: AsyncSession) -> AuditLogRepositoryPort:
|
||
return ChannelPersistenceAdapter(db, outbox_config=outbox_config, logger=logger_port)
|
||
|
||
return {
|
||
"session_factory": session_factory,
|
||
"audit_log_repo_factory": audit_log_repo_factory,
|
||
"logger": logger_port,
|
||
}
|