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
max_graph_nodes = 24
graph_edge_pool_limit = 300
social_graph_template_path = "plugins/value_rank/templates/social_graph.html"
default_trend_days = 7
max_trend_days = 30

View File

@@ -557,6 +557,7 @@ class ValueRankPlugin(MessagePluginInterface):
self.default_graph_nodes = 12
self.max_graph_nodes = 24
self.graph_edge_pool_limit = 300
self.social_graph_template_path = "plugins/value_rank/templates/social_graph.html"
self.default_trend_days = 7
self.max_trend_days = 30
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.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.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.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))
@@ -1241,7 +1245,7 @@ class ValueRankPlugin(MessagePluginInterface):
partner_map: Dict[str, set],
node_score_map: Dict[str, float],
) -> str:
"""构建社交关系图 HTML含 SVG 节点和边)。"""
"""基于模板文件构建社交关系图 HTML含 SVG 节点和边)。"""
if not selected_nodes:
return ""
@@ -1307,69 +1311,34 @@ class ValueRankPlugin(MessagePluginInterface):
f"关系边:{len(selected_edges)} 生成时间:{now_text}"
)
return f"""
<!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>
{''.join(edge_svg_parts)}
{''.join(node_svg_parts)}
</svg>
</div>
<div class="legend">
说明:节点越大代表连接群友越多;连线越粗代表互动越强。该图仅基于 @ 关系统计,不含纯文本对话引用关系。
</div>
</div>
</body>
</html>
"""
# 模板策略说明:
# 1. 本插件只走外部模板文件,便于后续视觉同学直接调整样式;
# 2. 模板缺失或读取失败时直接返回空字符串,让上层按“生成失败”处理;
# 3. 变量替换使用显式占位符,避免与 CSS 花括号冲突。
template_path = Path(self.social_graph_template_path)
if not template_path.is_absolute():
template_path = Path.cwd() / template_path
if not template_path.exists():
self.LOG.error(f"[{self.name}] 社交图模板不存在: {template_path}")
return ""
try:
template_html = template_path.read_text(encoding="utf-8")
except Exception as e:
self.LOG.error(f"[{self.name}] 社交图模板读取失败: {template_path}, error={e}")
return ""
replace_map = {
"__WIDTH__": str(width),
"__HEIGHT__": str(height),
"__GROUP_TITLE__": group_title,
"__SUMMARY_TEXT__": summary_text,
"__EDGE_SVG__": "".join(edge_svg_parts),
"__NODE_SVG__": "".join(node_svg_parts),
}
for key, value in replace_map.items():
template_html = template_html.replace(key, value)
return template_html
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>