ForcePilot/backend/package/yuxi/channels/contract/dtos/pairing.py
Kris c7eb196a7e chore: 批量完成多模块迭代优化与功能完善
本次提交涵盖了近百处代码优化与功能补全,包括:
1. 完善配置与数据模型:新增expired_at配对记录字段、路由绑定乐观锁版本控制、会话路由信息追踪字段
2. 优化业务流程:添加幂等记录操作人审计、会话合并领域服务文档更新、媒体处理异步化改造
3. 新增功能能力:健康检查时间更新、会话路由信息更新接口、内容审核/幂等记录清理定时任务
4. 修复与简化:移除废弃的max_message_length属性、修复微信iLink适配器配置读取路径、简化配对过期扫描逻辑
5. 代码规范优化:统一敏感词检测工具导入、完善事务上下文处理注释、调整wechat_woc入站适配器sender回退逻辑
2026-07-08 03:57:05 +08:00

390 lines
12 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。
定义 DM 安全与配对审批的不可变值对象,包括配对 ID、配对状态、DM 决策、
DM 策略、配对审批、配对记录与机器人循环预算。所有 DTO 均为
``dataclass(frozen=True)``,仅依赖标准库,用于 DM 安全策略决策与配对
审批流程。
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from enum import StrEnum
from yuxi.channels.contract.dtos.channel import ChannelType
from yuxi.channels.contract.dtos.common import Operator
from yuxi.channels.contract.errors import ValidationError
@dataclass(frozen=True)
class PairingId:
"""配对 ID。
标识一次配对审批流程的全局唯一 ID用于配对记录关联与审计。
字段:
value: 配对 ID 字符串。
"""
value: str
class PairingStatus(StrEnum):
"""配对状态。
标识配对审批流程的当前状态,用于配对记录查询与状态机流转。继承
``str, Enum`` 以支持 JSON 序列化与字符串比较。
取值:
PENDING: 待审批。
APPROVED: 已批准。
REJECTED: 已拒绝。
EXPIRED: 已过期。
REVOKED: 已撤销FR-33已批准的配对被主动撤销
"""
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
EXPIRED = "expired"
REVOKED = "revoked"
class DmDecision(StrEnum):
"""DM 决策。
描述 DM 消息的处置决策结果,用于入站管道的 DM 安全策略执行。继承
``str, Enum`` 以支持 JSON 序列化与字符串比较。
取值:
ALLOW: 允许。
DENY: 拒绝。
PENDING_PAIRING: 待配对审批。
WHITELIST: 白名单放行。
"""
ALLOW = "allow"
DENY = "deny"
PENDING_PAIRING = "pending_pairing"
WHITELIST = "whitelist"
class DmPolicy(StrEnum):
"""DM 策略。
描述渠道账户的 DM 安全策略,用于 DM 决策引擎的策略匹配。继承
``str, Enum`` 以支持 JSON 序列化与字符串比较。
取值:
ALLOW: 允许所有 DM。
DENY: 拒绝所有 DM。
PAIRING_REQUIRED: 需配对审批。
WHITELIST: 白名单。
"""
ALLOW = "allow"
DENY = "deny"
PAIRING_REQUIRED = "pairing_required"
WHITELIST = "whitelist"
@dataclass(frozen=True)
class PairingApproval:
"""配对审批。
描述一次配对审批的结果,携带配对 ID、渠道账户 ID、对端 ID、状态、
审批人与过期时间,用于审批结果回传与审计。
字段:
pairing_id: 配对 ID。
channel_account_id: 渠道账户 ID。
peer_id: 对端 ID。
status: 配对状态。
approver_id: 审批人 ID待审批时为 None
approved_at: 审批时间(待审批时为 None
expires_at: 过期时间(可选)。
"""
pairing_id: str
channel_account_id: str
peer_id: str
status: PairingStatus
approver_id: str | None = None
approved_at: datetime | None = None
expires_at: datetime | None = None
@dataclass(frozen=True)
class PairingRecord:
"""配对记录。
描述一条配对审批的持久化记录,携带配对 ID、渠道账户业务 ID、渠道类型、
对端信息、状态、审批人与全量时间戳用于管理员审批列表查询FR-33
状态机流转与审计追溯。
``channel_type`` 与 ``account_id`` 由适配器层在查询时通过 ``channel_accounts``
表关联填充,供管理后台展示与降级检查使用;``listExpiredPendingPairings``
等内部场景可不填充(保持 ``None``)。
字段:
pairing_id: 配对 ID业务标识UUID
channel_account_id: 渠道账户业务 ID字符串与 ``channel_type`` 共同确定账户作用域)。
channel_type: 渠道类型(可选,适配器层关联填充)。
peer_id: 对端用户 ID。
peer_name: 对端名称(冗余展示字段,可选)。
status: 配对状态。
approver_id: 审批人 ID待审批时为 ``None``)。
approved_at: 批准时间approved 状态时填充,可选)。
rejected_at: 拒绝时间rejected 状态时填充,可选)。
expired_at: 过期生效时间expired 状态时填充,由定时任务或审批时回填,可选)。
revoked_at: 撤销时间revoked 状态时填充FR-33可选
reason: 审批原因 / 拒绝原因 / 撤销原因(可选)。
created_at: 创建时间(可选)。
updated_at: 更新时间(可选)。
expires_at: 过期时间(可选,供过期扫描器重建聚合根调用 ``expireIfOverdue``)。
requested_at: 申请时间(可选,供管理后台按申请时间排序展示)。
version: 乐观锁版本号(默认 1
"""
pairing_id: str
channel_account_id: str
peer_id: str
status: PairingStatus
channel_type: ChannelType | None = None
peer_name: str | None = None
approver_id: str | None = None
approved_at: datetime | None = None
rejected_at: datetime | None = None
expired_at: datetime | None = None
revoked_at: datetime | None = None
reason: str | None = None
created_at: datetime | None = None
updated_at: datetime | None = None
expires_at: datetime | None = None
requested_at: datetime | None = None
version: int = 1
@dataclass(frozen=True)
class PairingQuery:
"""配对查询。
描述配对审批记录的查询条件,支持按渠道类型、账户、对端用户、状态过滤
与分页用于管理员配对审批列表查询FR-33 ``pairing/list`` 操作)。
``channel_type`` 与 ``account_id`` 共同确定渠道账户作用域,仅提供其一
时按提供的条件过滤。
字段:
channel_type: 渠道类型(可选)。
account_id: 渠道账户业务 ID可选
peer_id: 对端用户 ID可选
status: 配对状态(可选)。
created_after: 申请时间下界(可选,含,按 ``requested_at`` 过滤)。
created_before: 申请时间上界(可选,含,按 ``requested_at`` 过滤)。
limit: 分页大小(默认 100
offset: 分页偏移(默认 0
"""
channel_type: ChannelType | None = None
account_id: str | None = None
peer_id: str | None = None
status: PairingStatus | None = None
created_after: datetime | None = None
created_before: datetime | None = None
limit: int = 100
offset: int = 0
@dataclass(frozen=True)
class BotLoopBudget:
"""机器人循环预算。
描述机器人回复的速率限制预算,包括每小时最大回复数、冷却时间、当前计数
与上次回复时间,用于防止机器人循环与速率限制。
字段:
max_replies_per_hour: 每小时最大回复数。
cooldown_seconds: 冷却时间(秒)。
current_count: 当前计数(默认 0
last_reply_at: 上次回复时间(可选)。
"""
max_replies_per_hour: int
cooldown_seconds: int
current_count: int = 0
last_reply_at: datetime | None = None
@dataclass(frozen=True)
class CreatePairingResult:
"""创建配对结果FR-33
描述创建配对请求操作的返回结果,包含新创建的配对记录信息。
字段:
pairing_id: 配对 ID。
channel_account_id: 渠道账户 ID。
peer_id: 对端 ID。
status: 配对状态(应为 PENDING
expires_at: 过期时间。
created_at: 创建时间。
"""
pairing_id: str
channel_account_id: str
peer_id: str
status: PairingStatus
expires_at: datetime
created_at: datetime
@dataclass(frozen=True)
class BatchPairingCmd:
"""批量配对命令PRG-BATCH-ACT
由 ``PairingManagementPort.batchApprovePairings`` /
``batchRejectPairings`` 引用,操作类型由调用的方法名隐式确定
``batchApprovePairings`` → approve``batchRejectPairings`` → reject
字段:
pairing_ids: 配对 ID 元组(必填)。
operator: 操作人(审计用)。
reason: 操作原因(可选,最长 512
"""
pairing_ids: tuple[str, ...]
operator: Operator
reason: str | None = None
def __post_init__(self) -> None:
"""校验业务规则PRG-BATCH-ACT
- ``pairing_ids`` 非空且长度 ≤ 500。
- ``reason`` 非空时长度 ≤ 512。
在构造时即抛出 ``ValidationError``,避免非法值传播到 dispatch
handler 后才暴露INV-8
"""
if not self.pairing_ids:
raise ValidationError("pairing_ids", "pairing_ids must not be empty")
if len(self.pairing_ids) > 500:
raise ValidationError(
"pairing_ids",
f"pairing_ids length must be <= 500, got {len(self.pairing_ids)}",
)
if self.reason is not None and len(self.reason) > 512:
raise ValidationError(
"reason",
f"reason length must be <= 512, got {len(self.reason)}",
)
@dataclass(frozen=True)
class CleanExpiredPairingsCmd:
"""清理过期配对命令PRG-CLEAN-EXPIRED
由 ``PairingManagementPort.cleanExpiredPairings`` 引用,单一事务批量
更新过期配对为 EXPIRED 状态。
字段:
operator: 操作人(审计用)。
channel_type: 渠道类型过滤(可选)。
older_than: 过期时间上界ISO 8601缺省取所有过期
max_count: 单次最大清理数(默认 5001-1000
"""
operator: Operator
channel_type: ChannelType | None = None
older_than: datetime | None = None
max_count: int = 500
def __post_init__(self) -> None:
"""校验业务规则PRG-CLEAN-EXPIRED
``max_count`` 必须在 ``[1, 1000]`` 区间,在构造时即抛出
``ValidationError``,避免非法值传播到 dispatch handler 后才暴露
INV-8
"""
if self.max_count < 1 or self.max_count > 1000:
raise ValidationError(
"max_count",
f"max_count must be between 1 and 1000, got {self.max_count}",
)
@dataclass(frozen=True)
class PairingStatsQuery:
"""配对统计查询PRG-STATS
描述配对统计的查询条件,由 ``PairingRepositoryPort.getPairingStats``
消费。``granularity`` 取值 ``hour`` / ``day`` / ``week``,默认 ``day``。
字段:
channel_type: 渠道类型过滤(可选)。
start_time: 起始时间(可选,含)。
end_time: 结束时间(可选,含)。
granularity: 时间粒度(默认 ``day``)。
"""
channel_type: ChannelType | None = None
start_time: datetime | None = None
end_time: datetime | None = None
granularity: str = "day"
def __post_init__(self) -> None:
"""校验业务规则PRG-STATS
``granularity`` 必须为 ``hour`` / ``day`` / ``week``,在构造时即
抛出 ``ValidationError``避免非法值传播到仓储层后才暴露INV-8
"""
if self.granularity not in ("hour", "day", "week"):
raise ValidationError(
"granularity",
f"granularity must be 'hour', 'day' or 'week', got {self.granularity!r}",
)
@dataclass(frozen=True)
class PairingTrendPoint:
"""配对趋势点PRG-STATS
描述按时间粒度切片的配对趋势数据点,含申请数与批准数。
字段:
timestamp: 时间桶起始时间。
requested: 申请数。
approved: 批准数。
"""
timestamp: datetime
requested: int
approved: int
@dataclass(frozen=True)
class PairingStatsResult:
"""配对统计结果PRG-STATS
描述配对审批的聚合统计指标,由 ``getPairingStats`` 返回。
字段:
total_requested: 申请总数。
approved_count: 批准数。
rejected_count: 拒绝数。
revoked_count: 撤销数。
expired_count: 过期数。
approve_rate: 批准率0-100
avg_approval_seconds: 平均审批时长(秒)。
trend: 配对趋势数据点元组。
"""
total_requested: int
approved_count: int
rejected_count: int
revoked_count: int
expired_count: int
approve_rate: float
avg_approval_seconds: float
trend: tuple[PairingTrendPoint, ...]