feat(recommend): add cached_with_empty_check decorator

This commit is contained in:
InfinityPacer
2024-12-22 01:09:06 +08:00
parent de1c2c98d2
commit ec77213ca6
+64 -1
View File
@@ -1,4 +1,8 @@
from typing import Any from functools import wraps
from typing import Any, Callable
from cachetools import TTLCache
from cachetools.keys import hashkey
from app.chain import ChainBase from app.chain import ChainBase
from app.chain.bangumi import BangumiChain from app.chain.bangumi import BangumiChain
@@ -9,6 +13,35 @@ from app.schemas import MediaType
from app.utils.common import log_execution_time from app.utils.common import log_execution_time
from app.utils.singleton import Singleton from app.utils.singleton import Singleton
# 推荐相关的专用缓存
recommend_ttl = 6 * 3600
recommend_cache = TTLCache(maxsize=256, ttl=recommend_ttl)
# 推荐缓存装饰器,避免偶发网络获取数据为空时,页面由于缓存长时间渲染异常问题
def cached_with_empty_check(func: Callable):
"""
缓存装饰器,用于缓存函数的返回结果,仅在结果非空时进行缓存
:param func: 被装饰的函数
:return: 包装后的函数
"""
@wraps(func)
def wrapper(*args, **kwargs):
# 使用 cachetools 缓存,构造缓存键
cache_key = hashkey(*args, **kwargs)
if cache_key in recommend_cache:
return recommend_cache[cache_key]
result = func(*args, **kwargs)
# 如果返回值为空,则不缓存
if result in [None, [], {}]:
return result
recommend_cache[cache_key] = result
return result
return wrapper
class RecommendChain(ChainBase, metaclass=Singleton): class RecommendChain(ChainBase, metaclass=Singleton):
""" """
@@ -21,7 +54,25 @@ class RecommendChain(ChainBase, metaclass=Singleton):
self.doubanchain = DoubanChain() self.doubanchain = DoubanChain()
self.bangumichain = BangumiChain() self.bangumichain = BangumiChain()
def refresh_recommend(self):
"""
刷新推荐
"""
self.tmdb_movies()
self.tmdb_tvs()
self.tmdb_trending()
self.bangumi_calendar()
self.douban_movies()
self.douban_tvs()
self.movie_top250()
self.tv_weekly_chinese()
self.tv_weekly_global()
self.tv_animation()
self.movie_hot()
self.tv_hot()
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tmdb_movies(self, sort_by: str = "popularity.desc", with_genres: str = "", def tmdb_movies(self, sort_by: str = "popularity.desc", with_genres: str = "",
with_original_language: str = "", page: int = 1) -> Any: with_original_language: str = "", page: int = 1) -> Any:
""" """
@@ -35,6 +86,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [movie.to_dict() for movie in movies] if movies else [] return [movie.to_dict() for movie in movies] if movies else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tmdb_tvs(self, sort_by: str = "popularity.desc", with_genres: str = "", def tmdb_tvs(self, sort_by: str = "popularity.desc", with_genres: str = "",
with_original_language: str = "", page: int = 1) -> Any: with_original_language: str = "", page: int = 1) -> Any:
""" """
@@ -48,6 +100,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [tv.to_dict() for tv in tvs] if tvs else [] return [tv.to_dict() for tv in tvs] if tvs else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tmdb_trending(self, page: int = 1) -> Any: def tmdb_trending(self, page: int = 1) -> Any:
""" """
TMDB流行趋势 TMDB流行趋势
@@ -56,6 +109,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [info.to_dict() for info in infos] if infos else [] return [info.to_dict() for info in infos] if infos else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def bangumi_calendar(self, page: int = 1, count: int = 30) -> Any: def bangumi_calendar(self, page: int = 1, count: int = 30) -> Any:
""" """
Bangumi每日放送 Bangumi每日放送
@@ -64,6 +118,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in medias[(page - 1) * count: page * count]] if medias else [] return [media.to_dict() for media in medias[(page - 1) * count: page * count]] if medias else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def movie_showing(self, page: int = 1, count: int = 30) -> Any: def movie_showing(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣正在热映 豆瓣正在热映
@@ -72,6 +127,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in movies] if movies else [] return [media.to_dict() for media in movies] if movies else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def douban_movies(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> Any: def douban_movies(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> Any:
""" """
豆瓣最新电影 豆瓣最新电影
@@ -81,6 +137,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in movies] if movies else [] return [media.to_dict() for media in movies] if movies else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def douban_tvs(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> Any: def douban_tvs(self, sort: str = "R", tags: str = "", page: int = 1, count: int = 30) -> Any:
""" """
豆瓣最新电视剧 豆瓣最新电视剧
@@ -90,6 +147,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in tvs] if tvs else [] return [media.to_dict() for media in tvs] if tvs else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def movie_top250(self, page: int = 1, count: int = 30) -> Any: def movie_top250(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣电影TOP250 豆瓣电影TOP250
@@ -98,6 +156,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in movies] if movies else [] return [media.to_dict() for media in movies] if movies else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tv_weekly_chinese(self, page: int = 1, count: int = 30) -> Any: def tv_weekly_chinese(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣国产剧集榜 豆瓣国产剧集榜
@@ -106,6 +165,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in tvs] if tvs else [] return [media.to_dict() for media in tvs] if tvs else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tv_weekly_global(self, page: int = 1, count: int = 30) -> Any: def tv_weekly_global(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣全球剧集榜 豆瓣全球剧集榜
@@ -114,6 +174,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in tvs] if tvs else [] return [media.to_dict() for media in tvs] if tvs else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tv_animation(self, page: int = 1, count: int = 30) -> Any: def tv_animation(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣热门动漫 豆瓣热门动漫
@@ -122,6 +183,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in tvs] if tvs else [] return [media.to_dict() for media in tvs] if tvs else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def movie_hot(self, page: int = 1, count: int = 30) -> Any: def movie_hot(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣热门电影 豆瓣热门电影
@@ -130,6 +192,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
return [media.to_dict() for media in movies] if movies else [] return [media.to_dict() for media in movies] if movies else []
@log_execution_time(logger=logger) @log_execution_time(logger=logger)
@cached_with_empty_check
def tv_hot(self, page: int = 1, count: int = 30) -> Any: def tv_hot(self, page: int = 1, count: int = 30) -> Any:
""" """
豆瓣热门电视剧 豆瓣热门电视剧