2026-07-02 03:22:12 +08:00
|
|
|
|
"""插件 DTO。
|
|
|
|
|
|
|
|
|
|
|
|
定义插件相关的枚举、不可变值对象与 Protocol,包括权限枚举、环境变量、
|
|
|
|
|
|
领域事件、事件处理 Protocol、控制 RPC 处理 Protocol、配置加载 Protocol
|
|
|
|
|
|
与配置解密 Protocol。所有枚举继承 ``str, Enum`` 以支持 JSON 序列化,
|
|
|
|
|
|
DTO 均为 ``dataclass(frozen=True)``,Protocol 使用 ``@runtime_checkable``
|
|
|
|
|
|
装饰以支持 ``isinstance`` 检查。仅依赖标准库,用于插件生命周期、事件
|
|
|
|
|
|
分发、控制 RPC 与配置加载解密。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
from enum import StrEnum
|
2026-07-03 19:18:13 +08:00
|
|
|
|
from typing import TYPE_CHECKING, Any, Literal, Protocol, runtime_checkable
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
from yuxi.channels.contract.dtos.common import BatchOperationFailure, Operator
|
2026-07-03 19:18:13 +08:00
|
|
|
|
from yuxi.channels.contract.errors import ValidationError
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
2026-07-02 18:27:06 +08:00
|
|
|
|
from yuxi.channels.contract.dtos.capability import ChannelCapabilities
|
2026-07-02 03:22:12 +08:00
|
|
|
|
from yuxi.channels.contract.plugin.manifest import PluginManifest
|
|
|
|
|
|
|
2026-07-03 19:18:13 +08:00
|
|
|
|
# 插件生命周期状态字面量类型(对齐 LifecycleState 枚举值)。
|
|
|
|
|
|
# 供 PluginSummary / PluginCatalogItem / PluginDetail 的 state 字段与
|
|
|
|
|
|
# Router 层 state 查询参数共用,避免 11 个取值在多处重复声明。
|
|
|
|
|
|
PluginStateLiteral = Literal[
|
|
|
|
|
|
"discovered",
|
|
|
|
|
|
"resolved",
|
|
|
|
|
|
"loaded",
|
|
|
|
|
|
"initialized",
|
|
|
|
|
|
"started",
|
|
|
|
|
|
"paused",
|
|
|
|
|
|
"stopped",
|
|
|
|
|
|
"unloaded",
|
|
|
|
|
|
"failed",
|
|
|
|
|
|
"not_installed",
|
|
|
|
|
|
"installed",
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
class Permission(StrEnum):
|
|
|
|
|
|
"""权限等级。
|
|
|
|
|
|
|
|
|
|
|
|
标识插件或操作所需的用户权限等级,用于权限校验与审计。继承
|
|
|
|
|
|
``str, Enum`` 以支持 JSON 序列化与字符串比较。
|
|
|
|
|
|
|
|
|
|
|
|
取值:
|
|
|
|
|
|
REQUIRED_USER: 普通需求用户。
|
|
|
|
|
|
ADMIN_USER: 管理员用户。
|
|
|
|
|
|
SUPERADMIN_USER: 超级管理员用户。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
REQUIRED_USER = "required_user"
|
|
|
|
|
|
ADMIN_USER = "admin_user"
|
|
|
|
|
|
SUPERADMIN_USER = "superadmin_user"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class EnvVar:
|
|
|
|
|
|
"""环境变量声明。
|
|
|
|
|
|
|
|
|
|
|
|
描述插件所需的环境变量元信息,包括名称、描述、是否必填、默认值与
|
|
|
|
|
|
是否敏感,用于插件安装向导的环境变量校验与脱敏渲染。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
name: 环境变量名称。
|
|
|
|
|
|
description: 环境变量描述。
|
|
|
|
|
|
required: 是否必填(默认 True)。
|
|
|
|
|
|
default: 默认值(可选)。
|
|
|
|
|
|
sensitive: 是否敏感(需脱敏,默认 False)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
name: str
|
|
|
|
|
|
description: str
|
|
|
|
|
|
required: bool = True
|
|
|
|
|
|
default: str | None = None
|
|
|
|
|
|
sensitive: bool = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class DomainEvent:
|
|
|
|
|
|
"""领域事件。
|
|
|
|
|
|
|
|
|
|
|
|
描述插件生命周期或业务流程中产生的领域事件,包括事件 ID、事件类型、
|
|
|
|
|
|
负载、时间戳与链路追踪 ID,用于事件总线分发与审计。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
event_id: 事件 ID。
|
|
|
|
|
|
event_type: 事件类型(PluginLoaded | PairingApproved | ConfigChanged | ...)。
|
|
|
|
|
|
payload: 事件负载。
|
|
|
|
|
|
timestamp: 事件时间戳。
|
|
|
|
|
|
trace_id: 链路追踪 ID(可选)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
event_id: str
|
|
|
|
|
|
event_type: str
|
|
|
|
|
|
payload: dict[str, Any]
|
|
|
|
|
|
timestamp: datetime
|
|
|
|
|
|
trace_id: str | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@runtime_checkable
|
|
|
|
|
|
class EventHandler(Protocol):
|
|
|
|
|
|
"""事件处理 Protocol。
|
|
|
|
|
|
|
|
|
|
|
|
由插件实现,用于订阅并处理领域事件。使用 ``@runtime_checkable``
|
|
|
|
|
|
装饰以支持 ``isinstance`` 检查。事件处理为异步方法,避免阻塞
|
|
|
|
|
|
事件总线分发线程。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
async def handle(self, event: DomainEvent) -> None:
|
|
|
|
|
|
"""处理领域事件。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
event: 领域事件。
|
|
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@runtime_checkable
|
|
|
|
|
|
class ConfigLoader(Protocol):
|
|
|
|
|
|
"""配置加载 Protocol。
|
|
|
|
|
|
|
|
|
|
|
|
由插件实现,用于加载插件配置并返回配置字典。使用
|
|
|
|
|
|
``@runtime_checkable`` 装饰以支持 ``isinstance`` 检查。配置加载
|
|
|
|
|
|
为同步方法,适用于启动期一次性加载场景。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def load(self) -> dict[str, Any]:
|
|
|
|
|
|
"""加载插件配置。
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
配置字典。
|
|
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@runtime_checkable
|
|
|
|
|
|
class ConfigDecryptor(Protocol):
|
|
|
|
|
|
"""配置解密 Protocol。
|
|
|
|
|
|
|
|
|
|
|
|
由插件实现,用于解密敏感配置值。使用 ``@runtime_checkable`` 装饰
|
|
|
|
|
|
以支持 ``isinstance`` 检查。解密为同步方法,适用于配置加载后
|
|
|
|
|
|
的就地解密场景。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def decrypt(self, value: str) -> str:
|
|
|
|
|
|
"""解密配置值。
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
value: 待解密的配置值。
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
解密后的明文配置值。
|
|
|
|
|
|
"""
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class PluginSummary:
|
|
|
|
|
|
"""插件摘要(PLG-QUERY-01 列表项)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件唯一标识(manifest.manifest.id)。
|
|
|
|
|
|
name: 插件显示名称(manifest.manifest.name)。
|
|
|
|
|
|
version: 插件版本(manifest.manifest.version)。
|
2026-07-04 00:14:56 +08:00
|
|
|
|
channel_type: 绑定渠道类型(manifest.manifest.channel_type)。
|
2026-07-02 03:22:12 +08:00
|
|
|
|
state: 当前生命周期状态(LifecycleState.value)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
version: str
|
|
|
|
|
|
channel_type: str
|
2026-07-03 19:18:13 +08:00
|
|
|
|
state: PluginStateLiteral
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-07-02 18:27:06 +08:00
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class PluginCatalogItem:
|
|
|
|
|
|
"""插件目录项(PLG-CATALOG 卡片视图数据)。
|
|
|
|
|
|
|
2026-07-06 20:49:35 +08:00
|
|
|
|
与 ``PluginSummary`` 的区别在于携带 ``capabilities`` 与 ``icon`` 字段,
|
|
|
|
|
|
供前端卡片网格展示能力摘要与渠道图标。``capabilities`` 为
|
|
|
|
|
|
``ChannelCapabilities`` dataclass,由 Router 层
|
|
|
|
|
|
``serialize_control_data`` 递归序列化。
|
2026-07-02 18:27:06 +08:00
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件唯一标识。
|
|
|
|
|
|
name: 插件显示名称。
|
|
|
|
|
|
version: 插件版本。
|
|
|
|
|
|
channel_type: 绑定渠道类型。
|
|
|
|
|
|
state: 当前生命周期状态。
|
|
|
|
|
|
capabilities: 渠道能力集合(静态声明)。
|
2026-07-06 20:49:35 +08:00
|
|
|
|
icon: 渠道展示图标名(lucide 图标名,缺失时前端回退默认图标)。
|
|
|
|
|
|
来源为 ``ChannelManifest.icon``,供前端卡片/筛选/标签等场景
|
|
|
|
|
|
统一渲染渠道图标,避免在前端硬编码渠道清单。
|
2026-07-02 18:27:06 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
version: str
|
|
|
|
|
|
channel_type: str
|
2026-07-03 19:18:13 +08:00
|
|
|
|
state: PluginStateLiteral
|
2026-07-02 18:27:06 +08:00
|
|
|
|
capabilities: ChannelCapabilities
|
2026-07-06 20:49:35 +08:00
|
|
|
|
icon: str | None = None
|
2026-07-02 18:27:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-07-02 03:22:12 +08:00
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class PluginDetail:
|
|
|
|
|
|
"""插件详情(PLG-QUERY-02 响应)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件唯一标识。
|
|
|
|
|
|
name: 插件显示名称。
|
|
|
|
|
|
version: 插件版本。
|
|
|
|
|
|
channel_type: 绑定渠道类型。
|
|
|
|
|
|
state: 当前生命周期状态。
|
|
|
|
|
|
manifest: 完整 PluginManifest(含内嵌 ChannelManifest 与
|
|
|
|
|
|
adapters / stages / event_subscriptions / config_sources)。
|
|
|
|
|
|
ChannelManifest 含 capabilities / provides / depends /
|
|
|
|
|
|
config_schema / failure_policy / resource_quota 等字段。
|
2026-07-06 20:49:35 +08:00
|
|
|
|
last_error: 最近一次生命周期失败错误信息(可选),供详情页排障展示。
|
2026-07-02 03:22:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
name: str
|
|
|
|
|
|
version: str
|
|
|
|
|
|
channel_type: str
|
2026-07-03 19:18:13 +08:00
|
|
|
|
state: PluginStateLiteral
|
2026-07-02 03:22:12 +08:00
|
|
|
|
manifest: PluginManifest
|
2026-07-06 20:49:35 +08:00
|
|
|
|
last_error: str | None = None
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class InstallPluginCmd:
|
|
|
|
|
|
"""安装插件命令(PLG-INSTALL)。
|
|
|
|
|
|
|
|
|
|
|
|
由 ``PluginManagementPort.installPlugin`` 引用,从来源安装插件,需记录
|
|
|
|
|
|
操作人以满足审计要求(超级管理员)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
operator: 操作人(审计用)。
|
|
|
|
|
|
source_type: 来源类型(``path`` / ``url`` / ``registry``)。
|
|
|
|
|
|
source: 来源路径或 URL。
|
|
|
|
|
|
version: 指定版本(可选)。
|
|
|
|
|
|
force: 是否强制覆盖已存在插件(默认 False)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
operator: Operator
|
2026-07-03 19:18:13 +08:00
|
|
|
|
source_type: Literal["path", "url", "registry"]
|
2026-07-02 03:22:12 +08:00
|
|
|
|
source: str
|
|
|
|
|
|
version: str | None = None
|
|
|
|
|
|
force: bool = False
|
|
|
|
|
|
|
2026-07-03 19:18:13 +08:00
|
|
|
|
def __post_init__(self) -> None:
|
|
|
|
|
|
"""校验必填字段非空与 source_type 取值(PLG-INSTALL)。
|
|
|
|
|
|
|
|
|
|
|
|
``source`` 必须非空,``source_type`` 必须为 ``path`` / ``url`` /
|
|
|
|
|
|
``registry`` 之一,在构造时即抛出 ``ValidationError``,adapter 不再做
|
|
|
|
|
|
该校验(INV-8)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not self.source:
|
|
|
|
|
|
raise ValidationError("source", "must not be empty")
|
|
|
|
|
|
if self.source_type not in ("path", "url", "registry"):
|
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
|
"source_type",
|
|
|
|
|
|
"must be one of: path, url, registry",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class InstallPluginResult:
|
|
|
|
|
|
"""安装插件结果(PLG-INSTALL)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件 ID。
|
|
|
|
|
|
version: 安装的插件版本。
|
|
|
|
|
|
installed_at: 安装时间戳。
|
|
|
|
|
|
requires_load: 是否需要手动加载(默认 True)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
version: str
|
|
|
|
|
|
installed_at: datetime
|
|
|
|
|
|
requires_load: bool = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class UninstallPluginResult:
|
|
|
|
|
|
"""卸载插件结果(PLG-UNINSTALL-FILE)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件 ID。
|
|
|
|
|
|
uninstalled_at: 卸载时间戳。
|
|
|
|
|
|
files_removed: 已删除的文件列表。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
uninstalled_at: datetime
|
|
|
|
|
|
files_removed: tuple[str, ...]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class PluginConfigResult:
|
|
|
|
|
|
"""插件配置查询结果(PLG-CONFIG)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件 ID。
|
|
|
|
|
|
config: 当前配置值。
|
|
|
|
|
|
schema: 配置 schema(从 PluginManifest 获取)。
|
|
|
|
|
|
requires_restart: 是否需要重启生效。
|
|
|
|
|
|
updated_at: 配置最后更新时间(可选)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
config: dict[str, Any]
|
|
|
|
|
|
schema: dict[str, Any]
|
|
|
|
|
|
requires_restart: bool
|
|
|
|
|
|
updated_at: datetime | None = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class UpdatePluginConfigCmd:
|
|
|
|
|
|
"""更新插件配置命令(PLG-CONFIG-PUT)。
|
|
|
|
|
|
|
|
|
|
|
|
由 ``PluginManagementPort.updatePluginConfig`` 引用,按 ``apply_mode``
|
|
|
|
|
|
判断是否需要重启,需记录操作人以满足审计要求。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件 ID。
|
|
|
|
|
|
operator: 操作人(审计用)。
|
|
|
|
|
|
config: 配置值。
|
|
|
|
|
|
apply_mode: 应用模式(``hot`` / ``restart_required``)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
operator: Operator
|
|
|
|
|
|
config: dict[str, Any]
|
2026-07-03 19:18:13 +08:00
|
|
|
|
apply_mode: Literal["hot", "restart_required"]
|
|
|
|
|
|
|
|
|
|
|
|
def __post_init__(self) -> None:
|
|
|
|
|
|
"""校验必填字段非空与 apply_mode 取值(PLG-CONFIG-PUT)。
|
|
|
|
|
|
|
|
|
|
|
|
``plugin_id`` 必须非空,``config`` 必须非空字典,``apply_mode``
|
|
|
|
|
|
必须为 ``hot`` / ``restart_required`` 之一,在构造时即抛出
|
|
|
|
|
|
``ValidationError``,adapter 不再做该校验(INV-8)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not self.plugin_id:
|
|
|
|
|
|
raise ValidationError("plugin_id", "must not be empty")
|
|
|
|
|
|
if not self.config:
|
|
|
|
|
|
raise ValidationError("config", "must not be empty")
|
|
|
|
|
|
if self.apply_mode not in ("hot", "restart_required"):
|
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
|
"apply_mode",
|
|
|
|
|
|
"must be one of: hot, restart_required",
|
|
|
|
|
|
)
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class BatchPluginLifecycleSuccessItem:
|
|
|
|
|
|
"""批量插件生命周期成功条目(PLG-BATCH-LIFE)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
plugin_id: 插件 ID。
|
|
|
|
|
|
new_state: 新生命周期状态。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
plugin_id: str
|
|
|
|
|
|
new_state: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class BatchPluginLifecycleCmd:
|
|
|
|
|
|
"""批量插件生命周期命令(PLG-BATCH-LIFE)。
|
|
|
|
|
|
|
|
|
|
|
|
逐条独立事务执行批量启动/停止(模式 D),``plugin_ids`` 与 ``filter``
|
2026-07-03 19:18:13 +08:00
|
|
|
|
二选一:不可同时为空、不可同时提供(由 ``__post_init__`` 校验)。
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
operator: 操作人(审计用)。
|
|
|
|
|
|
plugin_ids: 显式插件 ID 元组(默认空元组)。
|
|
|
|
|
|
filter: 筛选条件(含 channel_type / state,可选)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
operator: Operator
|
|
|
|
|
|
plugin_ids: tuple[str, ...] = ()
|
|
|
|
|
|
filter: dict[str, Any] | None = None
|
|
|
|
|
|
|
2026-07-03 19:18:13 +08:00
|
|
|
|
def __post_init__(self) -> None:
|
|
|
|
|
|
"""校验 plugin_ids 与 filter 互斥性与非空(PLG-BATCH-LIFE)。
|
|
|
|
|
|
|
|
|
|
|
|
``plugin_ids`` 与 ``filter`` 二选一:不可同时为空、不可同时提供,
|
|
|
|
|
|
``plugin_ids`` 各项不得为空字符串。在构造时即抛出 ``ValidationError``,
|
|
|
|
|
|
使非法输入在 Router 层即被拒绝(INV-8)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
has_ids = bool(self.plugin_ids)
|
|
|
|
|
|
has_filter = self.filter is not None
|
|
|
|
|
|
if not has_ids and not has_filter:
|
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
|
"plugin_ids",
|
|
|
|
|
|
"either plugin_ids or filter must be provided",
|
|
|
|
|
|
)
|
|
|
|
|
|
if has_ids and has_filter:
|
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
|
"plugin_ids",
|
|
|
|
|
|
"plugin_ids and filter are mutually exclusive",
|
|
|
|
|
|
)
|
|
|
|
|
|
if has_ids and not all(pid for pid in self.plugin_ids):
|
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
|
"plugin_ids",
|
|
|
|
|
|
"plugin_ids must not contain empty strings",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class BatchPluginLifecycleResult:
|
|
|
|
|
|
"""批量插件生命周期结果(PLG-BATCH-LIFE)。
|
|
|
|
|
|
|
|
|
|
|
|
描述逐条独立事务执行批量启动/停止的执行结果,``failed`` 使用通用
|
|
|
|
|
|
``BatchOperationFailure``(``id`` 字段承载 plugin_id)。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
total: 待操作插件总数。
|
|
|
|
|
|
succeeded: 成功条目元组。
|
|
|
|
|
|
failed: 失败条目元组。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
total: int
|
|
|
|
|
|
succeeded: tuple[BatchPluginLifecycleSuccessItem, ...]
|
|
|
|
|
|
failed: tuple[BatchOperationFailure, ...]
|