@@ -445,162 +445,6 @@ class PluginManager:
|
||||
return [commands.strip()]
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def _dispatch_mode_label(mode: str) -> str:
|
||||
"""把消息分发模式转换成后台可读中文。"""
|
||||
normalized_mode = str(mode or "").strip().lower()
|
||||
mapping = {
|
||||
"sync": "前台同步",
|
||||
"background": "后台任务",
|
||||
"mixed": "混合模式",
|
||||
"non_message": "非消息插件",
|
||||
"unknown": "未知",
|
||||
}
|
||||
return mapping.get(normalized_mode, "未知")
|
||||
|
||||
def _build_dispatch_preview_message(self, plugin: PluginInterface, command: str = "") -> Dict[str, Any]:
|
||||
"""构造用于后台预览分发模式的假消息。
|
||||
|
||||
说明:
|
||||
1. 后台只需要知道“这个插件命中后大概率走前台还是后台”,不需要真实微信上下文;
|
||||
2. 因此这里构造一份最小可用消息,尽量覆盖多数插件 `get_message_dispatch_mode()` 的读取字段;
|
||||
3. 如果某些插件依赖更复杂上下文,后续会在调用层捕获异常并回退为 `unknown`,不会影响治理页整体可用性。
|
||||
"""
|
||||
command_text = str(command or "").strip()
|
||||
command_prefix = str(getattr(plugin, "command_prefix", "") or "").strip()
|
||||
content = command_text
|
||||
if command_text and command_prefix and not command_text.startswith(command_prefix):
|
||||
content = f"{command_prefix}{command_text}"
|
||||
|
||||
return {
|
||||
"type": "dashboard_preview",
|
||||
"content": content,
|
||||
"sender": "dashboard_preview",
|
||||
"roomid": "dashboard_preview_room",
|
||||
"is_at": False,
|
||||
"timestamp": 0,
|
||||
"trace_id": "dashboard-preview",
|
||||
"all_contacts": {},
|
||||
"full_wx_msg": None,
|
||||
"gbm": None,
|
||||
"bot": None,
|
||||
"revoke": None,
|
||||
}
|
||||
|
||||
def _preview_plugin_dispatch_mode(self, plugin: PluginInterface, preview_message: Dict[str, Any]) -> str:
|
||||
"""安全预览插件在给定消息下的分发模式。"""
|
||||
try:
|
||||
raw_mode = plugin.get_message_dispatch_mode(preview_message)
|
||||
return MessagePluginInterface.normalize_message_dispatch_mode(raw_mode)
|
||||
except Exception as e:
|
||||
module_name = self._get_module_name_from_plugin(plugin) or getattr(plugin, "name", "unknown")
|
||||
self.LOG.debug(f"插件分发模式预览失败: module={module_name}, error={e}")
|
||||
return "unknown"
|
||||
|
||||
def _build_plugin_dispatch_summary(
|
||||
self,
|
||||
plugin: PluginInterface,
|
||||
commands: Optional[List[str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""构建插件“前台 / 后台”执行方式摘要。
|
||||
|
||||
设计目标:
|
||||
1. 后台插件治理页需要一眼看出消息插件是“前台同步”“后台任务”还是“按命令混合切换”;
|
||||
2. 同一个插件里可能既有轻命令又有长命令,因此不能只看静态配置,还要做命令级预览;
|
||||
3. 统一在快照层产出摘要后,列表、详情、移动端卡片都能直接复用,不必各写一套判断逻辑。
|
||||
"""
|
||||
if not isinstance(plugin, MessagePluginInterface):
|
||||
return {
|
||||
"mode": "non_message",
|
||||
"label": self._dispatch_mode_label("non_message"),
|
||||
"description": "该插件不参与消息主链路分发,不区分前台同步或后台任务。",
|
||||
"is_message_plugin": False,
|
||||
"supports_dynamic_dispatch": False,
|
||||
"sync_command_count": 0,
|
||||
"background_command_count": 0,
|
||||
"unknown_command_count": 0,
|
||||
"preview_failed_count": 0,
|
||||
"sampled_command_count": 0,
|
||||
"command_modes": [],
|
||||
}
|
||||
|
||||
commands = list(commands or self._collect_plugin_commands(plugin))
|
||||
preview_items: List[Tuple[str, Dict[str, Any]]] = []
|
||||
if commands:
|
||||
for command in commands:
|
||||
preview_items.append((command, self._build_dispatch_preview_message(plugin, command)))
|
||||
else:
|
||||
# 没有显式命令声明的消息插件,至少用一条空消息预览默认分发模式:
|
||||
# 1. 例如链接解析类插件可能靠正则命中,而不是 commands 数组;
|
||||
# 2. 这类插件通常不会做命令级动态切换,空消息预览已经足够判断默认模式;
|
||||
# 3. 后台仍会标记 sampled_command_count=0,避免用户误以为这里真的存在命令清单。
|
||||
preview_items.append(("", self._build_dispatch_preview_message(plugin, "")))
|
||||
|
||||
mode_counters = {"sync": 0, "background": 0, "unknown": 0}
|
||||
command_modes = []
|
||||
for command, preview_message in preview_items:
|
||||
preview_mode = self._preview_plugin_dispatch_mode(plugin, preview_message)
|
||||
if preview_mode not in mode_counters:
|
||||
preview_mode = "unknown"
|
||||
mode_counters[preview_mode] += 1
|
||||
|
||||
if command:
|
||||
command_modes.append(
|
||||
{
|
||||
"command": command,
|
||||
"mode": preview_mode,
|
||||
"label": self._dispatch_mode_label(preview_mode),
|
||||
}
|
||||
)
|
||||
|
||||
known_modes = []
|
||||
if mode_counters["sync"] > 0:
|
||||
known_modes.append("sync")
|
||||
if mode_counters["background"] > 0:
|
||||
known_modes.append("background")
|
||||
|
||||
if len(known_modes) > 1:
|
||||
summary_mode = "mixed"
|
||||
description = (
|
||||
f"插件会按命令动态切换执行链路:前台同步 {mode_counters['sync']} 个,"
|
||||
f"后台任务 {mode_counters['background']} 个。"
|
||||
)
|
||||
elif len(known_modes) == 1:
|
||||
summary_mode = known_modes[0]
|
||||
if known_modes[0] == "background":
|
||||
if commands:
|
||||
description = f"当前采样的 {len(commands)} 个命令均会转入后台任务池执行。"
|
||||
else:
|
||||
description = "该消息插件默认转入后台任务池执行。"
|
||||
else:
|
||||
if commands:
|
||||
description = f"当前采样的 {len(commands)} 个命令均在前台消息链路同步执行。"
|
||||
else:
|
||||
description = "该消息插件默认在前台消息链路同步执行。"
|
||||
else:
|
||||
summary_mode = "unknown"
|
||||
description = "插件已加载,但当前无法稳定预览其前台 / 后台执行方式。"
|
||||
|
||||
if mode_counters["unknown"] > 0:
|
||||
description = (
|
||||
f"{description} 另有 {mode_counters['unknown']} 条预览样本无法识别,"
|
||||
"可能依赖更完整的运行时上下文。"
|
||||
)
|
||||
|
||||
return {
|
||||
"mode": summary_mode,
|
||||
"label": self._dispatch_mode_label(summary_mode),
|
||||
"description": description,
|
||||
"is_message_plugin": True,
|
||||
"supports_dynamic_dispatch": summary_mode == "mixed",
|
||||
"sync_command_count": mode_counters["sync"],
|
||||
"background_command_count": mode_counters["background"],
|
||||
"unknown_command_count": mode_counters["unknown"],
|
||||
"preview_failed_count": mode_counters["unknown"],
|
||||
"sampled_command_count": len(commands),
|
||||
"command_modes": command_modes,
|
||||
}
|
||||
|
||||
def _build_governance_diagnostics(
|
||||
self,
|
||||
*,
|
||||
@@ -862,7 +706,6 @@ class PluginManager:
|
||||
config_path = plugin.get_config_path()
|
||||
config_overview = self._read_plugin_config_overview(config_path)
|
||||
commands = self._collect_plugin_commands(plugin)
|
||||
dispatch_summary = self._build_plugin_dispatch_summary(plugin, commands)
|
||||
feature_key = str(getattr(plugin, "feature_key", "") or "").strip()
|
||||
feature_description = str(getattr(plugin, "feature_description", "") or "").strip()
|
||||
governance_diagnostics = self._build_governance_diagnostics(
|
||||
@@ -886,8 +729,6 @@ class PluginManager:
|
||||
"commands": commands,
|
||||
"command_count": len(commands),
|
||||
"command_prefix": getattr(plugin, "command_prefix", ""),
|
||||
"dispatch_mode": dispatch_summary["mode"],
|
||||
"dispatch_summary": dispatch_summary,
|
||||
"dependencies": list(getattr(plugin, "dependencies", []) or []),
|
||||
"feature_key": feature_key,
|
||||
"feature_description": feature_description,
|
||||
@@ -929,19 +770,6 @@ class PluginManager:
|
||||
status = "ERROR"
|
||||
elif runtime_state == "disabled_by_config":
|
||||
status = "STOPPED"
|
||||
dispatch_summary = {
|
||||
"mode": "unknown",
|
||||
"label": self._dispatch_mode_label("unknown"),
|
||||
"description": "插件未成功加载,暂时无法判断其前台 / 后台执行方式。",
|
||||
"is_message_plugin": False,
|
||||
"supports_dynamic_dispatch": False,
|
||||
"sync_command_count": 0,
|
||||
"background_command_count": 0,
|
||||
"unknown_command_count": 0,
|
||||
"preview_failed_count": 0,
|
||||
"sampled_command_count": 0,
|
||||
"command_modes": [],
|
||||
}
|
||||
|
||||
return {
|
||||
"name": module_name,
|
||||
@@ -955,8 +783,6 @@ class PluginManager:
|
||||
"commands": [],
|
||||
"command_count": 0,
|
||||
"command_prefix": "",
|
||||
"dispatch_mode": dispatch_summary["mode"],
|
||||
"dispatch_summary": dispatch_summary,
|
||||
"dependencies": [],
|
||||
"feature_key": "",
|
||||
"feature_description": "",
|
||||
@@ -1103,10 +929,10 @@ class PluginManager:
|
||||
if not target_name:
|
||||
return None
|
||||
|
||||
# 详情页也统一走“完整治理快照列表”:
|
||||
# 1. 列表页已经会补齐依赖关系、执行摘要、分发模式等增强字段;
|
||||
# 2. 如果详情页单独重建快照,容易出现“列表里有,详情里没有”的字段不一致;
|
||||
# 3. 这里直接复用同一套结果,保证前后端看到的是同一份治理视图。
|
||||
display_name, plugin = self.find_plugin_by_name(target_name)
|
||||
if plugin:
|
||||
return self._build_plugin_snapshot(plugin)
|
||||
|
||||
for snapshot in self.get_plugin_snapshots():
|
||||
if snapshot.get("module_name") == target_name or snapshot.get("name") == target_name:
|
||||
return snapshot
|
||||
|
||||
Reference in New Issue
Block a user