Files
Aether/src/utils/cache_decorator.py

76 lines
2.6 KiB
Python
Raw Normal View History

2025-12-10 20:52:44 +08:00
"""缓存装饰器工具"""
import functools
import json
from typing import Any, Callable, Optional
from src.core.logger import logger
from src.clients.redis_client import get_redis_client_sync
def cache_result(key_prefix: str, ttl: int = 60, user_specific: bool = True) -> Callable:
"""
缓存函数结果的装饰器
Args:
key_prefix: 缓存键前缀
ttl: 缓存过期时间
user_specific: 是否针对用户缓存 context.user.id 获取
"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
async def wrapper(*args, **kwargs) -> Any:
redis_client = get_redis_client_sync()
# 如果 Redis 不可用,直接执行原函数
if redis_client is None:
return await func(*args, **kwargs)
# 构建缓存键
try:
# 从 args 中获取 context通常是第一个参数
context = args[0] if args else None
if user_specific and context and hasattr(context, "user") and context.user:
cache_key = f"{key_prefix}:user:{context.user.id}"
else:
cache_key = f"{key_prefix}:global"
# 如果有额外的参数(如 days添加到键中
if hasattr(args[0], "__dict__"):
# 如果是 dataclass 或对象,获取其属性
for attr_name in ["days", "limit"]:
if hasattr(args[0], attr_name):
attr_value = getattr(args[0], attr_name)
cache_key += f":{attr_name}:{attr_value}"
# 尝试从缓存获取
cached = await redis_client.get(cache_key)
if cached:
logger.debug(f"缓存命中: {cache_key}")
return json.loads(cached)
# 执行原函数
result = await func(*args, **kwargs)
# 保存到缓存
try:
await redis_client.setex(
cache_key, ttl, json.dumps(result, ensure_ascii=False, default=str)
)
logger.debug(f"缓存已保存: {cache_key}, TTL: {ttl}s")
except Exception as e:
logger.warning(f"保存缓存失败: {e}")
return result
except Exception as e:
logger.warning(f"缓存处理出错: {e}, 直接执行原函数")
return await func(*args, **kwargs)
return wrapper
return decorator