修复多人游戏成绩上传报错

This commit is contained in:
咕谷酱
2025-08-22 02:26:39 +08:00
parent cc4ad91b40
commit 71e5f1815e
2 changed files with 51 additions and 17 deletions

View File

@@ -292,6 +292,9 @@ class ScoreResp(ScoreBase):
@classmethod @classmethod
async def from_db(cls, session: AsyncSession, score: Score) -> "ScoreResp": async def from_db(cls, session: AsyncSession, score: Score) -> "ScoreResp":
# 确保 score 对象完全加载,避免懒加载问题
await session.refresh(score)
s = cls.model_validate(score.model_dump()) s = cls.model_validate(score.model_dump())
assert score.id assert score.id
await score.awaitable_attrs.beatmap await score.awaitable_attrs.beatmap

View File

@@ -97,6 +97,9 @@ async def submit_score(
item_id: int | None = None, item_id: int | None = None,
room_id: int | None = None, room_id: int | None = None,
): ):
# 立即获取用户ID避免后续的懒加载问题
user_id = current_user.id
if not info.passed: if not info.passed:
info.rank = Rank.F info.rank = Rank.F
score_token = ( score_token = (
@@ -106,14 +109,14 @@ async def submit_score(
.where(ScoreToken.id == token) .where(ScoreToken.id == token)
) )
).first() ).first()
if not score_token or score_token.user_id != current_user.id: if not score_token or score_token.user_id != user_id:
raise HTTPException(status_code=404, detail="Score token not found") raise HTTPException(status_code=404, detail="Score token not found")
if score_token.score_id: if score_token.score_id:
score = ( score = (
await db.exec( await db.exec(
select(Score).where( select(Score).where(
Score.id == score_token.score_id, Score.id == score_token.score_id,
Score.user_id == current_user.id, Score.user_id == user_id,
) )
) )
).first() ).first()
@@ -169,6 +172,7 @@ async def submit_score(
.where(Score.id == score_id) .where(Score.id == score_id)
)).first() )).first()
assert score is not None assert score is not None
resp = await ScoreResp.from_db(db, score) resp = await ScoreResp.from_db(db, score)
total_users = (await db.exec(select(func.count()).select_from(User))).first() total_users = (await db.exec(select(func.count()).select_from(User))).first()
assert total_users is not None assert total_users is not None
@@ -195,14 +199,15 @@ async def submit_score(
await db.commit() await db.commit()
# 成绩提交后刷新用户缓存 - 移至后台任务避免阻塞主流程 # 成绩提交后刷新用户缓存 - 移至后台任务避免阻塞主流程
# 预先获取用户ID避免在后台任务中触发延迟加载 # 预先获取游戏模式,避免在后台任务中触发延迟加载
user_id = current_user.id score_gamemode = score.gamemode
if user_id is not None: if user_id is not None:
background_task.add_task( background_task.add_task(
_refresh_user_cache_background, _refresh_user_cache_background,
redis, redis,
user_id, user_id,
score.gamemode score_gamemode
) )
background_task.add_task(process_user_achievement, resp.id) background_task.add_task(process_user_achievement, resp.id)
return resp return resp
@@ -414,10 +419,13 @@ async def create_solo_score(
current_user: User = Security(get_client_user), current_user: User = Security(get_client_user),
): ):
assert current_user.id is not None assert current_user.id is not None
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
background_task.add_task(_preload_beatmap_for_pp_calculation, beatmap_id) background_task.add_task(_preload_beatmap_for_pp_calculation, beatmap_id)
async with db: async with db:
score_token = ScoreToken( score_token = ScoreToken(
user_id=current_user.id, user_id=user_id,
beatmap_id=beatmap_id, beatmap_id=beatmap_id,
ruleset_id=GameMode.from_int(ruleset_id), ruleset_id=GameMode.from_int(ruleset_id),
) )
@@ -469,6 +477,9 @@ async def create_playlist_score(
current_user: User = Security(get_client_user), current_user: User = Security(get_client_user),
): ):
assert current_user.id is not None assert current_user.id is not None
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
room = await session.get(Room, room_id) room = await session.get(Room, room_id)
if not room: if not room:
raise HTTPException(status_code=404, detail="Room not found") raise HTTPException(status_code=404, detail="Room not found")
@@ -498,7 +509,7 @@ async def create_playlist_score(
agg = await session.exec( agg = await session.exec(
select(ItemAttemptsCount).where( select(ItemAttemptsCount).where(
ItemAttemptsCount.room_id == room_id, ItemAttemptsCount.room_id == room_id,
ItemAttemptsCount.user_id == current_user.id, ItemAttemptsCount.user_id == user_id,
) )
) )
agg = agg.first() agg = agg.first()
@@ -516,7 +527,7 @@ async def create_playlist_score(
# 这里应该不用验证mod了吧。。。 # 这里应该不用验证mod了吧。。。
background_task.add_task(_preload_beatmap_for_pp_calculation, beatmap_id) background_task.add_task(_preload_beatmap_for_pp_calculation, beatmap_id)
score_token = ScoreToken( score_token = ScoreToken(
user_id=current_user.id, user_id=user_id,
beatmap_id=beatmap_id, beatmap_id=beatmap_id,
ruleset_id=GameMode.from_int(ruleset_id), ruleset_id=GameMode.from_int(ruleset_id),
playlist_item_id=playlist_id, playlist_item_id=playlist_id,
@@ -545,6 +556,10 @@ async def submit_playlist_score(
fetcher: Fetcher = Depends(get_fetcher), fetcher: Fetcher = Depends(get_fetcher),
): ):
assert current_user.id is not None assert current_user.id is not None
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
item = ( item = (
await session.exec( await session.exec(
select(Playlist).where( select(Playlist).where(
@@ -558,8 +573,6 @@ async def submit_playlist_score(
if not room: if not room:
raise HTTPException(status_code=404, detail="Room not found") raise HTTPException(status_code=404, detail="Room not found")
room_category = room.category room_category = room.category
user_id = current_user.id
score_resp = await submit_score( score_resp = await submit_score(
background_task, background_task,
info, info,
@@ -611,6 +624,9 @@ async def index_playlist_scores(
), ),
current_user: User = Security(get_current_user, scopes=["public"]), current_user: User = Security(get_current_user, scopes=["public"]),
): ):
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
room = await session.get(Room, room_id) room = await session.get(Room, room_id)
if not room: if not room:
raise HTTPException(status_code=404, detail="Room not found") raise HTTPException(status_code=404, detail="Room not found")
@@ -637,7 +653,7 @@ async def index_playlist_scores(
score_resp = [await ScoreResp.from_db(session, score.score) for score in scores] score_resp = [await ScoreResp.from_db(session, score.score) for score in scores]
for score in score_resp: for score in score_resp:
score.position = await get_position(room_id, playlist_id, score.id, session) score.position = await get_position(room_id, playlist_id, score.id, session)
if score.user_id == current_user.id: if score.user_id == user_id:
user_score = score user_score = score
if room.category == RoomCategory.DAILY_CHALLENGE: if room.category == RoomCategory.DAILY_CHALLENGE:
@@ -675,6 +691,9 @@ async def show_playlist_score(
current_user: User = Security(get_client_user), current_user: User = Security(get_client_user),
redis: Redis = Depends(get_redis), redis: Redis = Depends(get_redis),
): ):
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
room = await session.get(Room, room_id) room = await session.get(Room, room_id)
if not room: if not room:
raise HTTPException(status_code=404, detail="Room not found") raise HTTPException(status_code=404, detail="Room not found")
@@ -781,11 +800,14 @@ async def pin_score(
score_id: int = Path(description="成绩 ID"), score_id: int = Path(description="成绩 ID"),
current_user: User = Security(get_client_user), current_user: User = Security(get_client_user),
): ):
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
score_record = ( score_record = (
await db.exec( await db.exec(
select(Score).where( select(Score).where(
Score.id == score_id, Score.id == score_id,
Score.user_id == current_user.id, Score.user_id == user_id,
col(Score.passed).is_(True), col(Score.passed).is_(True),
) )
) )
@@ -823,9 +845,12 @@ async def unpin_score(
score_id: int = Path(description="成绩 ID"), score_id: int = Path(description="成绩 ID"),
current_user: User = Security(get_client_user), current_user: User = Security(get_client_user),
): ):
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
score_record = ( score_record = (
await db.exec( await db.exec(
select(Score).where(Score.id == score_id, Score.user_id == current_user.id) select(Score).where(Score.id == score_id, Score.user_id == user_id)
) )
).first() ).first()
if not score_record: if not score_record:
@@ -836,7 +861,7 @@ async def unpin_score(
changed_score = ( changed_score = (
await db.exec( await db.exec(
select(Score).where( select(Score).where(
Score.user_id == current_user.id, Score.user_id == user_id,
Score.pinned_order > score_record.pinned_order, Score.pinned_order > score_record.pinned_order,
Score.gamemode == score_record.gamemode, Score.gamemode == score_record.gamemode,
) )
@@ -864,9 +889,12 @@ async def reorder_score_pin(
before_score_id: int | None = Body(default=None, description="放在该成绩之前"), before_score_id: int | None = Body(default=None, description="放在该成绩之前"),
current_user: User = Security(get_client_user), current_user: User = Security(get_client_user),
): ):
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
score_record = ( score_record = (
await db.exec( await db.exec(
select(Score).where(Score.id == score_id, Score.user_id == current_user.id) select(Score).where(Score.id == score_id, Score.user_id == user_id)
) )
).first() ).first()
if not score_record: if not score_record:
@@ -957,6 +985,9 @@ async def download_score_replay(
current_user: User = Security(get_current_user, scopes=["public"]), current_user: User = Security(get_current_user, scopes=["public"]),
storage_service: StorageService = Depends(get_storage_service), storage_service: StorageService = Depends(get_storage_service),
): ):
# 立即获取用户ID避免懒加载问题
user_id = current_user.id
score = (await db.exec(select(Score).where(Score.id == score_id))).first() score = (await db.exec(select(Score).where(Score.id == score_id))).first()
if not score: if not score:
raise HTTPException(status_code=404, detail="Score not found") raise HTTPException(status_code=404, detail="Score not found")
@@ -967,11 +998,11 @@ async def download_score_replay(
raise HTTPException(status_code=404, detail="Replay file not found") raise HTTPException(status_code=404, detail="Replay file not found")
is_friend = ( is_friend = (
score.user_id == current_user.id score.user_id == user_id
or ( or (
await db.exec( await db.exec(
select(exists()).where( select(exists()).where(
Relationship.user_id == current_user.id, Relationship.user_id == user_id,
Relationship.target_id == score.user_id, Relationship.target_id == score.user_id,
Relationship.type == RelationshipType.FOLLOW, Relationship.type == RelationshipType.FOLLOW,
) )