ForcePilot/backend/package/yuxi/channels/contract/dtos/analytics.py
Kris b88c0ae29e feat(channels): 批量新增多渠道网关限界上下文基础代码与契约
新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
2026-07-02 03:22:12 +08:00

417 lines
14 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.

"""聚合视图域 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: 按渠道类型分组的计数 dictkey 为 channel_type 字符串值)。
by_role: 按发送角色分组的计数 dictkey 为 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: 按消息类型分组的占比 dict0-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: 按渠道类型分组的计数 dictkey 为 channel_type 字符串值)。
by_is_temporary: 按是否临时会话分组的计数 dictkey 为 "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 上限(默认 1001-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",
]