diff --git a/app/router/beatmap.py b/app/router/beatmap.py index f6b8f1a..697cf0a 100644 --- a/app/router/beatmap.py +++ b/app/router/beatmap.py @@ -32,7 +32,11 @@ async def get_beatmap( beatmap = ( await db.exec( select(Beatmap) - .options(joinedload(Beatmap.beatmapset).selectinload(Beatmapset.beatmaps)) # pyright: ignore[reportArgumentType] + .options( + joinedload(Beatmap.beatmapset).selectinload( # pyright: ignore[reportArgumentType] + Beatmapset.beatmaps # pyright: ignore[reportArgumentType] + ) + ) .where(Beatmap.id == bid) ) ).first() @@ -70,7 +74,11 @@ async def batch_get_beatmaps( await db.exec( select(Beatmap) .options( - joinedload(Beatmap.beatmapset).selectinload(Beatmapset.beatmaps) # pyright: ignore[reportArgumentType] + joinedload( + Beatmap.beatmapset # pyright: ignore[reportArgumentType] + ).selectinload( + Beatmapset.beatmaps # pyright: ignore[reportArgumentType] + ) ) .order_by(col(Beatmap.last_updated).desc()) .limit(50) @@ -81,7 +89,11 @@ async def batch_get_beatmaps( await db.exec( select(Beatmap) .options( - joinedload(Beatmap.beatmapset).selectinload(Beatmapset.beatmaps) # pyright: ignore[reportArgumentType] + joinedload( + Beatmap.beatmapset # pyright: ignore[reportArgumentType] + ).selectinload( + Beatmapset.beatmaps # pyright: ignore[reportArgumentType] + ) ) .where(col(Beatmap.id).in_(b_ids)) .limit(50) @@ -97,9 +109,9 @@ class BeatmapScores(BaseModel): @router.get( - "/beatmaps/{beatmap}/scores", tags=["beatmapset"], response_model=BeatmapScores + "/beatmaps/{beatmap}/scores", tags=["beatmap"], response_model=BeatmapScores ) -async def get_beatmapset_scores( +async def get_beatmap_scores( beatmap: int, legacy_only: bool = Query(None), # TODO:加入对这个参数的查询 mode: str = Query(None), @@ -126,7 +138,9 @@ async def get_beatmapset_scores( .options( joinedload(Score.beatmap) # pyright: ignore[reportArgumentType] .joinedload(Beatmap.beatmapset) # pyright: ignore[reportArgumentType] - .selectinload(Beatmapset.beatmaps) # pyright: ignore[reportArgumentType] + .selectinload( + Beatmapset.beatmaps # pyright: ignore[reportArgumentType] + ) ) .where(Score.beatmap_id == beatmap) .where(Score.user_id == current_user.id) @@ -137,3 +151,89 @@ async def get_beatmapset_scores( scores=[ScoreResp.from_db(score) for score in all_scores], userScore=ScoreResp.from_db(user_score) if user_score else None, ) + + +class BeatmapUserScore(BaseModel): + position: int + score: ScoreResp + + +@router.get( + "/beatmaps/{beatmap}/scores/users/{user}", + tags=["beatmap"], + response_model=BeatmapUserScore, +) +async def get_user_beatmap_score( + beatmap: int, + user: int, + legacy_only: bool = Query(None), + mode: str = Query(None), + mods: str = Query(None), # TODO:添加mods筛选 + current_user: DBUser = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +): + if legacy_only: + raise HTTPException( + status_code=404, detail="This server only contains non-legacy scores" + ) + user_score = ( + await db.exec( + select(Score) + .options( + joinedload(Score.beatmap) # pyright: ignore[reportArgumentType] + .joinedload(Beatmap.beatmapset) # pyright: ignore[reportArgumentType] + .selectinload( + Beatmapset.beatmaps # pyright: ignore[reportArgumentType] + ) + ) + .where(Score.beatmap_id == beatmap) + .where(Score.user_id == user) + .where(Score.gamemode == mode) + .order_by(col(Score.classic_total_score).desc()) + ) + ).first() + + if not user_score: + raise HTTPException( + status_code=404, detail="Cannot find user %s's score on this beatmap" % user + ) + else: + return BeatmapUserScore( + position=user_score.position if user_score.position is not None else 0, + score=ScoreResp.from_db(user_score), + ) + + +@router.get( + "/beatmaps/{beatmap}/scores/users/{user}/all", + tags=["beatmap"], + response_model=list[ScoreResp], +) +async def get_user_all_beatmap_scores( + beatmap: int, + user: int, + legacy_only: bool = Query(None), + ruleset: str = Query(None), + current_user: DBUser = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +): + if legacy_only: + raise HTTPException(status_code=404,detail="This server only contains non-legacy scores") + all_user_scores=( + await db.exec( + select(Score) + .options( + joinedload(Score.beatmap) # pyright: ignore[reportArgumentType] + .joinedload(Beatmap.beatmapset) # pyright: ignore[reportArgumentType] + .selectinload( + Beatmapset.beatmaps # pyright: ignore[reportArgumentType] + ) + ) + .where(Score.gamemode==ruleset) + .where(Score.beatmap_id == beatmap) + .where(Score.user_id == user) + .order_by(col(Score.classic_total_score).desc()) + ) + ).all() + + return [ScoreResp.from_db(score) for score in all_user_scores] diff --git a/test_api.py b/test_api.py index d11ab7b..861b28f 100644 --- a/test_api.py +++ b/test_api.py @@ -124,6 +124,24 @@ def get_beatmap_scores(access_token: str, beatmap_id: int): return None +def get_user_beatmap_score(access_token: str, beatmap_id: int, user_id: int): + """获取玩家成绩""" + url = f"{API_URL}/api/v2/beatmaps/{beatmap_id}/scores/users/{user_id}" + headers = {"Authorization": f"Bearer {access_token}"} + try: + response = requests.get(url, headers=headers) + if response.status_code == 200: + print(f"✅ 成功获取谱面 {beatmap_id} 中用户 {user_id} 的成绩数据") + return response.json() + else: + print(f"❌ 获取谱面成绩失败: {response.status_code}") + print(f"响应内容: {response.text}") + return None + except Exception as e: + print(f"❌ 获取谱面成绩请求失败: {e}") + return None + + def main(): """主测试函数""" print("=== osu! API 模拟服务器测试 ===\n") @@ -180,8 +198,17 @@ def main(): else: print("用户在该谱面没有成绩记录") - # 6. 测试令牌刷新 - print("\n6. 测试令牌刷新...") + # 6. 测试谱面指定用户成绩 + user_score = get_user_beatmap_score(token_data["access_token"], 1, 1) + if user_score: + print(f"用户成绩ID:{user_score['score']['id']}") + print(f"此成绩acc:{user_score['score']['accuracy']}") + print(f"总分:{user_score['score']['classic_total_score']}") + else: + print("该用户在此谱面没有记录") + + # 7. 测试令牌刷新 + print("\n7. 测试令牌刷新...") new_token_data = refresh_token(token_data["refresh_token"]) if new_token_data: print(f"新访问令牌: {new_token_data['access_token']}")