本次提交新增了完整的多渠道消息网关系统,包括: 1. 支持飞书、钉钉、Web、Hook 四种渠道的适配器与配置 2. 领域模型层:消息、会话、绑定、出箱等核心实体 3. 应用服务层:管道、中间件、DTO 与业务逻辑 4. 基础设施层:持久化、过滤器、队列等端口实现 5. 接口层:REST API、SSE、WebSocket 通信端点 6. 前端页面与路由配置,添加渠道管理菜单 7. 新增相关依赖包与 docker-compose 部署配置
67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import TYPE_CHECKING
|
|
|
|
from yuxi.channel.domain.service.message_context import MessageContext, Span
|
|
from yuxi.channel.domain.port.metrics_port import MetricsPort
|
|
|
|
if TYPE_CHECKING:
|
|
from yuxi.channel.domain.middleware.middleware import Middleware
|
|
|
|
|
|
class Pipeline:
|
|
def __init__(self, middlewares: list[Middleware], *, metrics: MetricsPort | None = None) -> None:
|
|
self._middlewares = middlewares
|
|
self._metrics = metrics
|
|
|
|
async def execute(self, ctx: MessageContext) -> MessageContext:
|
|
start = time.monotonic()
|
|
channel_type = ctx.channel_type or "unknown"
|
|
|
|
async def _chain(index: int, current_ctx: MessageContext) -> MessageContext:
|
|
if index >= len(self._middlewares):
|
|
return current_ctx
|
|
if current_ctx.is_aborted or current_ctx.is_skipped:
|
|
return current_ctx
|
|
|
|
middleware = self._middlewares[index]
|
|
span = Span(middleware_name=middleware.name)
|
|
span.started_at = time.monotonic()
|
|
|
|
try:
|
|
result_ctx = await middleware.process(current_ctx, lambda c: _chain(index + 1, c))
|
|
except Exception:
|
|
span.aborted = True
|
|
span.finished_at = time.monotonic()
|
|
span.duration_ms = (span.finished_at - span.started_at) * 1000
|
|
ctx.spans.append(span)
|
|
raise
|
|
|
|
span.finished_at = time.monotonic()
|
|
span.duration_ms = (span.finished_at - span.started_at) * 1000
|
|
span.aborted = result_ctx.is_aborted
|
|
span.skipped = result_ctx.is_skipped
|
|
ctx.spans.append(span)
|
|
|
|
if self._metrics:
|
|
await self._metrics.record_middleware_duration(middleware.name, channel_type, span.duration_ms / 1000)
|
|
|
|
return result_ctx
|
|
|
|
try:
|
|
result = await _chain(0, ctx)
|
|
finally:
|
|
if self._metrics:
|
|
elapsed = time.monotonic() - start
|
|
await self._metrics.record_pipeline_duration(channel_type, elapsed)
|
|
await self._metrics.record_pipeline_total(channel_type)
|
|
if ctx.is_aborted:
|
|
await self._metrics.record_pipeline_aborted(channel_type, ctx.abort_code or "unknown")
|
|
|
|
return result
|
|
|
|
@property
|
|
def middlewares(self) -> list[Middleware]:
|
|
return list(self._middlewares)
|