本次提交包含多维度代码优化与功能增强: 1. 移除报告模块冗余导入与枚举,清理报表相关代码 2. 新增扫码登录支持方法与飞书适配器适配 3. 完善异常日志与健康检查信息 4. 扩展目录、配对管理、能力查询等接口 5. 优化出站管道与事务提交后钩子逻辑 6. 修复飞书消息解析与响应空值问题 7. 重构配置更新与服务账号创建逻辑 8. 统一传输错误分类契约与错误基类扩展
331 lines
11 KiB
Python
331 lines
11 KiB
Python
"""Dashboard 聚合视图域 DTO。
|
||
|
||
定义跨子域 KPI 聚合视图的不可变值对象,供 dashboard_router 端点返回。
|
||
所有 DTO 均为 ``dataclass(frozen=True)``,仅依赖标准库与契约层内部类型。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
|
||
from yuxi.channels.contract.dtos.channel import ChannelType
|
||
from yuxi.channels.contract.dtos.outbox import OutboxStats
|
||
from yuxi.channels.contract.errors import ValidationError
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class AccountStats:
|
||
"""账户统计快照(DSB-02)。
|
||
|
||
字段:
|
||
total: 账户总数(仅含 is_deleted=0)。
|
||
by_channel: 按渠道类型分组的计数 dict(key 为 ChannelType.value)。
|
||
by_status: 按账户状态分组的计数 dict(key ∈ {active, disabled, degraded})。
|
||
"""
|
||
|
||
total: int
|
||
by_channel: dict[str, int]
|
||
by_status: dict[str, int]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MessageStats:
|
||
"""消息统计快照(DSB-03)。
|
||
|
||
字段:
|
||
total: 消息总数(满足时间过滤条件)。
|
||
by_channel: 按渠道类型分组的计数 dict(channel_type 通过 conversation 关联填充)。
|
||
by_role: 按发送角色分组的计数 dict(key ∈ {user, assistant, system, tool})。
|
||
by_delivery_status: 按渠道侧投递状态分组的计数 dict
|
||
(key ∈ {sent, delivered, read, recalled, edited})。
|
||
"""
|
||
|
||
total: int
|
||
by_channel: dict[str, int]
|
||
by_role: dict[str, int]
|
||
by_delivery_status: dict[str, int]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SessionStats:
|
||
"""会话统计快照(DSB-04)。
|
||
|
||
字段:
|
||
total: 未软删除会话总数。
|
||
by_channel: 按渠道类型分组的计数 dict。
|
||
by_is_temporary: 按是否临时会话分组的计数 dict(key 为 "true" / "false")。
|
||
"""
|
||
|
||
total: int
|
||
by_channel: dict[str, int]
|
||
by_is_temporary: dict[str, int]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DashboardOverview:
|
||
"""全局总览快照(DSB-01)。
|
||
|
||
聚合四类子域计数,供管理后台首页看板渲染。
|
||
|
||
字段:
|
||
accounts: 账户总览。
|
||
messages: 消息总览。
|
||
sessions: 会话总览。
|
||
outbox: 投递总览(复用 OutboxStats)。
|
||
"""
|
||
|
||
accounts: AccountStats
|
||
messages: MessageStats
|
||
sessions: SessionStats
|
||
outbox: OutboxStats
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DashboardOverviewQuery:
|
||
"""全局总览查询(DSB-01)。
|
||
|
||
描述全局总览查询条件,按渠道类型过滤;``start_time`` / ``end_time``
|
||
仅作用于 ``messages`` 子域(消息数为时间窗口内事件),``accounts`` /
|
||
``sessions`` / ``outbox`` 为当前状态快照,不受时间范围影响。
|
||
|
||
字段:
|
||
channel_type: 渠道类型过滤(可选)。
|
||
start_time: 消息统计起始时间(可选,须为 aware datetime)。
|
||
end_time: 消息统计截止时间(可选,须为 aware datetime)。
|
||
"""
|
||
|
||
channel_type: ChannelType | None = None
|
||
start_time: datetime | None = None
|
||
end_time: datetime | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验时间区间合法性与时区约束(DSB-01)。
|
||
|
||
与 ``DashboardDeliveryQuery`` 保持一致,避免区间反转与 naive
|
||
datetime 导致的数据偏移(INV-DASHBOARD-OVERVIEW-RANGE)。
|
||
"""
|
||
if self.start_time is not None and self.start_time.tzinfo is None:
|
||
raise ValidationError(
|
||
"start_time",
|
||
"start_time must be timezone-aware datetime",
|
||
)
|
||
if self.end_time is not None and self.end_time.tzinfo is None:
|
||
raise ValidationError(
|
||
"end_time",
|
||
"end_time must be timezone-aware datetime",
|
||
)
|
||
if self.start_time is not None and self.end_time is not None and self.start_time > self.end_time:
|
||
raise ValidationError(
|
||
"time_range",
|
||
"start_time must not be later than end_time",
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DashboardDeliveryQuery:
|
||
"""投递总览查询(DSB-DELIVERY)。
|
||
|
||
描述投递总览查询条件,按渠道类型与时间范围过滤;所有字段可选,
|
||
缺省时返回全局聚合。
|
||
|
||
字段:
|
||
channel_type: 渠道类型过滤(可选)。
|
||
start_time: 起始时间(可选,须为 aware datetime)。
|
||
end_time: 截止时间(可选,须为 aware datetime)。
|
||
"""
|
||
|
||
channel_type: ChannelType | None = None
|
||
start_time: datetime | None = None
|
||
end_time: datetime | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验时间区间合法性与时区约束(DSB-DELIVERY)。
|
||
|
||
- ``start_time`` / ``end_time`` 同时提供时,``start_time`` 必须
|
||
不晚于 ``end_time``,否则区间反转会导致 adapter 层 SQL 静默
|
||
返回空集(INV-DASHBOARD-DELIVERY-RANGE)。
|
||
- 任一时间字段提供时必须为 aware datetime(``tzinfo`` 非空),
|
||
与各表时间列的 UTC 存储语义对齐,避免 naive datetime 被按
|
||
本地时区解释导致偏移(INV-DASHBOARD-DELIVERY-TZ)。
|
||
|
||
校验在构造时即抛出 ``ValidationError``,避免非法值传播到
|
||
adapter 层后才暴露。
|
||
"""
|
||
if self.start_time is not None and self.start_time.tzinfo is None:
|
||
raise ValidationError(
|
||
"start_time",
|
||
"start_time must be timezone-aware datetime",
|
||
)
|
||
if self.end_time is not None and self.end_time.tzinfo is None:
|
||
raise ValidationError(
|
||
"end_time",
|
||
"end_time must be timezone-aware datetime",
|
||
)
|
||
if self.start_time is not None and self.end_time is not None and self.start_time > self.end_time:
|
||
raise ValidationError(
|
||
"time_range",
|
||
"start_time must not be later than end_time",
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ChannelDeliveryStat:
|
||
"""渠道投递统计(DSB-DELIVERY by_channel 条目)。
|
||
|
||
描述单渠道投递计数与成功率,用于投递总览的按渠道切片展示。
|
||
|
||
字段:
|
||
channel_type: 渠道类型字符串值。
|
||
sent: 该渠道投递总数。
|
||
success_rate: 成功率(0-100)。
|
||
"""
|
||
|
||
channel_type: str
|
||
sent: int
|
||
success_rate: float | None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DashboardDeliveryResult:
|
||
"""投递总览结果(DSB-DELIVERY)。
|
||
|
||
描述投递总览聚合结果,包含总计数、成功率、延迟分位数、队列深度、
|
||
死信数与按渠道切片。
|
||
|
||
字段:
|
||
total_sent: 投递总数。
|
||
success_count: 成功数。
|
||
failed_count: 失败数。
|
||
success_rate: 成功率(0-100);无投递样本时为 ``None``,避免
|
||
前端将 ``0.0%`` 误解为低投递率。
|
||
avg_latency_ms: 平均延迟(毫秒);无投递样本时为 ``None``。
|
||
p95_latency_ms: P95 延迟(毫秒);无投递样本时为 ``None``。
|
||
p99_latency_ms: P99 延迟(毫秒);无投递样本时为 ``None``。
|
||
current_queue_depth: 当前队列深度。
|
||
dead_letter_count: 死信总数。
|
||
by_channel: 按渠道切片的统计元组。
|
||
"""
|
||
|
||
total_sent: int
|
||
success_count: int
|
||
failed_count: int
|
||
success_rate: float | None
|
||
avg_latency_ms: float | None
|
||
p95_latency_ms: float | None
|
||
p99_latency_ms: float | None
|
||
current_queue_depth: int
|
||
dead_letter_count: int
|
||
by_channel: tuple[ChannelDeliveryStat, ...]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class DashboardTodoResult:
|
||
"""工作台待办计数聚合结果(DSB-TODOS)。
|
||
|
||
将分散在配对审批、内容审核、死信积压三个子域的待办计数聚合为
|
||
一次请求返回,确保看板角标与跳转页数据口径一致。
|
||
|
||
字段:
|
||
pending_pairings: 待审批配对数量。
|
||
pending_reviews: 待人工复核的内容审核数量。
|
||
dead_letter_count: 当前死信积压数量(按渠道过滤时仅统计该渠道)。
|
||
"""
|
||
|
||
pending_pairings: int
|
||
pending_reviews: int
|
||
dead_letter_count: int
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RealtimeQuery:
|
||
"""实时监控查询(DSB-REALTIME)。
|
||
|
||
描述实时监控查询条件,按渠道类型与滑动窗口过滤;``window_seconds``
|
||
范围 10-300,默认 60。
|
||
|
||
字段:
|
||
channel_type: 渠道类型过滤(可选)。
|
||
window_seconds: 滑动窗口秒数(默认 60,范围 10-300)。
|
||
"""
|
||
|
||
channel_type: ChannelType | None = None
|
||
window_seconds: int = 60
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验 ``window_seconds`` 取值范围(DSB-REALTIME)。
|
||
|
||
``window_seconds`` 必须在 ``[10, 300]`` 区间内:过小会导致
|
||
滑动窗口采样不足,过大会使内存计数器 deque 膨胀。校验在构造时
|
||
即抛出 ``ValidationError``,与 Router 层 ``Query(ge=10, le=300)``
|
||
形成双层防御,覆盖非 HTTP 调用方(如内部 API 直接构造 DTO)的
|
||
兜底场景(INV-DASHBOARD-REALTIME-WINDOW)。
|
||
"""
|
||
if self.window_seconds < 10 or self.window_seconds > 300:
|
||
raise ValidationError(
|
||
"window_seconds",
|
||
f"window_seconds must be in [10, 300], got {self.window_seconds}",
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ChannelRealtimeStat:
|
||
"""渠道实时统计(DSB-REALTIME by_channel 条目)。
|
||
|
||
描述单渠道实时消息速率与活跃会话数,用于实时监控的按渠道切片展示。
|
||
|
||
字段:
|
||
channel_type: 渠道类型字符串值。
|
||
mps: 每秒消息数(messages per second)。
|
||
active_sessions: 活跃会话数。
|
||
"""
|
||
|
||
channel_type: str
|
||
mps: float
|
||
active_sessions: int
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RealtimeMetricsResult:
|
||
"""实时监控结果(DSB-REALTIME)。
|
||
|
||
描述实时监控纯内存态聚合结果,包含全局消息速率、活跃会话/会话数、
|
||
队列深度、worker 利用率与按渠道切片。高频查询场景使用,业务不开启
|
||
DB 事务,审计走 INDEPENDENT 独立事务 best-effort 写入。
|
||
|
||
字段:
|
||
window_seconds: 滑动窗口秒数。
|
||
messages_per_second: 全局每秒消息数。
|
||
active_sessions: 活跃会话总数。
|
||
active_conversations: 活跃会话总数(与 active_sessions 同义,由
|
||
不同来源聚合:sessions 来自 QueuePort,conversations 来自会话仓库)。
|
||
queue_depth: 当前队列深度。
|
||
worker_utilization: worker 利用率(0.0-1.0,与 ``WorkerStatus``
|
||
同源;ARQ 适配器当前不采集真实利用率,返回 ``None`` 表示未采集)。
|
||
by_channel: 按渠道切片的实时统计元组(全局查询时遍历已注册渠道
|
||
填充,单渠道过滤时仅含该渠道一条)。
|
||
"""
|
||
|
||
window_seconds: int
|
||
messages_per_second: float
|
||
active_sessions: int
|
||
active_conversations: int
|
||
queue_depth: int
|
||
worker_utilization: float | None
|
||
by_channel: tuple[ChannelRealtimeStat, ...]
|
||
|
||
|
||
__all__ = [
|
||
"AccountStats",
|
||
"ChannelDeliveryStat",
|
||
"ChannelRealtimeStat",
|
||
"DashboardDeliveryQuery",
|
||
"DashboardDeliveryResult",
|
||
"DashboardOverview",
|
||
"DashboardTodoResult",
|
||
"MessageStats",
|
||
"RealtimeMetricsResult",
|
||
"RealtimeQuery",
|
||
"SessionStats",
|
||
]
|