refactor(value_rank): 社交关系图改为外部HTML模板渲染

- 新增 social_graph_template_path 配置项,支持独立维护社交图模板路径

- 新增模板文件 plugins/value_rank/templates/social_graph.html,承载关系图样式与占位符

- 移除内嵌模板拼接,改为读取模板文件并进行变量替换后渲染截图
This commit is contained in:
liuwei
2026-04-21 14:24:57 +08:00
parent 46ee371a76
commit 22c5101407
3 changed files with 95 additions and 64 deletions

View File

@@ -34,6 +34,7 @@ max_rank_limit = 50
default_graph_nodes = 12 default_graph_nodes = 12
max_graph_nodes = 24 max_graph_nodes = 24
graph_edge_pool_limit = 300 graph_edge_pool_limit = 300
social_graph_template_path = "plugins/value_rank/templates/social_graph.html"
default_trend_days = 7 default_trend_days = 7
max_trend_days = 30 max_trend_days = 30

View File

@@ -557,6 +557,7 @@ class ValueRankPlugin(MessagePluginInterface):
self.default_graph_nodes = 12 self.default_graph_nodes = 12
self.max_graph_nodes = 24 self.max_graph_nodes = 24
self.graph_edge_pool_limit = 300 self.graph_edge_pool_limit = 300
self.social_graph_template_path = "plugins/value_rank/templates/social_graph.html"
self.default_trend_days = 7 self.default_trend_days = 7
self.max_trend_days = 30 self.max_trend_days = 30
self.mention_batch_size = 200 self.mention_batch_size = 200
@@ -589,6 +590,9 @@ class ValueRankPlugin(MessagePluginInterface):
self.default_graph_nodes = int(cfg.get("default_graph_nodes", self.default_graph_nodes)) self.default_graph_nodes = int(cfg.get("default_graph_nodes", self.default_graph_nodes))
self.max_graph_nodes = int(cfg.get("max_graph_nodes", self.max_graph_nodes)) self.max_graph_nodes = int(cfg.get("max_graph_nodes", self.max_graph_nodes))
self.graph_edge_pool_limit = int(cfg.get("graph_edge_pool_limit", self.graph_edge_pool_limit)) self.graph_edge_pool_limit = int(cfg.get("graph_edge_pool_limit", self.graph_edge_pool_limit))
self.social_graph_template_path = str(
cfg.get("social_graph_template_path", self.social_graph_template_path)
).strip()
self.default_trend_days = int(cfg.get("default_trend_days", self.default_trend_days)) self.default_trend_days = int(cfg.get("default_trend_days", self.default_trend_days))
self.max_trend_days = int(cfg.get("max_trend_days", self.max_trend_days)) self.max_trend_days = int(cfg.get("max_trend_days", self.max_trend_days))
self.mention_batch_size = int(cfg.get("mention_batch_size", self.mention_batch_size)) self.mention_batch_size = int(cfg.get("mention_batch_size", self.mention_batch_size))
@@ -1241,7 +1245,7 @@ class ValueRankPlugin(MessagePluginInterface):
partner_map: Dict[str, set], partner_map: Dict[str, set],
node_score_map: Dict[str, float], node_score_map: Dict[str, float],
) -> str: ) -> str:
"""构建社交关系图 HTML含 SVG 节点和边)。""" """基于模板文件构建社交关系图 HTML含 SVG 节点和边)。"""
if not selected_nodes: if not selected_nodes:
return "" return ""
@@ -1307,69 +1311,34 @@ class ValueRankPlugin(MessagePluginInterface):
f"关系边:{len(selected_edges)} 生成时间:{now_text}" f"关系边:{len(selected_edges)} 生成时间:{now_text}"
) )
return f""" # 模板策略说明:
<!DOCTYPE html> # 1. 本插件只走外部模板文件,便于后续视觉同学直接调整样式;
<html lang="zh-CN"> # 2. 模板缺失或读取失败时直接返回空字符串,让上层按“生成失败”处理;
<head> # 3. 变量替换使用显式占位符,避免与 CSS 花括号冲突。
<meta charset="UTF-8" /> template_path = Path(self.social_graph_template_path)
<style> if not template_path.is_absolute():
body {{ template_path = Path.cwd() / template_path
margin: 0; if not template_path.exists():
background: linear-gradient(135deg, #f7fbff 0%, #f2f7ff 45%, #fff8ed 100%); self.LOG.error(f"[{self.name}] 社交图模板不存在: {template_path}")
font-family: "Microsoft YaHei", "PingFang SC", sans-serif; return ""
}}
.card {{ try:
width: {width}px; template_html = template_path.read_text(encoding="utf-8")
margin: 0 auto; except Exception as e:
padding: 26px 26px 20px 26px; self.LOG.error(f"[{self.name}] 社交图模板读取失败: {template_path}, error={e}")
box-sizing: border-box; return ""
}}
.title {{ replace_map = {
font-size: 38px; "__WIDTH__": str(width),
color: #1f2d46; "__HEIGHT__": str(height),
font-weight: 800; "__GROUP_TITLE__": group_title,
letter-spacing: 1px; "__SUMMARY_TEXT__": summary_text,
}} "__EDGE_SVG__": "".join(edge_svg_parts),
.subtitle {{ "__NODE_SVG__": "".join(node_svg_parts),
margin-top: 8px; }
font-size: 17px; for key, value in replace_map.items():
color: #5f6f8a; template_html = template_html.replace(key, value)
}} return template_html
.graph-wrap {{
margin-top: 18px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.82);
border: 1px solid rgba(207, 221, 246, 0.9);
box-shadow: 0 8px 24px rgba(58, 82, 130, 0.12);
overflow: hidden;
}}
.legend {{
margin-top: 14px;
font-size: 14px;
color: #6f7d96;
line-height: 1.7;
}}
</style>
</head>
<body>
<div class="card">
<div class="title">群友社交关系图</div>
<div class="subtitle">{group_title}</div>
<div class="subtitle">{summary_text}</div>
<div class="graph-wrap">
<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}">
<rect x="0" y="0" width="{width}" height="{height}" fill="rgba(247,251,255,0.72)"></rect>
{''.join(edge_svg_parts)}
{''.join(node_svg_parts)}
</svg>
</div>
<div class="legend">
说明:节点越大代表连接群友越多;连线越粗代表互动越强。该图仅基于 @ 关系统计,不含纯文本对话引用关系。
</div>
</div>
</body>
</html>
"""
async def _build_weekly_report_text(self, group_id: str) -> str: async def _build_weekly_report_text(self, group_id: str) -> str:
"""构建“身价周报”文本。 """构建“身价周报”文本。

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<style>
body {
margin: 0;
background: linear-gradient(135deg, #f7fbff 0%, #f2f7ff 45%, #fff8ed 100%);
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
}
.card {
width: __WIDTH__px;
margin: 0 auto;
padding: 26px 26px 20px 26px;
box-sizing: border-box;
}
.title {
font-size: 38px;
color: #1f2d46;
font-weight: 800;
letter-spacing: 1px;
}
.subtitle {
margin-top: 8px;
font-size: 17px;
color: #5f6f8a;
}
.graph-wrap {
margin-top: 18px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.82);
border: 1px solid rgba(207, 221, 246, 0.9);
box-shadow: 0 8px 24px rgba(58, 82, 130, 0.12);
overflow: hidden;
}
.legend {
margin-top: 14px;
font-size: 14px;
color: #6f7d96;
line-height: 1.7;
}
</style>
</head>
<body>
<div class="card">
<div class="title">群友社交关系图</div>
<div class="subtitle">__GROUP_TITLE__</div>
<div class="subtitle">__SUMMARY_TEXT__</div>
<div class="graph-wrap">
<svg width="__WIDTH__" height="__HEIGHT__" viewBox="0 0 __WIDTH__ __HEIGHT__">
<rect x="0" y="0" width="__WIDTH__" height="__HEIGHT__" fill="rgba(247,251,255,0.72)"></rect>
__EDGE_SVG__
__NODE_SVG__
</svg>
</div>
<div class="legend">
说明:节点越大代表连接群友越多;连线越粗代表互动越强。该图仅基于 @ 关系统计,不含纯文本对话引用关系。
</div>
</div>
</body>
</html>