包含以下变更: 1. 重构微信WOC账户模型,移除固定DEFAULT_ACCOUNT 2. 新增路由绑定管理、目录搜索导出能力 3. 扩展消息查询与出站管理过滤条件 4. 新增SSE事件广播、敏感字段/配置作用域注册表 5. 新增审计日志与配对过期定时任务 6. 优化会话处理与参数校验逻辑 7. 修复sender_id校验与outbox序列化问题
332 lines
10 KiB
Python
332 lines
10 KiB
Python
"""出站 DTO。
|
||
|
||
定义出站管道的富消息与最终输出值对象,包括富消息按钮、富消息选项、
|
||
富消息、富消息字段、出站负载、格式化消息、可信消息与最终消息。
|
||
所有 DTO 均为 ``dataclass(frozen=True)``,仅依赖标准库与契约层内部
|
||
类型,用于出站消息的格式化、可信注入与最终投递。集合字段使用 tuple
|
||
以保证 frozen dataclass 的不可变语义。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from typing import Any
|
||
|
||
from yuxi.channels.contract.dtos.common import Attachment, MessageFormat
|
||
from yuxi.channels.contract.errors import ValidationError
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageButton:
|
||
"""富消息按钮。
|
||
|
||
描述富消息中的可点击按钮,由适配器按渠道协议渲染为对应交互元素。
|
||
|
||
字段:
|
||
label: 按钮显示文本。
|
||
action: 按钮动作标识。
|
||
value: 按钮动作携带的值(可选)。
|
||
"""
|
||
|
||
label: str
|
||
action: str
|
||
value: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageOption:
|
||
"""富消息选项。
|
||
|
||
描述富消息中的可选项,用于下拉 / 列表类交互组件。
|
||
|
||
字段:
|
||
label: 选项显示文本。
|
||
value: 选项值。
|
||
"""
|
||
|
||
label: str
|
||
value: str
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageSelect:
|
||
"""富消息下拉选择器组件。
|
||
|
||
字段:
|
||
name: 表单字段名。
|
||
label: 显示标签。
|
||
options: 可选项列表。
|
||
placeholder: 占位文本,可选。
|
||
default_value: 默认值,可选。
|
||
"""
|
||
|
||
name: str
|
||
label: str
|
||
options: tuple[RichMessageOption, ...]
|
||
placeholder: str | None = None
|
||
default_value: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageDatePicker:
|
||
"""富消息日期选择器组件。
|
||
|
||
字段:
|
||
name: 表单字段名。
|
||
label: 显示标签。
|
||
placeholder: 占位文本,可选。
|
||
default_value: 默认日期(ISO 格式),可选。
|
||
min_date: 最小可选日期(ISO 格式),可选。
|
||
max_date: 最大可选日期(ISO 格式),可选。
|
||
"""
|
||
|
||
name: str
|
||
label: str
|
||
placeholder: str | None = None
|
||
default_value: str | None = None
|
||
min_date: str | None = None
|
||
max_date: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageCheckbox:
|
||
"""富消息复选框组件。
|
||
|
||
字段:
|
||
name: 表单字段名。
|
||
label: 显示标签。
|
||
options: 可选项列表。
|
||
default_values: 默认选中值列表。
|
||
"""
|
||
|
||
name: str
|
||
label: str
|
||
options: tuple[RichMessageOption, ...]
|
||
default_values: tuple[str, ...] = ()
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageInput:
|
||
"""富消息输入框组件。
|
||
|
||
字段:
|
||
name: 表单字段名。
|
||
label: 显示标签。
|
||
placeholder: 占位文本,可选。
|
||
default_value: 默认值,可选。
|
||
max_length: 最大输入长度,可选。
|
||
input_type: 输入类型(text/number/email 等),默认 text。
|
||
"""
|
||
|
||
name: str
|
||
label: str
|
||
placeholder: str | None = None
|
||
default_value: str | None = None
|
||
max_length: int | None = None
|
||
input_type: str = "text"
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessage:
|
||
"""富消息。
|
||
|
||
描述结构化的富消息内容,包括标题、正文、描述、图片、视频与交互元素
|
||
(按钮 / 选项),由适配器按渠道协议渲染。集合字段使用 tuple 以
|
||
保证不可变。
|
||
|
||
字段:
|
||
text: 正文文本。
|
||
title: 标题(可选)。
|
||
description: 描述(可选)。
|
||
image_url: 图片 URL(可选,向后兼容)。
|
||
video_url: 视频 URL(可选,FR-50)。
|
||
attachments: 多附件列表(承载多图、文件等,FR-50)。
|
||
buttons: 按钮列表(默认空 tuple)。
|
||
options: 选项列表(默认空 tuple)。
|
||
metadata: 渠道侧元数据(可选)。
|
||
selects: 下拉选择器组件列表。
|
||
date_pickers: 日期选择器组件列表。
|
||
checkboxes: 复选框组件列表。
|
||
inputs: 输入框组件列表。
|
||
"""
|
||
|
||
text: str
|
||
title: str | None = None
|
||
description: str | None = None
|
||
image_url: str | None = None
|
||
video_url: str | None = None
|
||
attachments: tuple[Attachment, ...] = ()
|
||
buttons: tuple[RichMessageButton, ...] = ()
|
||
options: tuple[RichMessageOption, ...] = ()
|
||
metadata: dict[str, Any] | None = None
|
||
selects: tuple[RichMessageSelect, ...] = ()
|
||
date_pickers: tuple[RichMessageDatePicker, ...] = ()
|
||
checkboxes: tuple[RichMessageCheckbox, ...] = ()
|
||
inputs: tuple[RichMessageInput, ...] = ()
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RichMessageFields:
|
||
"""富消息字段。
|
||
|
||
包装富消息与其回退格式,用于出站负载携带富消息渲染信息,当渠道
|
||
不支持富消息时按回退格式渲染。
|
||
|
||
字段:
|
||
rich_message: 富消息内容。
|
||
fallback_format: 回退格式(默认 MARKDOWN)。
|
||
"""
|
||
|
||
rich_message: RichMessage
|
||
fallback_format: MessageFormat = MessageFormat.MARKDOWN
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class OutboundPayload:
|
||
"""出站负载。
|
||
|
||
描述出站管道的统一负载,关联 Agent 运行 ID,携带流式分块、可选的
|
||
富消息字段与附件列表,用于驱动出站消息的格式化与投递。
|
||
|
||
字段:
|
||
agent_run_id: 关联的 Agent 运行 ID。
|
||
stream_chunks: 流式分块列表(默认空 tuple)。
|
||
rich_message_fields: 富消息字段(可选)。
|
||
attachments: 附件列表(默认空 tuple,FR-51)。
|
||
"""
|
||
|
||
agent_run_id: str | None = None
|
||
stream_chunks: tuple[str, ...] = ()
|
||
rich_message_fields: RichMessageFields | None = None
|
||
attachments: tuple[Attachment, ...] = ()
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class FormattedMessage:
|
||
"""格式化消息。
|
||
|
||
描述经格式化后的消息内容,携带文本、格式、可选的富消息与附件列表,
|
||
用于适配器按渠道协议渲染最终消息。
|
||
|
||
字段:
|
||
content: 消息文本内容。
|
||
format: 消息格式(默认 TEXT)。
|
||
rich_message: 渲染后的渠道原生富消息对象(可选,由
|
||
RichMessageAdapter.renderRichMessage 产生,类型由具体适配器
|
||
决定)。
|
||
attachments: 附件列表(默认空 tuple,FR-51)。
|
||
"""
|
||
|
||
content: str
|
||
format: MessageFormat = MessageFormat.TEXT
|
||
rich_message: Any = None
|
||
attachments: tuple[Attachment, ...] = ()
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class TrustedMessage:
|
||
"""可信消息。
|
||
|
||
描述经服务端强制注入发送者身份与所有者标记的消息,用于可信注入
|
||
边界(FR-25),确保渠道侧无法伪造发送者。
|
||
|
||
字段:
|
||
content: 消息文本内容。
|
||
sender_id: 发送者 ID(服务端强制注入)。
|
||
is_owner: 是否为会话所有者(服务端强制注入,默认 False)。
|
||
rich_message: 渲染后的渠道原生富消息对象(可选,从
|
||
FormattedMessage 透传,FR-08)。
|
||
"""
|
||
|
||
content: str
|
||
sender_id: str
|
||
is_owner: bool = False
|
||
rich_message: Any = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验必填字段非空。
|
||
|
||
``content`` 与 ``sender_id`` 必须非空,在构造时即抛出
|
||
``ValidationError``,避免空消息内容或空发送者身份传播到渠道
|
||
适配器(INV-8)。
|
||
"""
|
||
if not self.content:
|
||
raise ValidationError("content", "must not be empty")
|
||
if not self.sender_id:
|
||
raise ValidationError("sender_id", "must not be empty")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class FinalMessage:
|
||
"""最终消息。
|
||
|
||
描述出站管道最终投递至渠道适配器的消息,携带文本、发送者 ID 与
|
||
可选前缀,前缀用于所有者标记等场景。
|
||
|
||
字段:
|
||
content: 消息文本内容。
|
||
sender_id: 发送者 ID。
|
||
prefix: 前缀(可选,如所有者标记)。
|
||
rich_message: 渲染后的渠道原生富消息对象(可选,从
|
||
TrustedMessage 透传,FR-08)。
|
||
"""
|
||
|
||
content: str
|
||
sender_id: str
|
||
prefix: str | None = None
|
||
rich_message: Any = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验必填字段非空。
|
||
|
||
``content`` 与 ``sender_id`` 必须非空,在构造时即抛出
|
||
``ValidationError``,避免空消息内容或空发送者身份投递到渠道
|
||
(INV-8)。
|
||
"""
|
||
if not self.content:
|
||
raise ValidationError("content", "must not be empty")
|
||
if not self.sender_id:
|
||
raise ValidationError("sender_id", "must not be empty")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BatchSendItem:
|
||
"""批量发送单条结果。
|
||
|
||
描述 ``OutboundAdapter.batchSendMessages`` 单条投递结果,与
|
||
``MultiPartReceipt`` 区别:``MultiPartReceipt`` 描述**同一条**逻辑
|
||
消息拆分为多个分片的回执,``BatchSendItem`` 描述**同一负载**投递到
|
||
多个 peer 的逐条结果。
|
||
|
||
字段:
|
||
peer_id: 接收目标 ID。
|
||
channel_msg_id: 渠道侧消息 ID(成功时非空,失败时为空字符串)。
|
||
success: 是否投递成功。
|
||
error: 失败时的错误信息(可选,成功时为 ``None``)。
|
||
"""
|
||
|
||
peer_id: str
|
||
channel_msg_id: str
|
||
success: bool
|
||
error: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BatchSendResult:
|
||
"""批量发送结果。
|
||
|
||
描述 ``OutboundAdapter.batchSendMessages`` 的聚合结果,包含逐条结果
|
||
与汇总统计。集合字段使用 tuple 以保证 frozen dataclass 的不可变语义。
|
||
|
||
字段:
|
||
total: 总投递目标数。
|
||
succeeded: 成功的目标 ID 元组。
|
||
failed: 失败条目元组(含目标 ID 与错误信息)。
|
||
items: 逐条结果元组(含成功与失败)。
|
||
"""
|
||
|
||
total: int
|
||
succeeded: tuple[str, ...]
|
||
failed: tuple[BatchSendItem, ...]
|
||
items: tuple[BatchSendItem, ...]
|