ForcePilot/backend/package/yuxi/channel/interfaces/websocket/manager.py
Kris 9e503becd3
Some checks failed
Deploy VitePress site to Pages / build (push) Has been cancelled
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
feat(plugin): 实现完整的插件注册管理系统
新增了插件相关的完整领域模型、应用服务、基础设施实现,包括:
1. 插件状态、注册模式、来源等基础枚举和数据结构
2. 插件清单解析、发现、加载工具类
3. 插件注册表领域服务和内存存储实现
4. 插件相关的命令、查询、事件定义
5. 插件REST API接口和DTO映射
6. 集成了原有通道适配器到插件系统
7. 新增内置插件注册和自动发现能力
2026-05-31 16:44:13 +08:00

81 lines
3.1 KiB
Python

from __future__ import annotations
import asyncio
import logging
from uuid import uuid4
from yuxi.channel.application.service.inbound_service import InboundService
from yuxi.channel.domain.model.plugin_registry.plugin_registry import PluginRegistry
from yuxi.channel.domain.port.channel_adapter_port import ChannelAdapterPort
from yuxi.channel.domain.port.metrics_port import MetricsPort
from yuxi.channel.domain.port.ws_connection_port import WsConnectionPort
logger = logging.getLogger(__name__)
class WsConnectionManager:
def __init__(self, metrics: MetricsPort | None = None, *, registry: PluginRegistry | None = None) -> None:
self._connections: dict[str, WsConnectionPort] = {}
self._adapters: dict[str, ChannelAdapterPort] = {}
self._registry = registry
self._loop: asyncio.AbstractEventLoop | None = None
self._inbound_service: InboundService | None = None
self._metrics = metrics
def register(self, connection: WsConnectionPort) -> None:
self._connections[connection.channel_type] = connection
def set_adapters(self, adapters: dict[str, ChannelAdapterPort]) -> None:
self._adapters = adapters
def set_registry(self, registry: PluginRegistry) -> None:
self._registry = registry
async def start_all(
self,
loop: asyncio.AbstractEventLoop,
inbound_service: InboundService,
) -> None:
self._loop = loop
self._inbound_service = inbound_service
if self._connections:
await asyncio.gather(
*(conn.start(loop, self._on_message) for conn in self._connections.values()),
return_exceptions=True,
)
async def stop_all(self) -> None:
for conn in self._connections.values():
try:
await conn.stop()
except Exception:
logger.warning("ws stop failed for %s", conn.channel_type)
async def _on_message(self, raw: dict) -> None:
channel_type = raw.get("channel_type", "")
adapter = self._registry.get_adapter(channel_type) if self._registry else self._adapters.get(channel_type)
if not adapter:
logger.warning("no adapter for ws message from %s", channel_type)
return
try:
message = await adapter.receive_message(raw)
except Exception:
logger.exception("ws receive_message failed for %s", channel_type)
return
trace_id = raw.get("header", {}).get("event_id", str(uuid4()))
message.metadata["trace_id"] = trace_id
message.metadata["source"] = "websocket"
message.metadata["idempotency_key"] = trace_id
if self._inbound_service:
try:
await self._inbound_service.submit(message, channel_type=channel_type, trace_id=trace_id)
except Exception:
logger.exception("ws inbound submit failed for %s", channel_type)
@property
def connections_status(self) -> dict[str, bool]:
return {ct: conn.is_connected for ct, conn in self._connections.items()}