From 7510b4fae1268c0031c71fad9b4066485549ad20 Mon Sep 17 00:00:00 2001 From: MingxuanGame Date: Mon, 18 Aug 2025 09:48:46 +0000 Subject: [PATCH] fix(score): return user score & remove duplicated scores --- app/database/score.py | 49 +++++++++++++++++++++++++++++------------- app/router/v1/score.py | 2 +- app/router/v2/score.py | 16 ++++++++++---- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/app/database/score.py b/app/database/score.py index a445dfe..5725e60 100644 --- a/app/database/score.py +++ b/app/database/score.py @@ -2,6 +2,7 @@ from collections.abc import Sequence from datetime import UTC, date, datetime import json import math +import sys from typing import TYPE_CHECKING, Any from app.calculator import ( @@ -328,22 +329,39 @@ async def get_leaderboard( mods: list[str] | None = None, user: User | None = None, limit: int = 50, -) -> tuple[list[Score], Score | None]: +) -> tuple[list[Score], Score | None, int]: mods = mods or [] mode = mode.to_special_mode(mods) wheres = await _score_where(type, beatmap, mode, mods, user) if wheres is None: - return [], None - query = ( - select(BestScore) - .where(*wheres) - .limit(limit) - .order_by(col(BestScore.total_score).desc()) - ) - if mods: - query = query.params(w=json.dumps(mods)) - scores = [s.score for s in await session.exec(query)] + return [], None, 0 + count = (await session.exec(select(func.count()).where(*wheres))).one() + scores: dict[int, Score] = {} + max_score = sys.maxsize + while limit > 0: + query = ( + select(BestScore) + .where(*wheres, BestScore.total_score < max_score) + .limit(limit) + .order_by(col(BestScore.total_score).desc()) + ) + if mods: + query = query.params(w=json.dumps(mods)) + extra_need = 0 + for s in await session.exec(query): + if s.user_id in scores: + extra_need += 1 + count -= 1 + if s.total_score > scores[s.user_id].total_score: + scores[s.user_id] = s.score + else: + scores[s.user_id] = s.score + if max_score > s.total_score: + max_score = s.total_score + limit = extra_need + + result_scores = sorted(scores.values(), key=lambda u: u.total_score, reverse=True) user_score = None if user: self_query = ( @@ -366,9 +384,9 @@ async def get_leaderboard( user_bs = (await session.exec(self_query)).first() if user_bs: user_score = user_bs.score - if user_score and user_score not in scores: - scores.append(user_score) - return scores, user_score + if user_score and user_score not in result_scores: + result_scores.append(user_score) + return result_scores, user_score, count async def get_score_position_by_user( @@ -719,7 +737,8 @@ async def process_user( statistics.hit_accuracy = acc_sum if add_to_db: session.add(mouthly_playcount) - await process_beatmap_playcount(session, user.id, score.beatmap_id) + with session.no_autoflush: + await process_beatmap_playcount(session, user.id, score.beatmap_id) await session.commit() await session.refresh(user) diff --git a/app/router/v1/score.py b/app/router/v1/score.py index 302a1a2..775b5f8 100644 --- a/app/router/v1/score.py +++ b/app/router/v1/score.py @@ -167,7 +167,7 @@ async def get_scores( ) ).all() else: - scores, _ = await get_leaderboard( + scores, _, _ = await get_leaderboard( session, beatmap_id, GameMode.from_int_extra(ruleset_id), diff --git a/app/router/v2/score.py b/app/router/v2/score.py index 6f9bb5e..c57bea6 100644 --- a/app/router/v2/score.py +++ b/app/router/v2/score.py @@ -169,7 +169,8 @@ async def submit_score( class BeatmapScores(BaseModel): scores: list[ScoreResp] - userScore: ScoreResp | None = None + user_score: BeatmapUserScore | None = None + score_count: int = 0 @router.get( @@ -201,14 +202,21 @@ async def get_beatmap_scores( status_code=404, detail="this server only contains lazer scores" ) - all_scores, user_score = await get_leaderboard( + all_scores, user_score, count = await get_leaderboard( db, beatmap_id, mode, type=type, user=current_user, limit=limit, mods=mods ) - return BeatmapScores( + user_score_resp = await ScoreResp.from_db(db, user_score) if user_score else None + resp = BeatmapScores( scores=[await ScoreResp.from_db(db, score) for score in all_scores], - userScore=await ScoreResp.from_db(db, user_score) if user_score else None, + user_score=BeatmapUserScore( + score=user_score_resp, position=user_score_resp.rank_global or 0 + ) + if user_score_resp + else None, + score_count=count, ) + return resp class BeatmapUserScore(BaseModel):