mirror of
https://github.com/insistence/RuoYi-Vue3-FastAPI.git
synced 2026-10-04 01:10:37 +00:00
* 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: 更新后端依赖文件
212 lines
7.5 KiB
Python
212 lines
7.5 KiB
Python
from dataclasses import dataclass
|
|
from typing import Literal
|
|
|
|
from plugins.core.validation.versioning import PluginVersionComparator
|
|
|
|
PluginStatus = Literal['discovered', 'installed', 'pending_upgrade', 'error']
|
|
PluginStateOperation = Literal[
|
|
'discover',
|
|
'install',
|
|
'disable',
|
|
'enable',
|
|
'upgrade_available',
|
|
'upgrade',
|
|
'mark_error',
|
|
]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PluginStateTransition:
|
|
"""
|
|
插件状态流转规则。
|
|
|
|
:param source: 来源状态,None 表示数据库中尚无插件状态
|
|
:param operation: 状态流转操作
|
|
:param target: 目标状态
|
|
:param description: 流转说明
|
|
"""
|
|
|
|
source: PluginStatus | None
|
|
operation: PluginStateOperation
|
|
target: PluginStatus
|
|
description: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PluginStateSnapshot:
|
|
"""
|
|
插件状态输入快照。
|
|
|
|
:param source_version: 当前源码版本
|
|
:param installed_version: 已安装版本
|
|
:param enabled: 是否启用
|
|
:param current_status: 当前持久化状态
|
|
"""
|
|
|
|
source_version: str | None
|
|
installed_version: str | None
|
|
enabled: bool
|
|
current_status: str | None = None
|
|
|
|
|
|
class PluginStateResolver:
|
|
"""
|
|
插件状态解析器。
|
|
|
|
使用 State Resolver 模式集中维护插件管理和运行时注册表共享的状态流转规则。
|
|
"""
|
|
|
|
@classmethod
|
|
def resolve(cls, snapshot: PluginStateSnapshot) -> PluginStatus:
|
|
"""
|
|
根据源码版本、安装版本、启停状态和当前状态解析插件状态。
|
|
|
|
:param snapshot: 插件状态输入快照
|
|
:return: 插件状态
|
|
"""
|
|
if snapshot.current_status == 'error':
|
|
return 'error'
|
|
if not snapshot.installed_version and snapshot.current_status in {None, 'discovered'}:
|
|
return 'discovered'
|
|
if cls._needs_upgrade(snapshot.installed_version, snapshot.source_version):
|
|
return 'pending_upgrade'
|
|
return 'installed' if snapshot.installed_version else 'discovered'
|
|
|
|
@staticmethod
|
|
def is_enabled(database_plugin: object | None) -> bool:
|
|
"""
|
|
解析插件启用状态。
|
|
|
|
:param database_plugin: 数据库插件状态对象
|
|
:return: 是否启用
|
|
"""
|
|
if not database_plugin:
|
|
return False
|
|
if getattr(database_plugin, 'status', None) == 'error':
|
|
return False
|
|
if not getattr(database_plugin, 'installed_version', None):
|
|
return False
|
|
database_enabled = getattr(database_plugin, 'enabled', None) if database_plugin else None
|
|
if database_enabled is not None:
|
|
return database_enabled == '0'
|
|
return False
|
|
|
|
@staticmethod
|
|
def enabled_to_db_value(enabled: bool) -> str:
|
|
"""
|
|
将布尔启停状态转换为数据库枚举值。
|
|
|
|
:param enabled: 是否启用
|
|
:return: 数据库启停枚举值,`0` 表示启用,`1` 表示停用
|
|
"""
|
|
return '0' if enabled else '1'
|
|
|
|
@staticmethod
|
|
def db_value_to_enabled(enabled: str | None, fallback: bool) -> bool:
|
|
"""
|
|
将数据库启停枚举值转换为布尔值。
|
|
|
|
:param enabled: 数据库启停枚举值
|
|
:param fallback: 数据库值为空时使用的默认值
|
|
:return: 是否启用
|
|
"""
|
|
if enabled is None:
|
|
return fallback
|
|
return enabled == '0'
|
|
|
|
@classmethod
|
|
def is_database_plugin_enabled(cls, database_plugin: object | None, fallback: bool = False) -> bool:
|
|
"""
|
|
判断数据库插件状态是否表示可用启用态。
|
|
|
|
:param database_plugin: 数据库插件状态对象
|
|
:param fallback: 数据库对象或启停值为空时使用的默认值
|
|
:return: 是否为可用启用态
|
|
"""
|
|
if not database_plugin:
|
|
return fallback
|
|
if getattr(database_plugin, 'status', None) == 'error':
|
|
return False
|
|
return cls.db_value_to_enabled(getattr(database_plugin, 'enabled', None), fallback=fallback)
|
|
|
|
@staticmethod
|
|
def _needs_upgrade(installed_version: str | None, source_version: str | None) -> bool:
|
|
"""
|
|
判断源码版本是否高于已安装版本。
|
|
|
|
:param installed_version: 已安装版本
|
|
:param source_version: 当前源码版本
|
|
:return: 是否需要升级
|
|
"""
|
|
return bool(
|
|
installed_version
|
|
and source_version
|
|
and PluginVersionComparator.is_upgrade_available(installed_version, source_version)
|
|
)
|
|
|
|
|
|
class PluginStateTransitionTable:
|
|
"""
|
|
插件状态流转表。
|
|
|
|
使用 Table Driven 模式集中维护可观察的插件状态流转,供文档、测试和后续预检复用。
|
|
"""
|
|
|
|
_TRANSITIONS = (
|
|
PluginStateTransition(None, 'discover', 'discovered', '首次扫描到本地插件'),
|
|
PluginStateTransition('discovered', 'install', 'installed', '插件安装完成'),
|
|
PluginStateTransition('discovered', 'disable', 'discovered', '未安装插件被显式停用'),
|
|
PluginStateTransition('discovered', 'mark_error', 'error', '插件安装或启动失败'),
|
|
PluginStateTransition('installed', 'disable', 'installed', '插件被停用'),
|
|
PluginStateTransition('installed', 'enable', 'installed', '插件被启用'),
|
|
PluginStateTransition('installed', 'upgrade_available', 'pending_upgrade', '发现源码版本高于已安装版本'),
|
|
PluginStateTransition('installed', 'mark_error', 'error', '插件运行或操作失败'),
|
|
PluginStateTransition('pending_upgrade', 'upgrade', 'installed', '插件升级完成'),
|
|
PluginStateTransition('pending_upgrade', 'disable', 'pending_upgrade', '待升级插件被停用'),
|
|
PluginStateTransition('pending_upgrade', 'enable', 'pending_upgrade', '待升级插件被启用'),
|
|
PluginStateTransition('pending_upgrade', 'mark_error', 'error', '插件升级或运行失败'),
|
|
PluginStateTransition('error', 'disable', 'error', '异常插件被显式停用'),
|
|
PluginStateTransition('error', 'install', 'installed', '异常插件重新安装成功'),
|
|
PluginStateTransition('error', 'upgrade', 'installed', '异常插件升级修复完成'),
|
|
PluginStateTransition('error', 'mark_error', 'error', '异常插件再次记录失败信息'),
|
|
)
|
|
|
|
@classmethod
|
|
def list_transitions(cls) -> list[PluginStateTransition]:
|
|
"""
|
|
获取全部插件状态流转规则。
|
|
|
|
:return: 插件状态流转规则列表
|
|
"""
|
|
return list(cls._TRANSITIONS)
|
|
|
|
@classmethod
|
|
def can_transition(cls, source: PluginStatus | None, operation: PluginStateOperation) -> bool:
|
|
"""
|
|
判断状态操作是否允许执行。
|
|
|
|
:param source: 来源状态,None 表示数据库中尚无插件状态
|
|
:param operation: 状态流转操作
|
|
:return: 是否允许流转
|
|
"""
|
|
return cls.resolve_target(source, operation) is not None
|
|
|
|
@classmethod
|
|
def resolve_target(
|
|
cls,
|
|
source: PluginStatus | None,
|
|
operation: PluginStateOperation,
|
|
) -> PluginStatus | None:
|
|
"""
|
|
解析状态操作的目标状态。
|
|
|
|
:param source: 来源状态,None 表示数据库中尚无插件状态
|
|
:param operation: 状态流转操作
|
|
:return: 目标状态,不允许流转时返回 None
|
|
"""
|
|
for transition in cls._TRANSITIONS:
|
|
if transition.source == source and transition.operation == operation:
|
|
return transition.target
|
|
|
|
return None
|