新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
247 lines
7.1 KiB
Python
247 lines
7.1 KiB
Python
"""会话 DTO。
|
||
|
||
定义渠道会话相关的不可变值对象,包括渠道会话 ID、对端 ID、会话类型、
|
||
会话所有者、所有者转移命令与临时会话模式。所有 DTO 均为
|
||
``dataclass(frozen=True)``,仅依赖标准库与契约层内部类型,用于会话
|
||
解析、所有者管理与临时会话识别。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
from enum import StrEnum
|
||
from typing import Any
|
||
|
||
from yuxi.channels.contract.dtos.common import BatchOperationFailure, Operator
|
||
from yuxi.channels.contract.errors import ValidationError
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ChannelSessionId:
|
||
"""渠道会话 ID。
|
||
|
||
标识渠道侧一次会话的唯一 ID,用于会话定位与路由匹配。
|
||
|
||
字段:
|
||
value: 渠道会话 ID 字符串。
|
||
"""
|
||
|
||
value: str
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PeerId:
|
||
"""对端 ID。
|
||
|
||
标识渠道侧会话对端(用户或群组)的唯一 ID,用于会话解析与身份关联。
|
||
|
||
字段:
|
||
value: 对端 ID 字符串。
|
||
"""
|
||
|
||
value: str
|
||
|
||
|
||
class ChatType(StrEnum):
|
||
"""会话类型。
|
||
|
||
标识渠道侧会话的拓扑类型,用于路由匹配与会话所有者策略。继承
|
||
``str, Enum`` 以支持 JSON 序列化与字符串比较。
|
||
|
||
取值:
|
||
P2P: 单聊(点对点)。
|
||
GROUP: 群聊。
|
||
"""
|
||
|
||
P2P = "p2p"
|
||
GROUP = "group"
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SessionOwner:
|
||
"""会话所有者。
|
||
|
||
描述会话的所有者信息,用于 FR-26 所有者保护策略,确保仅所有者可触发
|
||
关键操作。
|
||
|
||
字段:
|
||
conversation_id: 所属会话 ID。
|
||
owner_peer_id: 所有者对端 ID。
|
||
created_at: 所有者关系建立时间。
|
||
"""
|
||
|
||
conversation_id: str
|
||
owner_peer_id: str
|
||
created_at: datetime
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class OwnerTransferCmd:
|
||
"""所有者转移命令。
|
||
|
||
由端口方法 ``ConversationPort.transferSessionOwner`` 引用,用于将会话
|
||
所有者从当前对端转移至新对端,需记录操作人以满足审计要求(FR-26)。
|
||
|
||
字段:
|
||
conversation_id: 目标会话 ID。
|
||
new_owner_id: 新所有者对端 ID。
|
||
operator: 操作人(审计用)。
|
||
"""
|
||
|
||
conversation_id: str
|
||
new_owner_id: str
|
||
operator: Operator
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验必填字段非空(FR-26)。
|
||
|
||
``conversation_id`` 与 ``new_owner_id`` 必须非空,在构造时即抛出
|
||
``ValidationError``,adapter 不再做该校验(INV-8)。
|
||
"""
|
||
if not self.conversation_id:
|
||
raise ValidationError("conversation_id", "must not be empty")
|
||
if not self.new_owner_id:
|
||
raise ValidationError("new_owner_id", "must not be empty")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class TemporarySessionPattern:
|
||
"""临时会话模式。
|
||
|
||
描述临时会话的匹配模式(FR-27),用于识别周期性任务产生的临时会话,
|
||
支持 cron 等模式表达式。
|
||
|
||
字段:
|
||
pattern: 模式表达式(如 ``cron:<任务 ID>:<运行 ID>``)。
|
||
description: 模式描述。
|
||
"""
|
||
|
||
pattern: str
|
||
description: str
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class CloseSessionCmd:
|
||
"""关闭会话命令。
|
||
|
||
由 ``SessionManagementPort.closeSession`` 端口方法引用,用于关闭指定
|
||
渠道会话,停止接收新消息。需记录操作人以满足审计要求。
|
||
|
||
字段:
|
||
session_id: 要关闭的会话 ID。
|
||
reason: 关闭原因(审计用,可选)。
|
||
operator: 操作人(审计用)。
|
||
"""
|
||
|
||
session_id: str
|
||
operator: Operator
|
||
reason: str | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验必填字段非空。
|
||
|
||
``session_id`` 必须非空,在构造时即抛出 ``ValidationError``,
|
||
adapter 不再做该校验(INV-8)。
|
||
"""
|
||
if not self.session_id:
|
||
raise ValidationError("session_id", "must not be empty")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SessionMessageItem:
|
||
"""会话消息列表项。
|
||
|
||
描述渠道会话消息列表中的单条消息,包含消息核心字段与渠道侧状态。
|
||
|
||
字段:
|
||
message_id: 消息 ID。
|
||
conversation_id: 会话 ID。
|
||
channel_type: 渠道类型。
|
||
role: 消息角色(user | assistant | admin)。
|
||
content: 消息内容。
|
||
channel_msg_id: 渠道侧消息 ID(可选)。
|
||
channel_status: 渠道侧消息状态(可选)。
|
||
created_at: 创建时间。
|
||
"""
|
||
|
||
message_id: str
|
||
conversation_id: str
|
||
channel_type: str
|
||
role: str
|
||
content: str
|
||
created_at: datetime
|
||
channel_msg_id: str | None = None
|
||
channel_status: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SessionStatsResult:
|
||
"""会话统计结果(SES-STATS-01)。
|
||
|
||
描述单会话的统计指标,由 dispatch handler 组合 ConversationPort 消息
|
||
查询后计算,不在核心层引入统计聚合根。
|
||
|
||
字段:
|
||
message_count: 消息总数。
|
||
user_message_count: 用户消息数。
|
||
assistant_message_count: 助手消息数。
|
||
started_at: 会话开始时间。
|
||
last_activity_at: 最近活动时间。
|
||
duration_seconds: 会话时长(秒)。
|
||
first_response_seconds: 首条用户消息到首条助手消息的间隔(秒,
|
||
无助手消息时为 None)。
|
||
avg_response_seconds: 平均响应间隔(秒,无助手消息时为 None)。
|
||
"""
|
||
|
||
message_count: int
|
||
user_message_count: int
|
||
assistant_message_count: int
|
||
started_at: datetime
|
||
last_activity_at: datetime
|
||
duration_seconds: int
|
||
first_response_seconds: int | None = None
|
||
avg_response_seconds: float | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BatchCloseSessionsCmd:
|
||
"""批量关闭会话命令(SES-BATCH-CLOSE-01)。
|
||
|
||
由 ``SessionManagementPort.batchCloseSessions`` 引用,支持显式 ID 列表
|
||
或筛选条件两种模式(二者不可同时空)。``session_ids`` 与 ``filter``
|
||
同时为空时由 dispatch handler 决定是否抛校验异常。
|
||
|
||
字段:
|
||
session_ids: 显式会话 ID 元组(默认空元组)。
|
||
filter: 筛选条件(含 channel_type / inactive_before / status,
|
||
可选)。
|
||
max_count: 单次最大关闭数(默认 100,1-1000)。
|
||
reason: 关闭原因(审计用,可选)。
|
||
operator: 操作人(审计用)。
|
||
"""
|
||
|
||
operator: Operator
|
||
session_ids: tuple[str, ...] = ()
|
||
filter: dict[str, Any] | None = None
|
||
max_count: int = 100
|
||
reason: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BatchCloseResult:
|
||
"""批量关闭会话结果(SES-BATCH-CLOSE-01)。
|
||
|
||
描述逐条独立事务关闭会话的执行结果,``failed`` 使用通用
|
||
``BatchOperationFailure``(``id`` 字段承载 session_id)。
|
||
|
||
字段:
|
||
total: 待关闭会话总数。
|
||
closed: 成功关闭的会话 ID 元组。
|
||
failed: 失败条目元组。
|
||
"""
|
||
|
||
total: int
|
||
closed: tuple[str, ...]
|
||
failed: tuple[BatchOperationFailure, ...]
|