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

516 lines
16 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.

"""渠道类型 DTO。
定义渠道账户、会话、身份、消息等跨层共享的不可变值对象。所有 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
class ChannelType(StrEnum):
"""渠道类型。
标识多渠道网关支持的外部渠道种类,用于路由匹配、账户管理与插件解析。
继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。
取值:
FEISHU: 飞书。
DINGTALK: 钉钉。
WECOM: 企业微信。
WEBCHAT: 内置 Web 渠道FR-31
TELEGRAM: Telegram。
DISCORD: Discord。
WHATSAPP: WhatsApp。
CUSTOM: 自定义渠道。
约束:
框架层contract / core / application**禁止** 在任何决策语句中
分支到具体枚举值(仅插件自身代码可分支),渠道特性决策 **必须**
通过 ``ChannelManifest`` 声明字段驱动(如 ``critical`` /
``requires_dm_pairing`` / ``requires_outbound_delivery``)。
新增渠道优先用 ``CUSTOM`` + manifest 声明,避免修改框架契约层枚举。
"""
FEISHU = "feishu"
DINGTALK = "dingtalk"
WECOM = "wecom"
WEBCHAT = "webchat"
TELEGRAM = "telegram"
DISCORD = "discord"
WHATSAPP = "whatsapp"
CUSTOM = "custom"
class AccountStatus(StrEnum):
"""渠道账户状态枚举。
聚合根 ``ChannelAccount`` 的状态机枚举。状态转换规则参见 ``ChannelAccount``
聚合根:
- ``ACTIVE`` ↔ ``DISABLED``:通过 ``enable()`` / ``disable()`` 切换。
- ``ACTIVE`` → ``DEGRADED``:通过 ``degrade()`` 切换(不可从 ``DISABLED`` 降级)。
- ``DEGRADED`` → ``ACTIVE``:通过 ``recover()`` 切换(不可直接 ``enable``)。
- ``DISABLED`` 不可直接 ``recover``,需先 ``enable`` 回到 ``ACTIVE``。
"""
ACTIVE = "active"
DISABLED = "disabled"
DEGRADED = "degraded"
class SessionStatus(StrEnum):
"""渠道会话状态枚举。
标识渠道会话的生命周期状态,用于会话关闭与消息投递决策。继承
``str, Enum`` 以支持 JSON 序列化与字符串比较。
取值:
ACTIVE: 活跃会话,可正常收发消息。
CLOSED: 已关闭会话,停止接收新消息。
"""
ACTIVE = "active"
CLOSED = "closed"
@dataclass(frozen=True)
class ChannelAccount:
"""渠道账户。
描述一个渠道账户的完整配置用于账户管理与消息路由。config 字段为脱敏后
的渠道配置,不包含原始密钥。
字段:
channel_type: 渠道类型。
account_id: 渠道账户 ID全局唯一
display_name: 显示名称。
config: 渠道配置(脱敏后)。
enabled: 是否启用。
status: 账户状态机值active/disabled/degraded
created_at: 创建时间。
updated_at: 更新时间。
transport_cursor: 传输游标Puller类型使用。
last_rotated_at: 凭据最近轮换时间(可选)。
"""
channel_type: ChannelType
account_id: str
display_name: str
config: dict[str, Any]
enabled: bool = True
status: AccountStatus = AccountStatus.ACTIVE
created_at: datetime | None = None
updated_at: datetime | None = None
transport_cursor: str = ""
last_rotated_at: datetime | None = None
@dataclass(frozen=True)
class ChannelAccountSummary:
"""渠道账户摘要。
用于账户列表查询等场景,仅包含展示必要字段,不泄露配置详情。
字段:
channel_type: 渠道类型。
account_id: 渠道账户 ID。
display_name: 显示名称。
enabled: 是否启用。
status: 账户状态机值active/disabled/degraded
"""
channel_type: ChannelType
account_id: str
display_name: str
enabled: bool
status: AccountStatus = AccountStatus.ACTIVE
@dataclass(frozen=True)
class ChannelSession:
"""渠道会话。
描述渠道侧会话状态,关联会话与统一身份、主会话所有者,支持临时会话标记
与软删除。
字段:
session_id: 会话 ID。
channel_type: 渠道类型。
account_id: 渠道账户 ID。
peer_id: 对端 ID。
chat_type: 会话类型("p2p" | "group")。
conversation_id: 关联的内部会话 ID。
unified_identity_id: 统一身份 ID。
owner_peer_id: 主会话所有者对端 IDFR-26
is_temporary: 临时会话标记FR-27
created_at: 创建时间。
updated_at: 更新时间。
deleted_at: 软删除时间。
closed_at: 会话关闭时间(可选)。
last_message_at: 最近一次消息时间(可选,用于会话列表排序与
inactive 临时会话清理FR-27
"""
session_id: str
channel_type: ChannelType
account_id: str
peer_id: str
chat_type: str
conversation_id: str | None = None
unified_identity_id: str | None = None
owner_peer_id: str | None = None
is_temporary: bool = False
created_at: datetime | None = None
updated_at: datetime | None = None
deleted_at: datetime | None = None
closed_at: datetime | None = None
last_message_at: datetime | None = None
@dataclass(frozen=True)
class UserIdentity:
"""用户身份。
描述用户在渠道侧或外部身份系统的身份信息,用于身份解析与统一身份关联。
``user_id`` 为可空字段但需显式传入,以区分"未关联用户""使用默认值"
字段:
identity_id: 统一身份 ID。
user_id: 关联用户表 ID可空
identity_type: 身份类型(邮箱 / 手机 / 组织员工 ID
identity_value: 身份值。
channel_type: 渠道类型。
channel_sender_id: 渠道侧发送者 ID。
source: 身份来源。
created_at: 创建时间。
updated_at: 更新时间。
"""
identity_id: str
user_id: str | None
identity_type: str
identity_value: str
channel_type: ChannelType | None = None
channel_sender_id: str | None = None
source: str | None = None
created_at: datetime | None = None
updated_at: datetime | None = None
@dataclass(frozen=True)
class SessionInfo:
"""会话信息。
渠道适配器解析原始事件后产出的会话定位信息,用于核心层解析或创建会话。
字段:
channel_type: 渠道类型。
account_id: 渠道账户 ID。
peer_id: 对端 ID。
chat_type: 会话类型("p2p" | "group")。
group_id: 群组 ID。
topic_id: 话题 ID。
"""
channel_type: ChannelType
account_id: str
peer_id: str
chat_type: str
group_id: str | None = None
topic_id: str | None = None
@dataclass(frozen=True)
class Message:
"""消息。
描述一条消息的完整状态,包括内部字段与渠道侧状态字段(已读、撤回、编辑
等),用于消息持久化与渠道侧状态同步。
字段:
message_id: 消息 ID。
conversation_id: 会话 ID。
role: 角色user | assistant | admin
content: 消息文本内容。
channel_status: 渠道侧状态FR-09
channel_msg_id: 渠道侧消息 ID。
ref_channel_msg_id: 引用的渠道消息 ID编辑/回复场景)。
channel_status_history: 渠道侧状态事件历史数组FR-09
operations_history: 消息操作历史数组FR-12每项记录一次消息操作
的执行信息(操作类型、执行者、时间戳、是否成功)。
channel_read_at: 渠道侧已读时间。
channel_recalled_at: 渠道侧撤回时间。
channel_edited_at: 渠道侧编辑时间。
created_at: 创建时间。
channel_type: 渠道类型(本期新增,由持久化层从会话关联填充,供 _messageStatus 返回)。
"""
message_id: str
conversation_id: str
role: str
content: str
channel_status: str | None = None
channel_msg_id: str | None = None
ref_channel_msg_id: str | None = None
channel_status_history: list[dict] | None = None
operations_history: list[dict] | None = None
channel_read_at: datetime | None = None
channel_recalled_at: datetime | None = None
channel_edited_at: datetime | None = None
created_at: datetime | None = None
channel_type: ChannelType | None = None
@dataclass(frozen=True)
class RotateCredentialsResult:
"""凭据轮换结果。
描述账户凭据轮换操作的返回结果,包括轮换时间与是否成功撤销旧凭据。
字段:
account_id: 渠道账户 ID。
rotated_at: 轮换完成时间。
old_credentials_revoked: 旧凭据是否已成功撤销。
"""
account_id: str
rotated_at: datetime
old_credentials_revoked: bool
@dataclass(frozen=True)
class ConnectionCheckResult:
"""连接检查结果。
描述单次渠道账户连接连通性检查的结果。
字段:
success: 连接是否成功。
checked_at: 检查时间。
latency_ms: 连接延迟(毫秒,可选)。
error: 错误信息(失败时填充,可选)。
"""
success: bool
checked_at: datetime
latency_ms: int | None = None
error: str | None = None
@dataclass(frozen=True)
class TestConnectionResult:
"""测试连接结果。
描述渠道账户连接测试的完整结果,包括连通性检查与凭据验证两部分。
字段:
account_id: 渠道账户 ID。
connection: 连通性检查结果。
credentials_valid: 凭据是否有效。
tested_at: 测试时间。
"""
account_id: str
connection: ConnectionCheckResult
credentials_valid: bool
tested_at: datetime
@dataclass(frozen=True)
class MessageSearchItem:
"""消息搜索结果项。
描述消息全文搜索的单条匹配结果,包含消息核心字段与匹配高亮信息。
字段:
message_id: 消息 ID。
conversation_id: 会话 ID。
channel_session_id: 渠道会话 ID可选
channel_type: 渠道类型。
role: 消息角色user | assistant | admin
content: 消息内容。
created_at: 创建时间。
snippet: 匹配片段(高亮后,可选)。
"""
message_id: str
conversation_id: str
channel_type: ChannelType
role: str
content: str
created_at: datetime
channel_session_id: str | None = None
snippet: str | None = None
@dataclass(frozen=True)
class WebhookTestCmd:
"""Webhook 测试命令WHK-TEST
由 ``AccountManagementPort.testWebhook`` 引用,触发对指定渠道账户的
webhook 测试事件投递。``account_id`` 缺省时由 dispatch handler 取该
渠道首个账户。
字段:
channel_type: 渠道类型。
account_id: 渠道账户 ID可选缺省取首个账户
event_type: 测试事件类型(默认 ``test_event``)。
payload: 测试事件负载(可选)。
operator: 操作人(审计用)。
"""
channel_type: ChannelType
operator: Operator
account_id: str | None = None
event_type: str = "test_event"
payload: dict[str, Any] | None = None
@dataclass(frozen=True)
class WebhookTestResult:
"""Webhook 测试结果WHK-TEST
描述测试事件投递的结果,由插件 ``WebhookTestable.testWebhook`` 返回。
字段:
test_id: 测试事件 ID。
delivered: 是否投递成功。
http_status: 渠道侧返回的 HTTP 状态码(投递失败时可能为 None
response_time_ms: 响应延迟(毫秒,投递失败时可能为 None
error: 错误信息(投递失败时填充,可选)。
"""
test_id: str
delivered: bool
http_status: int | None = None
response_time_ms: int | None = None
error: str | None = None
@dataclass(frozen=True)
class BatchStateChangeCmd:
"""批量启停账户命令ACC-BATCH-STATE
由 ``AccountManagementPort.batchEnableAccounts`` /
``batchDisableAccounts`` 引用,支持显式 ID 列表或筛选条件两种模式。
``action`` 取值 ``enable`` / ``disable``,由 dispatch handler 决定。
字段:
action: 操作类型(``enable`` / ``disable``)。
operator: 操作人(审计用)。
account_ids: 显式账户 ID 元组(默认空元组)。
filter: 筛选条件(含 channel_type / status可选
reason: 操作原因(审计用,可选)。
"""
action: str
operator: Operator
account_ids: tuple[str, ...] = ()
filter: dict[str, Any] | None = None
reason: str | None = None
@dataclass(frozen=True)
class BatchStateChangeResult:
"""批量启停账户结果ACC-BATCH-STATE
描述逐条独立事务启停账户的执行结果,``failed`` 使用通用
``BatchOperationFailure````id`` 字段承载 account_id
字段:
total: 待操作账户总数。
succeeded: 成功操作的账户 ID 元组。
failed: 失败条目元组。
"""
total: int
succeeded: tuple[str, ...]
failed: tuple[BatchOperationFailure, ...]
@dataclass(frozen=True)
class AccountExportResult:
"""账户配置导出结果ACC-EXPORT
描述账户配置导出的返回内容,``secrets_included=false`` 时 ``raw_config``
中敏感字段已通过 MaskingPort 脱敏。
字段:
account_id: 渠道账户 ID。
channel_type: 渠道类型。
display_name: 账户显示名。
raw_config: 原始配置(脱敏后或含凭据)。
exported_at: 导出时间。
secrets_included: 是否包含敏感凭据。
"""
account_id: str
channel_type: ChannelType
display_name: str
raw_config: dict[str, Any]
exported_at: datetime
secrets_included: bool
@dataclass(frozen=True)
class CloneAccountCmd:
"""克隆账户命令ACC-CLONE
由 ``AccountManagementPort.cloneAccount`` 引用,基于源账户配置创建
新账户。``include_credentials=false`` 时清除凭据字段。
字段:
channel_type: 渠道类型。
account_id: 源账户 ID。
new_display_name: 新账户显示名(必填)。
operator: 操作人(审计用)。
new_raw_config_overrides: 配置覆盖项(可选,默认空 dict
include_credentials: 是否克隆凭据(默认 False
"""
channel_type: ChannelType
account_id: str
new_display_name: str
operator: Operator
new_raw_config_overrides: dict[str, Any] | None = None
include_credentials: bool = False
def __post_init__(self) -> None:
"""校验必填字段非空ACC-CLONE
``new_display_name`` 必须非空字符串,在构造时即抛出
``ValidationError``,避免空值传播到聚合根 ``clone()`` 后才暴露
INV-8
"""
if not self.new_display_name:
raise ValidationError(
"new_display_name", "new_display_name must not be empty"
)
@dataclass(frozen=True)
class CloneAccountResult:
"""克隆账户结果ACC-CLONE
描述克隆操作返回的新账户信息,新账户默认为 DISABLED 状态,需手动 enable。
字段:
new_account_id: 新账户 ID。
cloned_from: 源账户 ID。
status: 新账户状态(默认 DISABLED
created_at: 创建时间。
"""
new_account_id: str
cloned_from: str
status: AccountStatus
created_at: datetime