From 27f345cb754e30f8d8937f350492594cccaf50a3 Mon Sep 17 00:00:00 2001 From: MingxuanGame Date: Sat, 8 Nov 2025 20:03:25 +0000 Subject: [PATCH] feat(fetcher): replace HTTPError with NoBeatmapError for better error handling --- app/fetcher/beatmap_raw.py | 16 +++++++++++----- tools/recalculate.py | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/fetcher/beatmap_raw.py b/app/fetcher/beatmap_raw.py index b358baa..6785863 100644 --- a/app/fetcher/beatmap_raw.py +++ b/app/fetcher/beatmap_raw.py @@ -16,6 +16,12 @@ urls = [ logger = fetcher_logger("BeatmapRawFetcher") +class NoBeatmapError(Exception): + """Beatmap 不存在异常""" + + pass + + class BeatmapRawFetcher(BaseFetcher): def __init__(self, client_id: str = "", client_secret: str = "", **kwargs): # BeatmapRawFetcher 不需要 OAuth,传递空值给父类 @@ -107,12 +113,12 @@ class BeatmapRawFetcher(BaseFetcher): if resp.status_code >= 400: logger.warning(f"Beatmap {beatmap_id} from {req_url}: HTTP {resp.status_code}") - last_error = HTTPError(f"HTTP {resp.status_code}") + last_error = NoBeatmapError(f"HTTP {resp.status_code}") continue if not resp.text: logger.warning(f"Beatmap {beatmap_id} from {req_url}: empty response") - last_error = HTTPError("Empty response") + last_error = NoBeatmapError("Empty response") continue logger.debug(f"Successfully fetched beatmap {beatmap_id} from {req_url}") @@ -125,9 +131,9 @@ class BeatmapRawFetcher(BaseFetcher): # 所有 URL 都失败了 error_msg = f"Failed to fetch beatmap {beatmap_id} from all sources" - if last_error: - raise HTTPError(error_msg) from last_error - raise HTTPError(error_msg) + if last_error and isinstance(last_error, NoBeatmapError): + raise last_error + raise HTTPError(error_msg) from last_error async def get_or_fetch_beatmap_raw(self, redis: redis.Redis, beatmap_id: int) -> str: from app.config import settings diff --git a/tools/recalculate.py b/tools/recalculate.py index 9773c0d..2f4ffd2 100644 --- a/tools/recalculate.py +++ b/tools/recalculate.py @@ -26,6 +26,7 @@ from app.database.score import Score, calculate_playtime, calculate_user_pp from app.dependencies.database import engine, get_redis from app.dependencies.fetcher import get_fetcher from app.fetcher import Fetcher +from app.fetcher.beatmap_raw import NoBeatmapError from app.log import log from app.models.mods import init_mods, init_ranked_mods, mod_to_save, mods_can_get_pp from app.models.score import GameMode, Rank @@ -667,6 +668,17 @@ async def recalc_score_pp( continue attempts -= 1 await asyncio.sleep(2) + except NoBeatmapError: + logger.warning(f"Beatmap raw not found for beatmap {score.beatmap_id}; cannot calculate pp") + return None + except CalculateError as exc: + attempts -= 1 + logger.warning( + f"Calculation error for score {score.id} on " + f"beatmap {score.beatmap_id}: {exc}; attempts left: {attempts}" + ) + await asyncio.sleep(2) + continue except Exception: logger.exception(f"Failed to calculate pp for score {score.id} on beatmap {score.beatmap_id}") return None @@ -1125,6 +1137,9 @@ async def recalculate_beatmap_rating( else: logger.exception(f"Failed to calculate rating for beatmap {beatmap_id} after multiple attempts") return + except NoBeatmapError: + logger.error(f"Beatmap data for {beatmap_id} not found; cannot calculate rating") + return except Exception: logger.exception(f"Unexpected error calculating rating for beatmap {beatmap_id}") return