diff --git a/app/router/private/avatar.py b/app/router/private/avatar.py index ec97bd8..4a91b56 100644 --- a/app/router/private/avatar.py +++ b/app/router/private/avatar.py @@ -1,6 +1,7 @@ import hashlib from typing import Annotated +from app.dependencies.cache import UserCacheService from app.dependencies.database import Database from app.dependencies.storage import StorageService from app.dependencies.user import ClientUser @@ -17,6 +18,7 @@ async def upload_avatar( content: Annotated[bytes, File(...)], current_user: ClientUser, storage: StorageService, + cache_service: UserCacheService, ): """上传用户头像 @@ -49,6 +51,8 @@ async def upload_avatar( current_user.avatar_url = url await session.commit() + await cache_service.invalidate_user_cache(current_user.id) + return { "url": url, "filehash": filehash, diff --git a/app/router/private/cover.py b/app/router/private/cover.py index e9eb40f..78dbefb 100644 --- a/app/router/private/cover.py +++ b/app/router/private/cover.py @@ -2,6 +2,7 @@ import hashlib from typing import Annotated from app.database.user import UserProfileCover +from app.dependencies.cache import UserCacheService from app.dependencies.database import Database from app.dependencies.storage import StorageService from app.dependencies.user import ClientUser @@ -18,6 +19,7 @@ async def upload_cover( content: Annotated[bytes, File(...)], current_user: ClientUser, storage: StorageService, + cache_service: UserCacheService, ): """上传用户头图 @@ -50,6 +52,8 @@ async def upload_cover( current_user.cover = UserProfileCover(url=url) await session.commit() + await cache_service.invalidate_user_cache(current_user.id) + return { "url": url, "filehash": filehash, diff --git a/app/router/private/user.py b/app/router/private/user.py index 5c04539..66deb58 100644 --- a/app/router/private/user.py +++ b/app/router/private/user.py @@ -14,7 +14,8 @@ from app.database.user_preference import ( UserListView, UserPreference, ) -from app.dependencies.database import Database, Redis +from app.dependencies.cache import UserCacheService +from app.dependencies.database import Database from app.dependencies.user import ClientUser from app.models.score import GameMode from app.models.user import Page @@ -26,7 +27,6 @@ from app.models.userpage import ( ValidateBBCodeResponse, ) from app.service.bbcode_service import bbcode_service -from app.service.user_cache_service import get_user_cache_service from app.utils import hex_to_hue, utcnow from .router import router @@ -41,6 +41,7 @@ async def user_rename( session: Database, new_name: Annotated[str, Body(..., description="新的用户名")], current_user: ClientUser, + cache_service: UserCacheService, ): """修改用户名 @@ -81,6 +82,9 @@ async def user_rename( } session.add(rename_event) await session.commit() + + await cache_service.invalidate_user_cache(current_user.id) + return None @@ -95,6 +99,7 @@ async def update_userpage( request: UpdateUserpageRequest, session: Database, current_user: ClientUser, + cache_service: UserCacheService, ): """更新用户页面内容""" if await current_user.is_restricted(session): @@ -110,6 +115,8 @@ async def update_userpage( await session.commit() await session.refresh(current_user) + await cache_service.invalidate_user_cache(current_user.id) + # 返回官方格式的响应:只包含html return UpdateUserpageResponse(html=processed_page["html"]) @@ -295,13 +302,11 @@ async def change_user_preference( request: Preferences, session: Database, current_user: ClientUser, - redis: Redis, + cache_service: UserCacheService, ): if await current_user.is_restricted(session): raise HTTPException(403, "Your account is restricted and cannot perform this action.") - cache_service = get_user_cache_service(redis) - await current_user.awaitable_attrs.user_preference user_pref: UserPreference | None = current_user.user_preference if user_pref is None: @@ -355,15 +360,14 @@ async def overwrite_user_preference( request: Preferences, session: Database, current_user: ClientUser, - redis: Redis, + cache_service: UserCacheService, ): if await current_user.is_restricted(session): raise HTTPException(403, "Your account is restricted and cannot perform this action.") await Preferences.clear(current_user, []) - await change_user_preference(request, session, current_user, redis) + await change_user_preference(request, session, current_user, cache_service) - cache_service = get_user_cache_service(redis) await cache_service.invalidate_user_cache(current_user.id) await session.commit() @@ -379,13 +383,12 @@ async def delete_user_preference( session: Database, current_user: ClientUser, fields: list[str], - redis: Redis, + cache_service: UserCacheService, ): if await current_user.is_restricted(session): raise HTTPException(403, "Your account is restricted and cannot perform this action.") await Preferences.clear(current_user, fields) - cache_service = get_user_cache_service(redis) await cache_service.invalidate_user_cache(current_user.id) await session.commit() diff --git a/app/router/v2/beatmapset.py b/app/router/v2/beatmapset.py index 1342e34..3001e49 100644 --- a/app/router/v2/beatmapset.py +++ b/app/router/v2/beatmapset.py @@ -5,7 +5,7 @@ from urllib.parse import parse_qs from app.database import Beatmap, Beatmapset, BeatmapsetResp, FavouriteBeatmapset, User from app.database.beatmapset import SearchBeatmapsetsResp from app.dependencies.beatmap_download import DownloadService -from app.dependencies.cache import BeatmapsetCacheService +from app.dependencies.cache import BeatmapsetCacheService, UserCacheService from app.dependencies.database import Database, Redis, with_db from app.dependencies.fetcher import Fetcher from app.dependencies.geoip import IPAddress, get_geoip_helper @@ -222,6 +222,7 @@ async def download_beatmapset( ) async def favourite_beatmapset( db: Database, + cache_service: UserCacheService, beatmapset_id: Annotated[int, Path(..., description="谱面集 ID")], action: Annotated[ Literal["favourite", "unfavourite"], @@ -247,3 +248,4 @@ async def favourite_beatmapset( else: await db.delete(existing_favourite) await db.commit() + await cache_service.invalidate_user_beatmapsets_cache(current_user.id) diff --git a/app/router/v2/user.py b/app/router/v2/user.py index 13f5fcc..ce3e080 100644 --- a/app/router/v2/user.py +++ b/app/router/v2/user.py @@ -17,6 +17,7 @@ from app.database.events import Event from app.database.score import LegacyScoreResp, Score, ScoreResp, get_user_first_scores from app.database.user import ALL_INCLUDED, SEARCH_INCLUDED from app.dependencies.api_version import APIVersion +from app.dependencies.cache import UserCacheService from app.dependencies.database import Database, get_redis from app.dependencies.user import get_current_user, get_optional_user from app.helpers.asset_proxy_helper import asset_proxy_response @@ -406,15 +407,13 @@ async def get_user_info( async def get_user_beatmapsets( session: Database, background_task: BackgroundTasks, + cache_service: UserCacheService, user_id: Annotated[int, Path(description="用户 ID")], type: Annotated[BeatmapsetType, Path(description="谱面集类型")], current_user: Annotated[User, Security(get_current_user, scopes=["public"])], limit: Annotated[int, Query(ge=1, le=1000, description="返回条数 (1-1000)")] = 100, offset: Annotated[int, Query(ge=0, description="偏移量")] = 0, ): - redis = get_redis() - cache_service = get_user_cache_service(redis) - # 先尝试从缓存获取 cached_result = await cache_service.get_user_beatmapsets_from_cache(user_id, type.value, limit, offset) if cached_result is not None: diff --git a/app/service/user_cache_service.py b/app/service/user_cache_service.py index 72ea979..3da0ab3 100644 --- a/app/service/user_cache_service.py +++ b/app/service/user_cache_service.py @@ -279,6 +279,18 @@ class UserCacheService: except Exception as e: logger.error(f"Error invalidating user scores cache: {e}") + async def invalidate_user_beatmapsets_cache(self, user_id: int): + """使用户谱面集缓存失效""" + try: + # 删除用户谱面集相关缓存 + pattern = f"user:{user_id}:beatmapsets:*" + keys = await self.redis.keys(pattern) + if keys: + await self.redis.delete(*keys) + logger.info(f"Invalidated {len(keys)} beatmapset cache entries for user {user_id}") + except Exception as e: + logger.error(f"Error invalidating user beatmapsets cache: {e}") + async def preload_user_cache(self, session: AsyncSession, user_ids: list[int]): """预加载用户缓存""" if self._refreshing: