"""审计 DTO。 定义审计日志的不可变值对象,包括审计日志 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, Literal 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 AuditLogId: """审计日志 ID。 标识一条审计日志的全局唯一 ID,用于审计日志关联与查询。 字段: value: 审计日志 ID 字符串。 """ value: str class AuditOperationType(StrEnum): """审计操作类型。 标识审计日志记录的操作类型,覆盖插件生命周期、能力声明、白名单、配对、 管理员消息、DM 决策、配置变更、Outbox 状态、诊断导出、账户生命周期与 扫码登录等场景。继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: PLUGIN_DISCOVERED: 插件发现。 PLUGIN_LOADED: 插件加载。 PLUGIN_STARTED: 插件启动。 PLUGIN_STOPPED: 插件停止。 PLUGIN_UNLOADED: 插件卸载。 PLUGIN_FAILED: 插件失败。 PLUGIN_PAUSED: 插件暂停。 PLUGIN_RESUMED: 插件恢复。 PLUGIN_RELOADED: 插件重载(FR-32,unload + load 组合操作)。 PLUGIN_RECOVERED: 插件从失败降级中恢复(FR-36,对称于 PLUGIN_FAILED)。 CAPABILITY_DECLARED: 能力声明。 CAPABILITY_VERIFIED: 能力验证。 CAPABILITY_CHANGED: 能力变更。 WHITELIST_ADDED: 白名单添加。 WHITELIST_REMOVED: 白名单移除。 WHITELIST_CLEARED: 白名单清空。 PAIRING_CREATED: 配对创建。 PAIRING_APPROVED: 配对批准。 PAIRING_REJECTED: 配对拒绝。 PAIRING_REVOKED: 配对撤销。 PAIRING_EXPIRED: 配对过期。 ADMIN_MESSAGE_SENT: 管理员消息发送。 MESSAGE_RECALLED: 消息撤回(MSG-05)。 DM_DECISION_ALLOW: DM 决策允许。 DM_DECISION_DENY: DM 决策拒绝。 DM_DECISION_PENDING: DM 决策待定。 CONFIG_CHANGED: 配置变更。 OUTBOX_STATE_CHANGED: Outbox 状态变更。 DIAGNOSTICS_EXPORTED: 诊断导出。 CHANNEL_PROBED: 渠道主动探测(FR-35)。 SESSION_MERGED: 会话合并。 SESSION_OWNER_TRANSFERRED: 会话所有者转移(FR-26)。 ADMIN_QUERY: 管理查询(目录列表 / 审计查询 / 健康详情等只读操作)。 DOCTOR_CHECK_LIST: 诊断检查项列表查询(FR-17,只读)。 DOCTOR_RUN: 全量诊断执行(FR-17,只读)。 DOCTOR_CHECK_ITEM: 单项诊断执行(FR-17,只读)。 DOCTOR_REPAIR: 诊断自动修复执行(FR-17 / AC-40,写操作)。 DOCTOR_REPAIR_PREVIEWED: 诊断修复预览(FR-17 / AC-40,dry-run 只读)。 WIZARD_APPLY: 安装向导步骤应用(FR-16)。 WIZARD_FINALIZE: 安装向导完成(FR-16)。 WIZARD_OAUTH_CALLBACK: 安装向导 OAuth 回调(FR-16)。 ACCOUNT_CREATED: 账户创建(AL-01)。 ACCOUNT_UPDATED: 账户配置更新(AL-01)。 ACCOUNT_DELETED: 账户删除(AL-04)。 ACCOUNT_ENABLED: 账户启用,回调成功或无回调(AL-02)。 ACCOUNT_DISABLED: 账户禁用,回调成功或无回调(AL-02)。 ACCOUNT_ENABLED_WARN: 账户启用,回调失败(AL-02)。 ACCOUNT_DISABLED_WARN: 账户禁用,回调失败(AL-02)。 AFTER_CONFIG_WRITTEN_OK: 配置写入后回调成功(AL-03)。 AFTER_CONFIG_WRITTEN_WARN: 配置写入后回调失败(AL-03)。 ACCOUNT_DELETE_CLEANUP_OK: 删除前清理回调成功(AL-04)。 ACCOUNT_DELETE_CLEANUP_WARN: 删除前清理回调失败(AL-04)。 START_QR_LOGIN: 发起扫码登录(QR-06)。 QR_LOGIN_SUCCESS: 扫码登录成功(QR-06)。 QR_LOGIN_FAILED: 扫码登录失败(QR-06)。 QR_LOGIN_TIMEOUT: 扫码登录超时(QR-06)。 QR_LOGIN_CANCELLED: 扫码取消(QR-06)。 QR_REFRESH: 扫码刷新(QR-06)。 LOGOUT: 登出(QR-06)。 CONTENT_REVIEW_PREVIEWED: 内容预审核(CR-01,本期使用)。 CONTENT_REVIEW_BLOCKED: 内容审核命中 block(预留,本期不使用; 未来管道自动审核命中 block 时记录)。 REPORT_CREATED: 报告创建。 REPORT_DOWNLOADED: 报告下载。 AGENT_LIST: Agent 列表查询(多 Agent 协作,只读)。 AGENT_GET: Agent 详情查询(多 Agent 协作,只读)。 AGENT_HANDOFF: Agent 移交执行(多 Agent 协作,写操作)。 AGENT_CONTEXT: Agent 上下文查询(多 Agent 协作,只读)。 SESSION_CLOSED: 会话关闭。 OUTBOX_DEAD_LETTER_BATCH_RETRIED: Outbox 死信批量重投。 OUTBOX_DEAD_LETTER_BATCH_DELETED: Outbox 死信批量删除。 OUTBOX_RETRY_POLICY_UPDATED: Outbox 重试策略更新。 PAIRING_REQUEST_CREATED: 配对请求创建。 ACCOUNT_CREDENTIALS_ROTATED: 账户凭据轮换。 ACCOUNT_CONNECTION_TESTED: 账户连接测试。 AUDIT_EXPORTED: 审计日志导出(同步导出)。 """ PLUGIN_DISCOVERED = "plugin_discovered" PLUGIN_LOADED = "plugin_loaded" PLUGIN_STARTED = "plugin_started" PLUGIN_STOPPED = "plugin_stopped" PLUGIN_UNLOADED = "plugin_unloaded" PLUGIN_FAILED = "plugin_failed" PLUGIN_PAUSED = "plugin_paused" PLUGIN_RESUMED = "plugin_resumed" PLUGIN_RELOADED = "plugin_reloaded" PLUGIN_RECOVERED = "plugin_recovered" CAPABILITY_DECLARED = "capability_declared" CAPABILITY_VERIFIED = "capability_verified" CAPABILITY_CHANGED = "capability_changed" WHITELIST_ADDED = "whitelist_added" WHITELIST_REMOVED = "whitelist_removed" WHITELIST_CLEARED = "whitelist_cleared" PAIRING_CREATED = "pairing_created" PAIRING_APPROVED = "pairing_approved" PAIRING_REJECTED = "pairing_rejected" PAIRING_REVOKED = "pairing_revoked" PAIRING_EXPIRED = "pairing_expired" ADMIN_MESSAGE_SENT = "admin_message_sent" MESSAGE_RECALLED = "message_recalled" DM_DECISION_ALLOW = "dm_decision_allow" DM_DECISION_DENY = "dm_decision_deny" DM_DECISION_PENDING = "dm_decision_pending" CONFIG_CHANGED = "config_changed" OUTBOX_STATE_CHANGED = "outbox_state_changed" DIAGNOSTICS_EXPORTED = "diagnostics_exported" CHANNEL_PROBED = "channel_probed" SESSION_MERGED = "session_merged" SESSION_OWNER_TRANSFERRED = "session_owner_transferred" ADMIN_QUERY = "admin_query" DOCTOR_CHECK_LIST = "doctor_check_list" DOCTOR_RUN = "doctor_run" DOCTOR_CHECK_ITEM = "doctor_check_item" DOCTOR_REPAIR = "doctor_repair" DOCTOR_REPAIR_PREVIEWED = "doctor_repair_previewed" WIZARD_APPLY = "wizard_apply" WIZARD_FINALIZE = "wizard_finalize" WIZARD_OAUTH_CALLBACK = "wizard_oauth_callback" # 账户生命周期审计类型(AL-01~AL-04) ACCOUNT_CREATED = "account_created" ACCOUNT_UPDATED = "account_updated" ACCOUNT_DELETED = "account_deleted" ACCOUNT_ENABLED = "account_enabled" ACCOUNT_DISABLED = "account_disabled" ACCOUNT_ENABLED_WARN = "account_enabled_warn" ACCOUNT_DISABLED_WARN = "account_disabled_warn" AFTER_CONFIG_WRITTEN_OK = "after_config_written_ok" AFTER_CONFIG_WRITTEN_WARN = "after_config_written_warn" ACCOUNT_DELETE_CLEANUP_OK = "account_delete_cleanup_ok" ACCOUNT_DELETE_CLEANUP_WARN = "account_delete_cleanup_warn" # 扫码登录审计类型(QR-06) START_QR_LOGIN = "start_qr_login" QR_LOGIN_SUCCESS = "qr_login_success" QR_LOGIN_FAILED = "qr_login_failed" QR_LOGIN_TIMEOUT = "qr_login_timeout" QR_LOGIN_CANCELLED = "qr_login_cancelled" QR_REFRESH = "qr_refresh" LOGOUT = "logout" # 内容审核审计类型(CR-01 / 管道自动审核) CONTENT_REVIEW_PREVIEWED = "content_review_previewed" CONTENT_REVIEW_BLOCKED = "content_review_blocked" # 预留:管道自动审核命中 block 时记录 # 多 Agent 协作审计类型 AGENT_LIST = "agent_list" AGENT_GET = "agent_get" AGENT_HANDOFF = "agent_handoff" AGENT_CONTEXT = "agent_context" # 会话关闭审计类型 SESSION_CLOSED = "session_closed" # Outbox 死信批量操作审计类型 OUTBOX_DEAD_LETTER_BATCH_RETRIED = "outbox_dead_letter_batch_retried" OUTBOX_DEAD_LETTER_BATCH_DELETED = "outbox_dead_letter_batch_deleted" OUTBOX_RETRY_POLICY_UPDATED = "outbox_retry_policy_updated" # 配对请求创建审计类型 PAIRING_REQUEST_CREATED = "pairing_request_created" # 账户凭据轮换与连接测试审计类型 ACCOUNT_CREDENTIALS_ROTATED = "account_credentials_rotated" ACCOUNT_CONNECTION_TESTED = "account_connection_tested" # 强制下线审计类型(LGN-FORCE-LOGOUT) ACCOUNT_OFFLINE = "account_offline" # 目录缓存清理审计类型(DIR-CACHE-CLEAR) DIRECTORY_CACHE_CLEARED = "directory_cache_cleared" # 审计日志导出(同步导出,区别于 admin_query 查询) AUDIT_EXPORTED = "audit_exported" # ---- 配置导入(CFG-IMPORT)---- CONFIG_IMPORTED = "config_imported" # ---- 白名单批量/清理操作(ALW-BATCH-DEL / ALW-CLEAN-EXPIRED / whitelist update)---- WHITELIST_UPDATED = "whitelist_updated" WHITELIST_BATCH_ADDED = "whitelist_batch_added" WHITELIST_BATCH_REMOVED = "whitelist_batch_removed" WHITELIST_EXPIRED_CLEANED = "whitelist_expired_cleaned" # ---- 安装向导 OAuth 发起(FR-16)---- WIZARD_OAUTH_INITIATE = "wizard_oauth_initiate" # ---- 诊断检查项详情查询(FR-17)---- DOCTOR_CHECK_DETAIL = "doctor_check_detail" # ---- 账户恢复(AL-02 对称,DEGRADED → ACTIVE)---- ACCOUNT_RECOVERED = "account_recovered" ACCOUNT_RECOVERED_WARN = "account_recovered_warn" # ---- 插件安装/卸载/配置更新(PLG-INSTALL / PLG-UNINSTALL-FILE / PLG-CONFIG)---- PLUGIN_INSTALLED = "plugin_installed" PLUGIN_UNINSTALLED = "plugin_uninstalled" PLUGIN_CONFIG_UPDATED = "plugin_config_updated" # ---- Outbox 投递操作(OBX-01~OBX-08)---- OUTBOX_RETRY = "outbox_retry" OUTBOX_DEAD_LETTER_DELETED = "outbox_dead_letter_deleted" OUTBOX_BATCH_RETRY = "outbox_batch_retry" OUTBOX_BATCH_DELETE = "outbox_batch_delete" # ---- 内容审核决定(CR-DECISION-BATCH)---- CONTENT_REVIEW_DECIDED = "content_review_decided" # ---- 配对过期清理(PRG-CLEAN-EXPIRED)---- PAIRING_EXPIRED_CLEANED = "pairing_expired_cleaned" # ---- 账户导出(ACC-EXPORT)---- ACCOUNT_EXPORTED = "account_exported" # ---- 消息续投与附件上传(MSG-RESEND / MSG-ATTACH-UPLOAD)---- MESSAGE_RESENT = "message_resent" ATTACHMENT_UPLOADED = "attachment_uploaded" # ---- 审计保留策略更新(AUD-RETENTION-PUT)---- RETENTION_POLICY_UPDATED = "retention_policy_updated" # ---- Webhook 测试(WHK-TEST)---- WEBHOOK_TEST_SENT = "webhook_test_sent" # ---- 路由绑定操作(route_binding.*,audit_type 含点号与表保持一致)---- ROUTE_BINDING_LIST = "route_binding.list" ROUTE_BINDING_CREATE = "route_binding.create" ROUTE_BINDING_UPDATE = "route_binding.update" ROUTE_BINDING_DELETE = "route_binding.delete" ROUTE_BINDING_RESOLVE = "route_binding.resolve" # ---- 身份合并回滚与放行(C-13 / H-21)---- IDENTITY_ROLLBACK_MERGE = "identity_rollback_merge" IDENTITY_APPROVE_MERGE = "identity_approve_merge" # ---- 目录导出(DIR-EXPORT,Fix 3 配套)---- DIRECTORY_EXPORTED = "directory_exported" class AuditTxStrategy(StrEnum): """审计事务策略。 标识控制面操作产生副作用的可回滚性,决定 audit 阶段的事务边界。 继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: SHARED: 共享事务(fail-closed)。适用于纯 DB 写入或只读操作, 审计与业务写入原子提交,审计失败回滚业务(FR-34 fail-closed)。 INDEPENDENT: 独立事务(best-effort)。适用于已产生不可回滚外部 副作用的操作(IM 发送、Redis 写入、事件发布),审计使用独立 事务,失败时仅记录告警,不中止业务——避免"外部副作用已发生 但审计回滚导致无痕迹"(§10.1 事务边界)。 """ SHARED = "shared" INDEPENDENT = "independent" @dataclass(frozen=True) class AuditEntry: """审计条目。 描述一条审计日志的完整内容,包括操作人、操作类型、目标、参数摘要、结果、 时间戳与链路追踪信息,用于审计日志持久化与查询。 字段: operator: 操作人(用户 ID 或 "system")。 operation: 操作类型。 target: 操作目标。 result: 操作结果(success | failed)。 timestamp: 操作时间戳。 params_summary: 脱敏后的参数摘要(可选)。 trace_id: 链路追踪 ID(可选)。 source_ip: 来源 IP(可选,FR-34)。 request_id: 请求 ID(可选,链路追踪用,FR-34)。 message_id: 关联消息 ID(可选,FR-19)。 content_summary: 消息内容摘要(可选,FR-19)。 target_channel: 目标渠道类型(可选,FR-34 审计日志按渠道作用域)。 target_account: 目标账户 ID(可选,FR-34 按账户作用域查询)。 """ operator: str operation: AuditOperationType target: str result: Literal["success", "failed"] timestamp: datetime params_summary: dict[str, Any] | None = None trace_id: str | None = None source_ip: str | None = None request_id: str | None = None message_id: str | None = None content_summary: str | None = None target_channel: str | None = None target_account: str | None = None @dataclass(frozen=True) class AuditQuery: """审计查询(AUD-001)。 描述审计日志查询条件,支持按操作类型、操作人、目标渠道、目标账户、 时间范围与分页过滤。管理员审计日志 API 的作用域为"按渠道类型+账户" (PRD FR-34),target_channel 与 target_account 共同确定查询范围。 字段: operation_type: 操作类型(可选)。 operator: 操作人(可选)。 target_channel: 目标渠道(可选)。 target_account: 目标账户(可选,FR-34 按账户作用域查询)。 start_time: 起始时间(可选)。 end_time: 结束时间(可选)。 limit: 分页大小(默认 100)。 offset: 分页偏移(默认 0)。 trace_id: 链路追踪 ID(可选,按链路追踪 ID 过滤)。 """ operation_type: AuditOperationType | None = None operator: str | None = None target_channel: ChannelType | None = None target_account: str | None = None start_time: datetime | None = None end_time: datetime | None = None limit: int = 100 offset: int = 0 trace_id: str | None = None def __post_init__(self) -> None: """校验时间范围与分页参数。 ``start_time`` 与 ``end_time`` 同时提供时,``start_time`` 必须早于 ``end_time``;``limit`` 必须为正整数,``offset`` 必须为非负整数。 在构造时即抛出 ``ValidationError``,adapter 不再做该校验(INV-8)。 """ if self.start_time is not None and self.end_time is not None: if self.start_time >= self.end_time: raise ValidationError( "time_range", "start_time must be earlier than end_time", ) if self.limit <= 0: raise ValidationError("limit", "must be a positive integer") if self.offset < 0: raise ValidationError("offset", "must be a non-negative integer") @dataclass(frozen=True) class AuditLogStats: """审计日志统计聚合结果。 描述审计日志按操作类型、操作结果分组的计数与时间范围,由 ``getAuditLogStats`` 返回,供管理后台展示审计日志分布与合规报表。 字段: total: 满足过滤条件的审计日志总数。 by_operation_type: 按操作类型分组的计数(key 为 operation_type 字符串)。 by_result: 按操作结果分组的计数(key 为 "success" / "failed")。 time_range_start: 匹配记录的最早时间戳(无数据时为 None)。 time_range_end: 匹配记录的最晚时间戳(无数据时为 None)。 """ total: int by_operation_type: dict[str, int] by_result: dict[str, int] time_range_start: datetime | None time_range_end: datetime | None @dataclass(frozen=True) class RetentionPolicy: """审计保留策略(AUD-RETENTION)。 描述审计日志的保留策略配置,包括默认保留天数、按操作类型的保留天数、 自动归档开关与归档提前天数,由 ConfigManager 管理 ``audit_retention_policy`` 配置键(GLOBAL 作用域)。 字段: default_retention_days: 默认保留天数。 by_operation_type: 按操作类型分组的保留天数(key 为 AuditOperationType 字符串值)。 auto_archive_enabled: 是否启用自动归档。 auto_archive_before_days: 归档提前天数(到期前 N 天归档)。 updated_at: 策略最后更新时间(可选,未更新时为 None)。 """ default_retention_days: int by_operation_type: dict[str, int] auto_archive_enabled: bool auto_archive_before_days: int updated_at: datetime | None = None @dataclass(frozen=True) class RetentionPolicyUpdateCmd: """审计保留策略更新命令(AUD-RETENTION-PUT)。 由 ``AuditQueryPort.updateRetentionPolicy`` 引用,更新审计日志保留策略, 需记录操作人以满足审计要求(PUT 需超级管理员)。 字段: operator: 操作人(审计用)。 default_retention_days: 默认保留天数(默认 90)。 by_operation_type: 按操作类型分组的保留天数(可选)。 auto_archive_enabled: 是否启用自动归档(默认 True)。 auto_archive_before_days: 归档提前天数(默认 80)。 """ operator: Operator default_retention_days: int = 90 by_operation_type: dict[str, Any] | None = None auto_archive_enabled: bool = True auto_archive_before_days: int = 80 def __post_init__(self) -> None: """校验业务规则(INV-8 / 端口 @pre)。 - ``default_retention_days`` 与 ``auto_archive_before_days`` 必须为正整数。 - ``auto_archive_before_days`` 必须小于 ``default_retention_days``。 - ``by_operation_type``(如提供)的 key 必须为合法的 ``AuditOperationType`` 字符串值,value 必须为正整数。 在构造时即抛出 ``ValidationError``,避免非法值传播到 dispatch 阶段。 """ if self.default_retention_days <= 0: raise ValidationError("default_retention_days", "must be positive") if self.auto_archive_before_days <= 0: raise ValidationError("auto_archive_before_days", "must be positive") if self.auto_archive_before_days >= self.default_retention_days: raise ValidationError( "auto_archive_before_days", "must be less than default_retention_days", ) if self.by_operation_type is not None: valid_keys = {e.value for e in AuditOperationType} for k, v in self.by_operation_type.items(): if k not in valid_keys: raise ValidationError( "by_operation_type", f"invalid operation_type key: {k}", ) if not isinstance(v, int) or isinstance(v, bool) or v <= 0: raise ValidationError( "by_operation_type", f"value for '{k}' must be positive int, got {v!r}", ) __all__ = [ "AuditEntry", "AuditLogId", "AuditLogStats", "AuditOperationType", "AuditQuery", "AuditTxStrategy", "RetentionPolicy", "RetentionPolicyUpdateCmd", ]