Files
RuoYi-Vue3-FastAPI/ruoyi-fastapi-backend/plugins/core/capability.py
T
insistence 025b0822df refactor: 统一多前端工程架构、插件配置与 CI (#131)
* refactor: 重构项目架构

* refactor: 重构ci和测试

* fix: 修复测试

* feat: 新增更多E2E测试

* fix: 修复E2E测试
2026-10-10 21:15:22 +08:00

159 lines
5.2 KiB
Python

from dataclasses import dataclass
from typing import Literal
from plugins.core.discovery.scanner import DiscoveredPlugin, is_artifact_plugin
PluginOperation = Literal[
'install',
'uninstall',
'enable',
'disable',
'upgrade',
'purge',
'dependency_install',
'batch_install',
'batch_enable',
'batch_upgrade',
]
STATE_CHANGE_OPERATIONS: set[str] = {
'install',
'uninstall',
'enable',
'disable',
'upgrade',
'purge',
'dependency_install',
'batch_install',
'batch_enable',
'batch_upgrade',
}
SERVICE_MODE_REASON = (
'当前为服务运行模式,插件路由、任务和生命周期资源需要重启后激活。请在开发模式或维护窗口中执行插件变更。'
)
BUILT_FRONTEND_REASON = '当前为已构建前端环境,该插件包含前端源码资源,需要在构建前安装依赖并重新构建前端后生效。'
@dataclass(frozen=True)
class PluginRuntimeCapability:
"""
插件运行时操作能力。
"""
plugin_id: str
frontend_mode: str
backend_runtime_mode: str
has_frontend_resources: bool
frontend_build_required: bool
frontend_runtime_manageable: bool
backend_runtime_manageable: bool
runtime_manageable: bool
blocked_operations: list[str]
warnings: list[str]
@property
def primary_reason(self) -> str:
"""
获取首个阻断原因。
:return: 阻断原因
"""
return self.warnings[0] if self.warnings else ''
def allows(self, operation: str) -> bool:
"""
判断指定操作是否允许。
:param operation: 操作类型
:return: 是否允许
"""
return operation not in self.blocked_operations
def to_payload(self) -> dict[str, object]:
"""
构建前后端通用能力负载。
:return: 能力负载
"""
return {
'pluginId': self.plugin_id,
'frontendMode': self.frontend_mode,
'backendRuntimeMode': self.backend_runtime_mode,
'hasFrontendResources': self.has_frontend_resources,
'frontendBuildRequired': self.frontend_build_required,
'frontendRuntimeManageable': self.frontend_runtime_manageable,
'backendRuntimeManageable': self.backend_runtime_manageable,
'runtimeManageable': self.runtime_manageable,
'blockedOperations': self.blocked_operations,
'warnings': self.warnings,
'primaryReason': self.primary_reason,
}
class PluginRuntimeCapabilityResolver:
"""
插件运行时能力解析器。
"""
def __init__(self, *, frontend_mode: str, backend_runtime_mode: str) -> None:
"""
初始化能力解析器。
:param frontend_mode: 前端运行模式
:param backend_runtime_mode: 后端运行模式
"""
self.frontend_mode = frontend_mode
self.backend_runtime_mode = backend_runtime_mode
def resolve(self, discovered_plugin: DiscoveredPlugin) -> PluginRuntimeCapability:
"""
解析单个插件运行时能力。
:param discovered_plugin: 已发现插件
:return: 插件运行时能力
"""
manifest = discovered_plugin.manifest
has_frontend_resources = bool(
manifest.frontend.menus
or manifest.dependencies.has_frontend_dependencies
or manifest.frontend.delivery.type == 'bundle'
)
frontend_build_required = manifest.frontend.delivery.type != 'bundle' and (
manifest.frontend.delivery.build_required or has_frontend_resources
)
frontend_runtime_manageable = not (
self.frontend_mode == 'built' and has_frontend_resources and frontend_build_required
)
backend_runtime_manageable = self.backend_runtime_mode in {'dev', 'maintenance'}
if is_artifact_plugin(discovered_plugin) and self.backend_runtime_mode != 'maintenance':
backend_runtime_manageable = False
runtime_manageable = frontend_runtime_manageable and backend_runtime_manageable
warnings = []
if not backend_runtime_manageable:
warnings.append(
'签名制品的生命周期只能通过插件维护发布命令执行,完成后重启 worker。'
if is_artifact_plugin(discovered_plugin)
else SERVICE_MODE_REASON
)
if not frontend_runtime_manageable:
warnings.append(BUILT_FRONTEND_REASON)
blocked_operations: list[str] = []
if not runtime_manageable:
blocked_operations = sorted(STATE_CHANGE_OPERATIONS)
return PluginRuntimeCapability(
plugin_id=manifest.id,
frontend_mode=self.frontend_mode,
backend_runtime_mode=self.backend_runtime_mode,
has_frontend_resources=has_frontend_resources,
frontend_build_required=frontend_build_required,
frontend_runtime_manageable=frontend_runtime_manageable,
backend_runtime_manageable=backend_runtime_manageable,
runtime_manageable=runtime_manageable,
blocked_operations=blocked_operations,
warnings=warnings,
)