feat(user): support get the user's first scores

This commit is contained in:
MingxuanGame
2025-08-26 16:42:57 +00:00
parent d2e3b86bd1
commit 7ec716d4de
3 changed files with 72 additions and 10 deletions

View File

@@ -2,6 +2,7 @@ from datetime import datetime, timedelta
import json import json
from typing import TYPE_CHECKING, NotRequired, TypedDict from typing import TYPE_CHECKING, NotRequired, TypedDict
from app.config import settings
from app.models.model import UTCBaseModel from app.models.model import UTCBaseModel
from app.models.score import GameMode from app.models.score import GameMode
from app.models.user import Country, Page from app.models.user import Country, Page
@@ -223,7 +224,7 @@ class UserResp(UserBase):
follower_count: int = 0 follower_count: int = 0
friends: list["RelationshipResp"] | None = None friends: list["RelationshipResp"] | None = None
scores_best_count: int = 0 scores_best_count: int = 0
scores_first_count: int = 0 # TODO scores_first_count: int = 0
scores_recent_count: int = 0 scores_recent_count: int = 0
scores_pinned_count: int = 0 scores_pinned_count: int = 0
beatmap_playcounts_count: int = 0 beatmap_playcounts_count: int = 0
@@ -261,7 +262,7 @@ class UserResp(UserBase):
from .favourite_beatmapset import FavouriteBeatmapset from .favourite_beatmapset import FavouriteBeatmapset
from .pp_best_score import PPBestScore from .pp_best_score import PPBestScore
from .relationship import Relationship, RelationshipResp, RelationshipType from .relationship import Relationship, RelationshipResp, RelationshipType
from .score import Score from .score import Score, get_user_first_score_count
ruleset = ruleset or obj.playmode ruleset = ruleset or obj.playmode
@@ -409,6 +410,7 @@ class UserResp(UserBase):
) )
) )
).one() ).one()
u.scores_first_count = await get_user_first_score_count(session, obj.id, ruleset)
u.beatmap_playcounts_count = ( u.beatmap_playcounts_count = (
await session.exec( await session.exec(
select(func.count()) select(func.count())
@@ -421,7 +423,6 @@ class UserResp(UserBase):
# 检查会话验证状态 # 检查会话验证状态
# 如果邮件验证功能被禁用,则始终设置 session_verified 为 true # 如果邮件验证功能被禁用,则始终设置 session_verified 为 true
from app.config import settings
if not settings.enable_email_verification: if not settings.enable_email_verification:
u.session_verified = True u.session_verified = True

View File

@@ -570,6 +570,64 @@ async def get_user_best_score_with_mod_in_beatmap(
).first() ).first()
async def get_user_first_scores(
session: AsyncSession, user_id: int, mode: GameMode, limit: int = 5, offset: int = 0
) -> list[BestScore]:
rownum = (
func.row_number()
.over(
partition_by=(col(BestScore.beatmap_id), col(BestScore.gamemode)),
order_by=col(BestScore.total_score).desc(),
)
.label("rn")
)
# Step 1: Fetch top score_ids in Python
subq = (
select(
col(BestScore.score_id).label("score_id"),
col(BestScore.user_id).label("user_id"),
rownum,
)
.where(col(BestScore.gamemode) == mode)
.subquery()
)
top_ids_stmt = select(subq.c.score_id).where(subq.c.rn == 1, subq.c.user_id == user_id).limit(limit).offset(offset)
top_ids = await session.exec(top_ids_stmt)
top_ids = list(top_ids)
stmt = select(BestScore).where(col(BestScore.score_id).in_(top_ids)).order_by(col(BestScore.total_score).desc())
result = await session.exec(stmt)
return list(result.all())
async def get_user_first_score_count(session: AsyncSession, user_id: int, mode: GameMode) -> int:
rownum = (
func.row_number()
.over(
partition_by=(col(BestScore.beatmap_id), col(BestScore.gamemode)),
order_by=col(BestScore.total_score).desc(),
)
.label("rn")
)
subq = (
select(
col(BestScore.score_id).label("score_id"),
col(BestScore.user_id).label("user_id"),
rownum,
)
.where(col(BestScore.gamemode) == mode)
.subquery()
)
count_stmt = select(func.count()).where(subq.c.rn == 1, subq.c.user_id == user_id)
result = await session.exec(count_stmt)
return result.one()
async def get_user_best_pp_in_beatmap( async def get_user_best_pp_in_beatmap(
session: AsyncSession, session: AsyncSession,
beatmap: int, beatmap: int,

View File

@@ -15,7 +15,7 @@ from app.database import (
from app.database.events import Event from app.database.events import Event
from app.database.lazer_user import SEARCH_INCLUDED from app.database.lazer_user import SEARCH_INCLUDED
from app.database.pp_best_score import PPBestScore from app.database.pp_best_score import PPBestScore
from app.database.score import Score, ScoreResp from app.database.score import Score, ScoreResp, get_user_first_scores
from app.dependencies.database import Database, get_redis from app.dependencies.database import Database, get_redis
from app.dependencies.user import get_current_user from app.dependencies.user import get_current_user
from app.log import logger from app.log import logger
@@ -360,14 +360,17 @@ async def get_user_scores(
where_clause &= Score.ended_at > utcnow() - timedelta(hours=24) where_clause &= Score.ended_at > utcnow() - timedelta(hours=24)
order_by = col(Score.ended_at).desc() order_by = col(Score.ended_at).desc()
elif type == "firsts": elif type == "firsts":
# TODO
where_clause &= false() where_clause &= false()
if type != "firsts":
scores = ( scores = (
await session.exec(select(Score).where(where_clause).order_by(order_by).limit(limit).offset(offset)) await session.exec(select(Score).where(where_clause).order_by(order_by).limit(limit).offset(offset))
).all() ).all()
if not scores: if not scores:
return [] return []
else:
best_scores = await get_user_first_scores(session, db_user.id, gamemode, limit)
scores = [best_score.score for best_score in best_scores]
score_responses = [ score_responses = [
await ScoreResp.from_db( await ScoreResp.from_db(