新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
417 lines
14 KiB
Python
417 lines
14 KiB
Python
"""聚合视图域 analytics 子域契约 DTO。
|
||
|
||
定义聚合视图域 analytics 子域 6 个跨子域深度分析端点(ANL-01/02/04/05/06/07)
|
||
返回的不可变值对象。所有 DTO 均为 ``dataclass(frozen=True)``,仅依赖标准库,
|
||
用于按时间 / 类型 / 状态切片的细分聚合展示,供管理后台运营审计与运维排障使用。
|
||
|
||
与 dashboard 子域的区分:dashboard 提供总览计数,analytics 提供按维度切片的
|
||
细分聚合;与单子域 stats(如 OBX-02 outbox stats)的区分:单子域统计归各子域
|
||
router,本域仅承载跨子域深度分析视图。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
from typing import Any
|
||
|
||
from yuxi.channels.contract.dtos.channel import ChannelType
|
||
from yuxi.channels.contract.dtos.common import CategoryStat, TrendDataPoint
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class TimeSeriesPoint:
|
||
"""时间序列数据点。
|
||
|
||
描述按时间粒度切片后的单个时间桶计数,供消息与会话分析的趋势展示使用。
|
||
|
||
字段:
|
||
timestamp: 时间桶起始时间(ISO 8601 字符串)。
|
||
value: 计数值。
|
||
"""
|
||
|
||
timestamp: str
|
||
value: int
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
return {"timestamp": self.timestamp, "value": self.value}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MessageAnalytics:
|
||
"""消息深度分析结果(ANL-01)。
|
||
|
||
描述时间范围内消息量按时间 / 渠道 / 角色 / 投递状态切片的细分聚合,由
|
||
``getMessageAnalytics`` 返回,供运营审计按多维切片定位消息量趋势。
|
||
|
||
字段:
|
||
total: 时间范围内消息总数。
|
||
timeseries: 按时间粒度切片的消息量趋势。
|
||
by_channel: 按渠道类型分组的计数 dict(key 为 channel_type 字符串值)。
|
||
by_role: 按发送角色分组的计数 dict(key 为 user / assistant / system / tool)。
|
||
by_delivery_status: 按投递状态分组的计数 dict。
|
||
"""
|
||
|
||
total: int
|
||
timeseries: tuple[TimeSeriesPoint, ...]
|
||
by_channel: dict[str, int]
|
||
by_role: dict[str, int]
|
||
by_delivery_status: dict[str, int]
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
"""序列化为 dict,递归处理嵌套 ``TimeSeriesPoint`` 为 dict 列表。"""
|
||
return {
|
||
"total": self.total,
|
||
"timeseries": [point.to_dict() for point in self.timeseries],
|
||
"by_channel": self.by_channel,
|
||
"by_role": self.by_role,
|
||
"by_delivery_status": self.by_delivery_status,
|
||
}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MessageDistribution:
|
||
"""消息类型分布结果(ANL-02)。
|
||
|
||
描述时间范围内消息按类型分组的计数与占比,由 ``getMessageDistribution`` 返回,
|
||
供内容运营按类型(text / card / attachment / mixed)定位消息构成。
|
||
|
||
字段:
|
||
total: 时间范围内消息总数。
|
||
by_type: 按消息类型分组的计数 dict。
|
||
by_type_percent: 按消息类型分组的占比 dict(0-100)。
|
||
"""
|
||
|
||
total: int
|
||
by_type: dict[str, int]
|
||
by_type_percent: dict[str, float]
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
"""序列化为 dict,供控制面结果返回与日志输出使用。"""
|
||
return {
|
||
"total": self.total,
|
||
"by_type": self.by_type,
|
||
"by_type_percent": self.by_type_percent,
|
||
}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SessionAnalytics:
|
||
"""会话分析结果(ANL-04)。
|
||
|
||
描述时间范围内会话量按时间 / 渠道 / 临时性 / 消息数区间切片的细分聚合,由
|
||
``getSessionAnalytics`` 返回,供运营审计按多维切片定位会话量趋势。
|
||
|
||
字段:
|
||
total: 时间范围内活跃会话总数。
|
||
timeseries: 按时间粒度切片的会话量趋势。
|
||
by_channel: 按渠道类型分组的计数 dict(key 为 channel_type 字符串值)。
|
||
by_is_temporary: 按是否临时会话分组的计数 dict(key 为 "true" / "false")。
|
||
by_message_count_bucket: 按消息数区间分组的计数 dict
|
||
(key 为 "0-10" / "11-50" / "51-100" / "100+")。
|
||
"""
|
||
|
||
total: int
|
||
timeseries: tuple[TimeSeriesPoint, ...]
|
||
by_channel: dict[str, int]
|
||
by_is_temporary: dict[str, int]
|
||
by_message_count_bucket: dict[str, int]
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
"""序列化为 dict,递归处理嵌套 ``TimeSeriesPoint`` 为 dict 列表。"""
|
||
return {
|
||
"total": self.total,
|
||
"timeseries": [point.to_dict() for point in self.timeseries],
|
||
"by_channel": self.by_channel,
|
||
"by_is_temporary": self.by_is_temporary,
|
||
"by_message_count_bucket": self.by_message_count_bucket,
|
||
}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DeliveryAnalytics:
|
||
"""投递链路分析结果(ANL-05)。
|
||
|
||
与现有 ``OutboxStats``(按状态分组计数)的关系:``DeliveryAnalytics``
|
||
是 ``OutboxStats`` 的扩展,新增 success_rate / failure_rate /
|
||
retry_distribution / avg_latency_ms 四个聚合维度,由
|
||
``getDeliveryAnalytics`` 返回,供运维排障定位投递成功率与重试分布。
|
||
|
||
字段:
|
||
total: 时间范围内投递条目总数。
|
||
success_rate: 投递成功率(0-100)。
|
||
failure_rate: 投递失败率(0-100)。
|
||
retry_distribution: 按重试次数分组的计数 dict
|
||
(key 为 "0" / "1" / "2" / "3+" / "max_reached")。
|
||
avg_latency_ms: 平均投递延迟(毫秒),无 latency_ms 数据时为 None。
|
||
"""
|
||
|
||
total: int
|
||
success_rate: float
|
||
failure_rate: float
|
||
retry_distribution: dict[str, int]
|
||
avg_latency_ms: float | None
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
"""序列化为 dict,``avg_latency_ms`` 为 None 时返回 None。"""
|
||
return {
|
||
"total": self.total,
|
||
"success_rate": self.success_rate,
|
||
"failure_rate": self.failure_rate,
|
||
"retry_distribution": self.retry_distribution,
|
||
"avg_latency_ms": self.avg_latency_ms,
|
||
}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DeliveryLatencyDistribution:
|
||
"""投递延迟分布结果(ANL-06)。
|
||
|
||
描述投递延迟的分位数与直方图分布,由 ``getDeliveryLatencyDistribution``
|
||
返回,供运维排障定位长尾延迟。所有分位数字段无 latency_ms 数据时为 None。
|
||
|
||
字段:
|
||
p50_ms: P50 延迟(毫秒),无数据时为 None。
|
||
p90_ms: P90 延迟(毫秒),无数据时为 None。
|
||
p99_ms: P99 延迟(毫秒),无数据时为 None。
|
||
max_ms: 最大延迟(毫秒),无数据时为 None。
|
||
histogram: 按延迟区间分组的计数 dict
|
||
(key 为 "0-100ms" / "100-500ms" / "500ms-1s" / "1-5s" / "5s+")。
|
||
"""
|
||
|
||
p50_ms: float | None
|
||
p90_ms: float | None
|
||
p99_ms: float | None
|
||
max_ms: float | None
|
||
histogram: dict[str, int]
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
"""序列化为 dict,分位数字段为 None 时返回 None。"""
|
||
return {
|
||
"p50_ms": self.p50_ms,
|
||
"p90_ms": self.p90_ms,
|
||
"p99_ms": self.p99_ms,
|
||
"max_ms": self.max_ms,
|
||
"histogram": self.histogram,
|
||
}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DeliveryFunnel:
|
||
"""投递漏斗分析结果(ANL-07)。
|
||
|
||
描述投递管道各节点的条目数与相对 enter 的转化率,由 ``getDeliveryFunnel``
|
||
返回,供运维排障定位投递链路损耗节点。
|
||
|
||
字段:
|
||
enter: 进入投递管道的条目数。
|
||
sent: 投递成功数。
|
||
suppressed: 被抑制数。
|
||
failed: 失败数。
|
||
dead: 死信数。
|
||
conversion_rate: 各节点相对 enter 的转化率 dict
|
||
(key 为 "sent" / "suppressed" / "failed" / "dead",value 为 0-100)。
|
||
"""
|
||
|
||
enter: int
|
||
sent: int
|
||
suppressed: int
|
||
failed: int
|
||
dead: int
|
||
conversion_rate: dict[str, float]
|
||
|
||
def to_dict(self) -> dict[str, Any]:
|
||
"""序列化为 dict,供控制面结果返回与日志输出使用。"""
|
||
return {
|
||
"enter": self.enter,
|
||
"sent": self.sent,
|
||
"suppressed": self.suppressed,
|
||
"failed": self.failed,
|
||
"dead": self.dead,
|
||
"conversion_rate": self.conversion_rate,
|
||
}
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class AccountAnalyticsQuery:
|
||
"""账户活跃度分析查询(ANL-ACCOUNTS)。
|
||
|
||
描述账户活跃度分析查询条件,按时间范围(必填)与渠道类型过滤,
|
||
支持按粒度切片。
|
||
|
||
字段:
|
||
start_time: 起始时间(必填)。
|
||
end_time: 截止时间(必填)。
|
||
granularity: 时间粒度(默认 day,可选 hour/day/week)。
|
||
channel_type: 渠道类型过滤(可选)。
|
||
"""
|
||
|
||
start_time: datetime
|
||
end_time: datetime
|
||
granularity: str = "day"
|
||
channel_type: ChannelType | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class AccountActivityStat:
|
||
"""账户活跃统计(ANL-ACCOUNTS by_account 条目)。
|
||
|
||
描述单账户在时间范围内的活跃度指标,由 MessageRepositoryPort 与
|
||
ChannelSessionRepositoryPort 聚合计算。
|
||
|
||
字段:
|
||
account_id: 账户 ID。
|
||
channel_type: 渠道类型字符串值。
|
||
message_count: 消息总数。
|
||
session_count: 会话总数。
|
||
active_days: 活跃天数。
|
||
avg_daily_messages: 日均消息数。
|
||
"""
|
||
|
||
account_id: str
|
||
channel_type: str
|
||
message_count: int
|
||
session_count: int
|
||
active_days: int
|
||
avg_daily_messages: float
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class AccountAnalyticsResult:
|
||
"""账户活跃度分析结果(ANL-ACCOUNTS)。
|
||
|
||
描述账户活跃度分析聚合结果,包含按账户切片的统计与活跃账户数趋势。
|
||
``trend`` 复用通用 ``TrendDataPoint``,``value`` 承载该时间桶的活跃账户数,
|
||
由序列化层映射为 ``active_accounts`` 字段名。
|
||
|
||
字段:
|
||
by_account: 按账户切片的统计元组。
|
||
trend: 活跃账户数趋势元组(value=active_accounts)。
|
||
"""
|
||
|
||
by_account: tuple[AccountActivityStat, ...]
|
||
trend: tuple[TrendDataPoint, ...]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PeerAnalyticsQuery:
|
||
"""对端活跃度分析查询(ANL-PEERS)。
|
||
|
||
描述对端活跃度分析查询条件,按时间范围(必填)与渠道类型/账户过滤,
|
||
返回按消息数降序的 Top N 对端。
|
||
|
||
字段:
|
||
start_time: 起始时间(必填)。
|
||
end_time: 截止时间(必填)。
|
||
channel_type: 渠道类型过滤(可选)。
|
||
account_id: 账户 ID 过滤(可选)。
|
||
limit: Top N 上限(默认 100,1-500)。
|
||
"""
|
||
|
||
start_time: datetime
|
||
end_time: datetime
|
||
channel_type: ChannelType | None = None
|
||
account_id: str | None = None
|
||
limit: int = 100
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PeerActivityStat:
|
||
"""对端活跃统计(ANL-PEERS top_active 条目)。
|
||
|
||
描述单对端在时间范围内的活跃度指标,由 MessageRepositoryPort 聚合计算。
|
||
|
||
字段:
|
||
peer_id: 对端 ID。
|
||
channel_type: 渠道类型字符串值。
|
||
message_count: 消息总数。
|
||
session_count: 会话总数。
|
||
first_seen: 首次出现时间(可选,无数据时为 None)。
|
||
last_seen: 最后出现时间(可选,无数据时为 None)。
|
||
"""
|
||
|
||
peer_id: str
|
||
channel_type: str
|
||
message_count: int
|
||
session_count: int
|
||
first_seen: datetime | None
|
||
last_seen: datetime | None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PeerAnalyticsResult:
|
||
"""对端活跃度分析结果(ANL-PEERS)。
|
||
|
||
描述对端活跃度分析聚合结果,按 ``message_count`` 降序返回 Top N 对端。
|
||
|
||
字段:
|
||
top_active: 按消息数降序的对端统计元组。
|
||
"""
|
||
|
||
top_active: tuple[PeerActivityStat, ...]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ContentReviewAnalyticsQuery:
|
||
"""内容审核分析查询(ANL-CR)。
|
||
|
||
描述内容审核分析查询条件,按时间范围(必填)与渠道类型过滤,
|
||
支持按粒度切片。
|
||
|
||
字段:
|
||
start_time: 起始时间(必填)。
|
||
end_time: 截止时间(必填)。
|
||
granularity: 时间粒度(默认 day,可选 hour/day/week)。
|
||
channel_type: 渠道类型过滤(可选)。
|
||
"""
|
||
|
||
start_time: datetime
|
||
end_time: datetime
|
||
granularity: str = "day"
|
||
channel_type: ChannelType | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ContentReviewAnalyticsResult:
|
||
"""内容审核分析结果(ANL-CR)。
|
||
|
||
描述内容审核分析聚合结果,包含总审核/拦截计数、拦截率、按分类切片与
|
||
审核趋势。``by_category`` 复用通用 ``CategoryStat``(``rate`` 由序列化层
|
||
按 ``count / total_reviewed`` 计算补充);``trend`` 为多值时间序列
|
||
(``reviewed`` / ``blocked`` 双计数),无法用单一 ``value`` 字段承载,
|
||
故使用 ``dict[str, Any]`` 元组,由适配器层按 ``{timestamp, reviewed,
|
||
blocked}`` 结构组装。
|
||
|
||
字段:
|
||
total_reviewed: 审核总数。
|
||
total_blocked: 拦截总数。
|
||
block_rate: 拦截率(0-100)。
|
||
by_category: 按分类切片的统计元组(rate 由序列化层补充)。
|
||
trend: 审核趋势元组(每项含 timestamp/reviewed/blocked)。
|
||
"""
|
||
|
||
total_reviewed: int
|
||
total_blocked: int
|
||
block_rate: float
|
||
by_category: tuple[CategoryStat, ...]
|
||
trend: tuple[dict[str, Any], ...]
|
||
|
||
|
||
__all__ = [
|
||
"AccountActivityStat",
|
||
"AccountAnalyticsQuery",
|
||
"AccountAnalyticsResult",
|
||
"ContentReviewAnalyticsQuery",
|
||
"ContentReviewAnalyticsResult",
|
||
"DeliveryAnalytics",
|
||
"DeliveryFunnel",
|
||
"DeliveryLatencyDistribution",
|
||
"MessageAnalytics",
|
||
"MessageDistribution",
|
||
"PeerActivityStat",
|
||
"PeerAnalyticsQuery",
|
||
"PeerAnalyticsResult",
|
||
"SessionAnalytics",
|
||
"TimeSeriesPoint",
|
||
]
|