Files
RuoYi-Vue3-FastAPI/ruoyi-fastapi-backend/plugins/core/capability.py
T
insistence 2a055ba648 feat: 新增插件系统 (#112)
* feat: 初始化插件系统

* refactor: 收口插件系统运行时重构

* perf: 优化插件系统类型提示

* fix&perf: 修复和优化插件系统

* fix: 修复gitignore规则误忽略插件文件的问题

* fix: 修复运行时插件根路径算错的问题

* fix: 加强插件发现和路由注册的防护措施

* revert: 回滚定时任务白名单

* fix: 移除未使用的应用路由注册探测

* revert: 恢复部分代码

* perf: 优化插件系统

* docs: 新增插件开发文档

* perf: 优化插件管理模块

* perf: 提升插件系统核心能力

* refactor: 重构生命周期 step runner

* fix: 修复lint错误

* test: 清理测试用例

* test: 调整测试目录名称

* fix: 修复前后端目录硬编码的问题

* fix: 修复插件系统安全性缺口

* refactor: 重新设计插件生命周期 Migration 事务与回滚

* perf: 优化插件系统边界问题

* refactor: 重构当前插件系统的依赖体系设计

* perf: 优化代码

* perf: 优化代码

* fix: 修复代码合并问题

* fix: 修复bug

* perf: 优化代码

* perf&fix: 优化代码和修复bug

* docs: 优化文档格式

* feat: 适配Vue2版本

* docs: 更新README文档

* fix: 修复ruff lint错误

* chore: 更新后端依赖文件
2026-07-28 20:35:18 +08:00

149 lines
4.7 KiB
Python

from dataclasses import dataclass
from typing import Literal
from plugins.core.discovery.scanner import DiscoveredPlugin
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.npm or manifest.dependencies.npm_dev
)
frontend_build_required = 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 == 'dev'
runtime_manageable = frontend_runtime_manageable and backend_runtime_manageable
warnings = []
if not backend_runtime_manageable:
warnings.append(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,
)