From 6f4efcfa727f12c89178b023288eeff9b2e23888 Mon Sep 17 00:00:00 2001 From: liuwei Date: Tue, 21 Apr 2026 15:47:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(value=5Frank):=20=E7=A4=BE=E4=BA=A4?= =?UTF-8?q?=E8=BE=B9=E6=A0=87=E7=AD=BE=E6=94=B9=E4=B8=BA=E6=98=B5=E7=A7=B0?= =?UTF-8?q?=E5=B9=B6=E5=A2=9E=E5=8A=A0=E6=81=B6=E6=90=9E=E5=85=B3=E7=B3=BB?= =?UTF-8?q?=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 关系图边标签新增双向@次数明细,并在边中点展示 - 标签从 wxid 改为群昵称显示,提升可读性 - 增加恶搞关系标签规则(如双向奔赴、单向上头、单箭头输出等) --- plugins/value_rank/main.py | 67 +++++++++++++++++-- .../value_rank/templates/social_graph.html | 1 + 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/plugins/value_rank/main.py b/plugins/value_rank/main.py index 8ddeff7..3c7fd69 100644 --- a/plugins/value_rank/main.py +++ b/plugins/value_rank/main.py @@ -1247,10 +1247,37 @@ class ValueRankPlugin(MessagePluginInterface): if len(selected_nodes) < 2: return None - selected_edges: List[Tuple[str, str, float, float]] = [] + # 记录每条无向边对应的双向明细次数: + # pair_dir_count[(a,b)] = {"a_to_b": x, "b_to_a": y} + # 这样在图上可以直接标注“双向@次数”,避免只看总互动分看不出方向关系。 + pair_dir_count: Dict[Tuple[str, str], Dict[str, int]] = {} + for row in edge_rows: + uid_a = str(row.get("from_user_id") or "").strip() + uid_b = str(row.get("to_user_id") or "").strip() + if not uid_a or not uid_b or uid_a == uid_b: + continue + a, b = (uid_a, uid_b) if uid_a < uid_b else (uid_b, uid_a) + mention_count = int(float(row.get("mention_count_window") or 0.0)) + bucket = pair_dir_count.setdefault((a, b), {"a_to_b": 0, "b_to_a": 0}) + if uid_a == a and uid_b == b: + bucket["a_to_b"] += mention_count + else: + bucket["b_to_a"] += mention_count + + selected_edges: List[Tuple[str, str, float, float, int, int]] = [] for (a, b), data in edge_map.items(): if a in selected_set and b in selected_set: - selected_edges.append((a, b, float(data.get("mention_count", 0.0)), float(data.get("score", 0.0)))) + dir_bucket = pair_dir_count.get((a, b), {"a_to_b": 0, "b_to_a": 0}) + selected_edges.append( + ( + a, + b, + float(data.get("mention_count", 0.0)), + float(data.get("score", 0.0)), + int(dir_bucket.get("a_to_b", 0)), + int(dir_bucket.get("b_to_a", 0)), + ) + ) if not selected_edges: return None @@ -1274,7 +1301,7 @@ class ValueRankPlugin(MessagePluginInterface): self, group_id: str, selected_nodes: List[str], - selected_edges: List[Tuple[str, str, float, float]], + selected_edges: List[Tuple[str, str, float, float, int, int]], partner_map: Dict[str, set], node_score_map: Dict[str, float], ) -> str: @@ -1303,7 +1330,11 @@ class ValueRankPlugin(MessagePluginInterface): cm = ContactManager.get_instance() edge_svg_parts: List[str] = [] - for a, b, mention_count, score in selected_edges: + # 双向@标签层: + # 1. 每条边中点标注“a->b / b->a”; + # 2. 使用白底半透明标签提升可读性,避免与连线重叠难辨识。 + edge_label_parts: List[str] = [] + for a, b, mention_count, score, a_to_b_count, b_to_a_count in selected_edges: ax, ay = pos_map[a] bx, by = pos_map[b] normalized = max(0.12, min(score / max_edge_score, 1.0)) @@ -1313,6 +1344,33 @@ class ValueRankPlugin(MessagePluginInterface): f'' ) + # 通过字典序固定方向,确保同一条边每次渲染文案方向一致。 + # 标签文案改为“昵称 + 双向次数 + 恶搞关系标签”,不再展示 wxid。 + mx, my = (ax + bx) / 2.0, (ay + by) / 2.0 + nick_a = cm.get_group_name(group_id, a) or a + nick_b = cm.get_group_name(group_id, b) or b + + total_count = int(a_to_b_count) + int(b_to_a_count) + diff_count = abs(int(a_to_b_count) - int(b_to_a_count)) + if total_count >= 12 and diff_count <= 2: + relation_tag = "双向奔赴" + elif diff_count >= 8 and max(int(a_to_b_count), int(b_to_a_count)) >= 10: + relation_tag = "单向上头" + elif total_count <= 2: + relation_tag = "点头之交" + elif int(a_to_b_count) == 0 or int(b_to_a_count) == 0: + relation_tag = "单箭头输出" + else: + relation_tag = "互相捧场" + + label_text = f"{nick_a}→{nick_b} {a_to_b_count}/{b_to_a_count} | {relation_tag}" + safe_label = html.escape(label_text) + edge_label_parts.append( + f'' + f'' + f'{safe_label}' + f'' + ) # 节点头像层拆分为 defs + body 两段: # 1. defs 内定义每个节点的裁剪路径,避免头像越界; @@ -1399,6 +1457,7 @@ class ValueRankPlugin(MessagePluginInterface): "__GROUP_TITLE__": group_title, "__SUMMARY_TEXT__": summary_text, "__EDGE_SVG__": "".join(edge_svg_parts), + "__EDGE_LABELS__": "".join(edge_label_parts), "__NODE_DEFS__": "".join(node_defs_parts), "__NODE_SVG__": "".join(node_svg_parts), } diff --git a/plugins/value_rank/templates/social_graph.html b/plugins/value_rank/templates/social_graph.html index 99d6b77..6ce9614 100644 --- a/plugins/value_rank/templates/social_graph.html +++ b/plugins/value_rank/templates/social_graph.html @@ -53,6 +53,7 @@ __NODE_DEFS__ __EDGE_SVG__ + __EDGE_LABELS__ __NODE_SVG__