@@ -120,70 +120,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16" class="insight-grid">
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-card class="workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>依赖核心插件</h3>
|
||||
<p>优先保护被多个插件依赖的基础能力节点,避免单点异常扩散。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rank-list">
|
||||
<div v-if="topDependencyCorePlugins.length === 0" class="mobile-empty-state">暂无依赖关系数据</div>
|
||||
<div v-for="(plugin, index) in topDependencyCorePlugins" :key="`core-${plugin.module_name}`" class="rank-item">
|
||||
<div class="rank-item__index">{% raw %}{{ index + 1 }}{% endraw %}</div>
|
||||
<div class="rank-item__content">
|
||||
<div class="rank-item__title-row">
|
||||
<div class="rank-item__title">{% raw %}{{ plugin.name }}{% endraw %}</div>
|
||||
<el-tag :type="governanceTagType(plugin.governance_status)" size="mini">
|
||||
{% raw %}{{ `${(plugin.dependency_summary || {}).dependent_count || 0} 个上游` }}{% endraw %}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="rank-item__summary">
|
||||
{% raw %}{{ buildDependencyCoreSummary(plugin) }}{% endraw %}
|
||||
</div>
|
||||
<div class="rank-item__meta">
|
||||
<span>执行:{% raw %}{{ executionLabel((plugin.execution_summary || {}).status) }}{% endraw %}</span>
|
||||
<span>治理:{% raw %}{{ governanceLabel(plugin.governance_status) }}{% endraw %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-card class="workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>缺失依赖风险</h3>
|
||||
<p>快速查看声明了依赖但当前目标未加载的插件,优先处理运行链断裂问题。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rank-list">
|
||||
<div v-if="pluginsWithMissingDependencies.length === 0" class="mobile-empty-state">当前没有缺失依赖风险</div>
|
||||
<div v-for="(plugin, index) in pluginsWithMissingDependencies" :key="`missing-${plugin.module_name}`" class="rank-item">
|
||||
<div class="rank-item__index">{% raw %}{{ index + 1 }}{% endraw %}</div>
|
||||
<div class="rank-item__content">
|
||||
<div class="rank-item__title-row">
|
||||
<div class="rank-item__title">{% raw %}{{ plugin.name }}{% endraw %}</div>
|
||||
<el-tag type="warning" size="mini">
|
||||
{% raw %}{{ `${(plugin.dependency_summary || {}).missing_count || 0} 个缺失` }}{% endraw %}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="rank-item__summary">
|
||||
{% raw %}{{ buildMissingDependencySummary(plugin) }}{% endraw %}
|
||||
</div>
|
||||
<div class="rank-item__meta">
|
||||
<span>模块:{% raw %}{{ plugin.module_name }}{% endraw %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-card class="workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
@@ -322,9 +258,6 @@
|
||||
<span>{% raw %}{{ governanceIssueSummary(plugin) }}{% endraw %}</span>
|
||||
<span>{% raw %}{{ plugin.feature_key ? `Feature: ${plugin.feature_key}` : '未接入群级权限' }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="mobile-plugin-card__meta">
|
||||
<span>依赖:{% raw %}{{ buildDependencySummaryText(plugin) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="mobile-plugin-card__actions">
|
||||
<el-button
|
||||
size="mini"
|
||||
@@ -389,55 +322,6 @@
|
||||
</div>
|
||||
<span v-else>无</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="依赖关系" :span="2" v-if="selectedPlugin.dependency_summary">
|
||||
<div class="config-overview-grid">
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">声明依赖</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.dependency_summary.declared_count || 0 }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">已解析依赖</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.dependency_summary.resolved_count || 0 }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">缺失依赖</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.dependency_summary.missing_count || 0 }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">下游依赖</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.dependency_summary.dependent_count || 0 }}{% endraw %}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dependency-panels">
|
||||
<div class="dependency-panel">
|
||||
<div class="dependency-panel__title">已解析依赖</div>
|
||||
<div v-if="selectedPlugin.resolved_dependencies && selectedPlugin.resolved_dependencies.length > 0" class="command-tags">
|
||||
<el-tag v-for="dependency in selectedPlugin.resolved_dependencies" :key="`resolved-${dependency.module_name}`" size="mini" effect="plain">
|
||||
{% raw %}{{ `${dependency.name} (${dependency.status_label || dependency.status || '未知'})` }}{% endraw %}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else class="entity-subtitle">无</div>
|
||||
</div>
|
||||
<div class="dependency-panel">
|
||||
<div class="dependency-panel__title">缺失依赖</div>
|
||||
<div v-if="selectedPlugin.missing_dependencies && selectedPlugin.missing_dependencies.length > 0" class="command-tags">
|
||||
<el-tag v-for="dependency in selectedPlugin.missing_dependencies" :key="`missing-${dependency.name}`" size="mini" type="warning">
|
||||
{% raw %}{{ dependency.name }}{% endraw %}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else class="entity-subtitle">无</div>
|
||||
</div>
|
||||
<div class="dependency-panel">
|
||||
<div class="dependency-panel__title">下游依赖插件</div>
|
||||
<div v-if="selectedPlugin.dependent_plugins && selectedPlugin.dependent_plugins.length > 0" class="command-tags">
|
||||
<el-tag v-for="dependency in selectedPlugin.dependent_plugins" :key="`dependent-${dependency.module_name}`" size="mini" type="success" effect="plain">
|
||||
{% raw %}{{ `${dependency.name} (${dependency.status_label || dependency.status || '未知'})` }}{% endraw %}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else class="entity-subtitle">无</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="命令列表" :span="2" v-if="selectedPlugin.commands && selectedPlugin.commands.length > 0">
|
||||
<div class="command-tags">
|
||||
<el-tag v-for="cmd in selectedPlugin.commands" :key="cmd" size="mini" class="command-tag">
|
||||
@@ -777,29 +661,6 @@
|
||||
})
|
||||
.slice(0, 5);
|
||||
},
|
||||
topDependencyCorePlugins() {
|
||||
// 核心依赖插件优先按“被多少插件依赖”排序,
|
||||
// 这样最容易形成单点影响的基础插件会排在前面。
|
||||
return (this.plugins || [])
|
||||
.filter(plugin => Number(((plugin.dependency_summary || {}).dependent_count) || 0) > 0)
|
||||
.slice()
|
||||
.sort((left, right) => {
|
||||
return (
|
||||
Number(((right.dependency_summary || {}).dependent_count) || 0) - Number(((left.dependency_summary || {}).dependent_count) || 0)
|
||||
|| Number(((right.dependency_summary || {}).declared_count) || 0) - Number(((left.dependency_summary || {}).declared_count) || 0)
|
||||
);
|
||||
})
|
||||
.slice(0, 5);
|
||||
},
|
||||
pluginsWithMissingDependencies() {
|
||||
return (this.plugins || [])
|
||||
.filter(plugin => Number(((plugin.dependency_summary || {}).missing_count) || 0) > 0)
|
||||
.slice()
|
||||
.sort((left, right) => {
|
||||
return Number(((right.dependency_summary || {}).missing_count) || 0) - Number(((left.dependency_summary || {}).missing_count) || 0);
|
||||
})
|
||||
.slice(0, 5);
|
||||
},
|
||||
slowestPlugins() {
|
||||
// 慢插件排行只看有执行样本的插件,避免未执行插件把榜单冲掉。
|
||||
return (this.plugins || [])
|
||||
@@ -913,33 +774,6 @@
|
||||
if (!Number.isFinite(normalizedValue) || normalizedValue <= 0) return '-';
|
||||
return `${normalizedValue.toFixed(2)} ms`;
|
||||
},
|
||||
buildDependencySummaryText(plugin) {
|
||||
const dependencySummary = (plugin && plugin.dependency_summary) || {};
|
||||
const declaredCount = Number(dependencySummary.declared_count || 0);
|
||||
const missingCount = Number(dependencySummary.missing_count || 0);
|
||||
const dependentCount = Number(dependencySummary.dependent_count || 0);
|
||||
if (declaredCount <= 0 && dependentCount <= 0) {
|
||||
return '无依赖关系';
|
||||
}
|
||||
if (missingCount > 0) {
|
||||
return `声明 ${declaredCount} 个,缺失 ${missingCount} 个`;
|
||||
}
|
||||
if (dependentCount > 0) {
|
||||
return `被 ${dependentCount} 个插件依赖`;
|
||||
}
|
||||
return `已解析 ${declaredCount} 个依赖`;
|
||||
},
|
||||
buildDependencyCoreSummary(plugin) {
|
||||
const dependencySummary = (plugin && plugin.dependency_summary) || {};
|
||||
return `当前被 ${(dependencySummary.dependent_count || 0)} 个插件依赖,自身声明 ${(dependencySummary.declared_count || 0)} 个依赖。`;
|
||||
},
|
||||
buildMissingDependencySummary(plugin) {
|
||||
const missingDependencies = ((plugin && plugin.missing_dependencies) || []).map(item => item.name).filter(Boolean);
|
||||
if (!missingDependencies.length) {
|
||||
return '当前没有缺失依赖。';
|
||||
}
|
||||
return `缺失依赖:${missingDependencies.join('、')}`;
|
||||
},
|
||||
loadPlugins() {
|
||||
this.loading = true;
|
||||
axios.get('/api/plugins')
|
||||
@@ -1359,24 +1193,6 @@
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
.dependency-panels {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.dependency-panel {
|
||||
padding: 12px;
|
||||
border-radius: 14px;
|
||||
background: rgba(248,250,252,0.82);
|
||||
border: 1px solid rgba(148,163,184,0.12);
|
||||
}
|
||||
.dependency-panel__title {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #334155;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.config-overview-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -801,85 +801,6 @@ class PluginManager:
|
||||
"execution_summary": execution_summary,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _normalize_snapshot_dependency_key(value: Any) -> str:
|
||||
"""把依赖引用统一规整成可匹配的 key。"""
|
||||
return str(value or "").strip().lower()
|
||||
|
||||
def _enrich_dependency_relationships(self, snapshots: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
"""为插件快照补齐依赖关系摘要。
|
||||
|
||||
设计目标:
|
||||
1. 后台既要看“我依赖谁”,也要看“谁依赖我”;
|
||||
2. 依赖声明有可能写插件名,也可能写模块名,因此这里做双 key 兼容;
|
||||
3. 缺失依赖需要单独产出,便于页面上做风险聚合与高亮。
|
||||
"""
|
||||
normalized_snapshots = [dict(item or {}) for item in (snapshots or [])]
|
||||
lookup_by_key: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
for snapshot in normalized_snapshots:
|
||||
module_name = str(snapshot.get("module_name") or "").strip()
|
||||
display_name = str(snapshot.get("name") or "").strip()
|
||||
if module_name:
|
||||
lookup_by_key[self._normalize_snapshot_dependency_key(module_name)] = snapshot
|
||||
if display_name:
|
||||
lookup_by_key[self._normalize_snapshot_dependency_key(display_name)] = snapshot
|
||||
|
||||
for snapshot in normalized_snapshots:
|
||||
snapshot["resolved_dependencies"] = []
|
||||
snapshot["missing_dependencies"] = []
|
||||
snapshot["dependent_plugins"] = []
|
||||
|
||||
for snapshot in normalized_snapshots:
|
||||
dependencies = list(snapshot.get("dependencies", []) or [])
|
||||
resolved_dependencies = []
|
||||
missing_dependencies = []
|
||||
|
||||
for dependency_name in dependencies:
|
||||
normalized_key = self._normalize_snapshot_dependency_key(dependency_name)
|
||||
target_snapshot = lookup_by_key.get(normalized_key)
|
||||
if target_snapshot:
|
||||
dependency_row = {
|
||||
"name": str(target_snapshot.get("name") or "").strip(),
|
||||
"module_name": str(target_snapshot.get("module_name") or "").strip(),
|
||||
"status": str(target_snapshot.get("status") or "").strip(),
|
||||
"status_label": str(target_snapshot.get("status_label") or "").strip(),
|
||||
"governance_status": str(target_snapshot.get("governance_status") or "").strip(),
|
||||
}
|
||||
resolved_dependencies.append(dependency_row)
|
||||
target_snapshot.setdefault("dependent_plugins", []).append(
|
||||
{
|
||||
"name": str(snapshot.get("name") or "").strip(),
|
||||
"module_name": str(snapshot.get("module_name") or "").strip(),
|
||||
"status": str(snapshot.get("status") or "").strip(),
|
||||
"status_label": str(snapshot.get("status_label") or "").strip(),
|
||||
"governance_status": str(snapshot.get("governance_status") or "").strip(),
|
||||
}
|
||||
)
|
||||
else:
|
||||
missing_dependencies.append(
|
||||
{
|
||||
"name": str(dependency_name or "").strip(),
|
||||
}
|
||||
)
|
||||
|
||||
snapshot["resolved_dependencies"] = resolved_dependencies
|
||||
snapshot["missing_dependencies"] = missing_dependencies
|
||||
snapshot["dependency_summary"] = {
|
||||
"declared_count": len(dependencies),
|
||||
"resolved_count": len(resolved_dependencies),
|
||||
"missing_count": len(missing_dependencies),
|
||||
"dependent_count": len(snapshot.get("dependent_plugins", []) or []),
|
||||
"has_missing": len(missing_dependencies) > 0,
|
||||
}
|
||||
|
||||
for snapshot in normalized_snapshots:
|
||||
dependent_plugins = list(snapshot.get("dependent_plugins", []) or [])
|
||||
dependent_plugins.sort(key=lambda item: (str(item.get("name") or ""), str(item.get("module_name") or "")))
|
||||
snapshot["dependent_plugins"] = dependent_plugins
|
||||
|
||||
return normalized_snapshots
|
||||
|
||||
@staticmethod
|
||||
def _status_to_label(status: str) -> str:
|
||||
"""把运行态状态码转换成中文展示文案。"""
|
||||
@@ -912,8 +833,6 @@ class PluginManager:
|
||||
for module_name in sorted(discovered_module_names - loaded_module_names):
|
||||
snapshots.append(self._build_unloaded_plugin_snapshot(module_name))
|
||||
|
||||
snapshots = self._enrich_dependency_relationships(snapshots)
|
||||
|
||||
snapshots.sort(
|
||||
key=lambda item: (
|
||||
0 if item.get("status") == "RUNNING" else 1,
|
||||
|
||||
@@ -406,7 +406,6 @@
|
||||
- 第一阶段已完成:后台插件管理页已补充治理健康、能力类型、Feature Key、依赖与配置概览信息
|
||||
- 第一阶段已完成:插件配置保存前已增加格式校验,避免坏配置直接写回线上文件
|
||||
- 第二阶段已完成:插件管理页已补充执行表现摘要、最近错误信息与高风险/慢插件排行,便于快速定位运行异常插件
|
||||
- 第二阶段已完成:插件快照已补充依赖拓扑摘要,后台可直接查看核心依赖插件、缺失依赖风险与上下游关系
|
||||
- 后续可继续补充插件错误历史、性能排名、依赖图与熔断/隔离控制
|
||||
|
||||
建议内容:
|
||||
|
||||
Reference in New Issue
Block a user