From 2e513fa0e41742415d5b80ea19437726557a636a Mon Sep 17 00:00:00 2001 From: jackwener Date: Thu, 14 May 2026 14:17:48 +0800 Subject: [PATCH] fix(group): keep duplicate nickname senders separate in stats --- src/daemon/query.rs | 59 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/src/daemon/query.rs b/src/daemon/query.rs index a8d09e2..041ff0b 100644 --- a/src/daemon/query.rs +++ b/src/daemon/query.rs @@ -987,6 +987,30 @@ fn sender_display( .unwrap_or_else(|| username.to_string()) } +fn group_top_senders( + sender_counts: &HashMap, + names: &HashMap, + group_nicknames: &HashMap, + limit: usize, +) -> Vec { + let mut top_senders: Vec = sender_counts.iter() + .map(|(username, count)| json!({ + "sender": sender_display(username, "", names, group_nicknames), + "count": count, + })) + .collect(); + top_senders.sort_by(|a, b| { + b["count"].as_i64().unwrap_or(0) + .cmp(&a["count"].as_i64().unwrap_or(0)) + .then_with(|| { + a["sender"].as_str().unwrap_or("") + .cmp(b["sender"].as_str().unwrap_or("")) + }) + }); + top_senders.truncate(limit); + top_senders +} + fn sender_label( real_sender_id: i64, content: &str, @@ -1795,8 +1819,6 @@ pub async fn q_stats( let tname = table_name.clone(); let uname = username.clone(); let is_group2 = is_group; - let names_map = names.map.clone(); - let group_nicknames2 = group_nicknames.clone(); // 用 SQL GROUP BY 在数据库侧聚合,避免把全量消息内容加载进内存 let result: (i64, HashMap, HashMap, [i64; 24]) = @@ -1883,8 +1905,7 @@ pub async fn q_stats( for (id, cnt) in rows.flatten() { if let Some(u) = id2u.get(&id) { if u != &uname { - let name = sender_display(u, "", &names_map, &group_nicknames2); - *sender_c.entry(name).or_insert(0) += cnt; + *sender_c.entry(u.clone()).or_insert(0) += cnt; } } } @@ -1909,11 +1930,7 @@ pub async fn q_stats( by_type.sort_by_key(|v| std::cmp::Reverse(v["count"].as_i64().unwrap_or(0))); // 发言排行,Top 10 - let mut top_senders: Vec = sender_counts.iter() - .map(|(s, c)| json!({ "sender": s, "count": c })) - .collect(); - top_senders.sort_by_key(|v| std::cmp::Reverse(v["count"].as_i64().unwrap_or(0))); - top_senders.truncate(10); + let top_senders = group_top_senders(&sender_counts, &names.map, &group_nicknames, 10); // 24小时分布 let by_hour: Vec = hour_counts.iter().enumerate() @@ -2487,6 +2504,30 @@ mod group_nickname_tests { ); assert!(!nicknames.contains_key("candidate_name")); } + + #[test] + fn group_top_senders_keeps_duplicate_display_names_separate() { + let sender_counts = HashMap::from([ + ("wxid_alice".to_string(), 7), + ("wxid_bob".to_string(), 3), + ]); + let names = HashMap::from([ + ("wxid_alice".to_string(), "Alice Contact".to_string()), + ("wxid_bob".to_string(), "Bob Contact".to_string()), + ]); + let group_nicknames = HashMap::from([ + ("wxid_alice".to_string(), "同名".to_string()), + ("wxid_bob".to_string(), "同名".to_string()), + ]); + + let top = group_top_senders(&sender_counts, &names, &group_nicknames, 10); + + assert_eq!(top.len(), 2); + assert_eq!(top[0]["sender"].as_str(), Some("同名")); + assert_eq!(top[0]["count"].as_i64(), Some(7)); + assert_eq!(top[1]["sender"].as_str(), Some("同名")); + assert_eq!(top[1]["count"].as_i64(), Some(3)); + } } #[cfg(test)]