From f32a8d990a2121a4e37674556090a66c7a4eb7a6 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 8 Jun 2026 23:27:13 +0800 Subject: [PATCH] Fix ops weekly leaderboard ranking --- .../ops/__tests__/opsUsersLeaderboard.test.ts | 27 ++++++++++++++ .../components/ops/users/UsersPageClient.tsx | 13 +++++-- src/database/db_manager.py | 36 ++++++++++--------- tests/test_weekly_points_display.py | 29 +++++++++++++++ 4 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 frontend/components/ops/__tests__/opsUsersLeaderboard.test.ts diff --git a/frontend/components/ops/__tests__/opsUsersLeaderboard.test.ts b/frontend/components/ops/__tests__/opsUsersLeaderboard.test.ts new file mode 100644 index 00000000..ed347380 --- /dev/null +++ b/frontend/components/ops/__tests__/opsUsersLeaderboard.test.ts @@ -0,0 +1,27 @@ +import fs from "node:fs"; +import path from "node:path"; + +function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(message); +} + +export function runTests() { + const projectRoot = process.cwd(); + const usersPageSource = fs.readFileSync( + path.join(projectRoot, "components", "ops", "users", "UsersPageClient.tsx"), + "utf8", + ); + + assert( + usersPageSource.includes("leaderboardName") && + usersPageSource.includes("String(entry.username || \"\").trim()") && + usersPageSource.includes("TG${entry.telegram_id}"), + "ops users leaderboard must fall back when username is blank", + ); + assert( + usersPageSource.includes("本周暂无积分记录") && + usersPageSource.includes("本周积分") && + !usersPageSource.includes("entry.username ?? `TG${entry.telegram_id}`"), + "ops users leaderboard must show weekly points clearly and avoid zero-point fake rankings", + ); +} diff --git a/frontend/components/ops/users/UsersPageClient.tsx b/frontend/components/ops/users/UsersPageClient.tsx index 32665636..28a73b00 100644 --- a/frontend/components/ops/users/UsersPageClient.tsx +++ b/frontend/components/ops/users/UsersPageClient.tsx @@ -7,6 +7,13 @@ import { Button } from "@/components/ui/button"; import { opsApi } from "@/lib/ops-api"; import type { OpsUser, LeaderboardEntry } from "@/types/ops"; +function leaderboardName(entry: LeaderboardEntry) { + const username = String(entry.username || "").trim(); + if (username) return username; + if (entry.telegram_id != null) return `TG${entry.telegram_id}`; + return "未知用户"; +} + export function UsersPageClient() { const [query, setQuery] = useState(""); const [searching, setSearching] = useState(false); @@ -130,16 +137,16 @@ export function UsersPageClient() { 本周排行榜 Top {leaderboard.length} {leaderboard.length === 0 ? ( - 无数据 + 本周暂无积分记录 ) : (
    {leaderboard.map((entry, i) => (
  1. #{entry.rank ?? i + 1} - {entry.username ?? `TG${entry.telegram_id}`} + {leaderboardName(entry)} - {entry.weekly_points ?? 0} 分 + 本周积分 {entry.weekly_points ?? 0} 分
  2. ))}
diff --git a/src/database/db_manager.py b/src/database/db_manager.py index 9e0e4508..9a87f190 100644 --- a/src/database/db_manager.py +++ b/src/database/db_manager.py @@ -2916,22 +2916,26 @@ class DBManager: conn.row_factory = sqlite3.Row cursor = conn.execute( """ - SELECT - username, - u.points AS points, - u.message_count AS message_count, - u.telegram_id AS telegram_id, - COALESCE(a.points, - CASE - WHEN u.weekly_points_week = ? THEN COALESCE(u.weekly_points, 0) - ELSE 0 - END - ) AS weekly_points - FROM users u - LEFT JOIN weekly_points_archive a - ON a.telegram_id = u.telegram_id - AND a.week_key = ? - ORDER BY weekly_points DESC, points DESC, message_count DESC + SELECT * + FROM ( + SELECT + username, + u.points AS points, + u.message_count AS message_count, + u.telegram_id AS telegram_id, + COALESCE(a.points, + CASE + WHEN u.weekly_points_week = ? THEN COALESCE(u.weekly_points, 0) + ELSE 0 + END + ) AS weekly_points + FROM users u + LEFT JOIN weekly_points_archive a + ON a.telegram_id = u.telegram_id + AND a.week_key = ? + ) ranked + WHERE weekly_points > 0 + ORDER BY weekly_points DESC, points DESC, message_count DESC, telegram_id ASC LIMIT ? """, (week_key, week_key, limit), diff --git a/tests/test_weekly_points_display.py b/tests/test_weekly_points_display.py index 5e700a21..88ac8c4e 100644 --- a/tests/test_weekly_points_display.py +++ b/tests/test_weekly_points_display.py @@ -51,3 +51,32 @@ def test_points_rank_display_uses_invite_points_copy(tmp_path): assert "积分获取: 邀请付费用户" in text assert "本周发言积分" not in text assert "今日发言积分" not in text + + +def test_weekly_leaderboard_excludes_zero_weekly_points_and_orders_active_users(tmp_path): + db = DBManager(str(tmp_path / "weekly-leaderboard.db")) + now = datetime.now() + week_key = f"{now.isocalendar()[0]}-W{now.isocalendar()[1]:02d}" + users = [ + (1001, "old_high_total", 10000, 0, week_key), + (1002, "", 100, 8, week_key), + (1003, "active_winner", 200, 12, week_key), + (1004, "last_week_only", 900, 50, "2025-W01"), + ] + for telegram_id, username, points, weekly_points, weekly_week in users: + db.upsert_user(telegram_id, username) + with db._get_connection() as conn: # noqa: SLF001 + conn.execute( + """ + UPDATE users + SET points = ?, message_count = ?, weekly_points = ?, weekly_points_week = ? + WHERE telegram_id = ? + """, + (points, 10, weekly_points, weekly_week, telegram_id), + ) + conn.commit() + + rows = db.get_weekly_leaderboard(limit=10) + + assert [row["telegram_id"] for row in rows] == [1003, 1002] + assert [row["weekly_points"] for row in rows] == [12, 8]