Fix ops weekly leaderboard ranking
This commit is contained in:
@@ -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",
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,6 +7,13 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { opsApi } from "@/lib/ops-api";
|
import { opsApi } from "@/lib/ops-api";
|
||||||
import type { OpsUser, LeaderboardEntry } from "@/types/ops";
|
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() {
|
export function UsersPageClient() {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
@@ -130,16 +137,16 @@ export function UsersPageClient() {
|
|||||||
<CardHeader><CardTitle>本周排行榜 Top {leaderboard.length}</CardTitle></CardHeader>
|
<CardHeader><CardTitle>本周排行榜 Top {leaderboard.length}</CardTitle></CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{leaderboard.length === 0 ? (
|
{leaderboard.length === 0 ? (
|
||||||
<span className="text-slate-500 text-sm">无数据</span>
|
<span className="text-slate-500 text-sm">本周暂无积分记录</span>
|
||||||
) : (
|
) : (
|
||||||
<ol className="space-y-1">
|
<ol className="space-y-1">
|
||||||
{leaderboard.map((entry, i) => (
|
{leaderboard.map((entry, i) => (
|
||||||
<li key={entry.telegram_id || i} className="flex justify-between text-sm py-1.5 border-b border-white/5">
|
<li key={entry.telegram_id || i} className="flex justify-between text-sm py-1.5 border-b border-white/5">
|
||||||
<span>
|
<span>
|
||||||
<span className="text-slate-500 w-6 inline-block">#{entry.rank ?? i + 1}</span>
|
<span className="text-slate-500 w-6 inline-block">#{entry.rank ?? i + 1}</span>
|
||||||
<span className="text-white">{entry.username ?? `TG${entry.telegram_id}`}</span>
|
<span className="text-white">{leaderboardName(entry)}</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="text-cyan-400 font-medium">{entry.weekly_points ?? 0} 分</span>
|
<span className="text-cyan-400 font-medium">本周积分 {entry.weekly_points ?? 0} 分</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
+20
-16
@@ -2916,22 +2916,26 @@ class DBManager:
|
|||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
cursor = conn.execute(
|
cursor = conn.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT *
|
||||||
username,
|
FROM (
|
||||||
u.points AS points,
|
SELECT
|
||||||
u.message_count AS message_count,
|
username,
|
||||||
u.telegram_id AS telegram_id,
|
u.points AS points,
|
||||||
COALESCE(a.points,
|
u.message_count AS message_count,
|
||||||
CASE
|
u.telegram_id AS telegram_id,
|
||||||
WHEN u.weekly_points_week = ? THEN COALESCE(u.weekly_points, 0)
|
COALESCE(a.points,
|
||||||
ELSE 0
|
CASE
|
||||||
END
|
WHEN u.weekly_points_week = ? THEN COALESCE(u.weekly_points, 0)
|
||||||
) AS weekly_points
|
ELSE 0
|
||||||
FROM users u
|
END
|
||||||
LEFT JOIN weekly_points_archive a
|
) AS weekly_points
|
||||||
ON a.telegram_id = u.telegram_id
|
FROM users u
|
||||||
AND a.week_key = ?
|
LEFT JOIN weekly_points_archive a
|
||||||
ORDER BY weekly_points DESC, points DESC, message_count DESC
|
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 ?
|
LIMIT ?
|
||||||
""",
|
""",
|
||||||
(week_key, week_key, limit),
|
(week_key, week_key, limit),
|
||||||
|
|||||||
@@ -51,3 +51,32 @@ def test_points_rank_display_uses_invite_points_copy(tmp_path):
|
|||||||
assert "积分获取: <code>邀请付费用户</code>" in text
|
assert "积分获取: <code>邀请付费用户</code>" in text
|
||||||
assert "本周发言积分" not in text
|
assert "本周发言积分" not 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]
|
||||||
|
|||||||
Reference in New Issue
Block a user