"""Cache management module""" import json from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, Optional, Union from .constants import AWS_DIR DEFAULT_CACHE_DIR = AWS_DIR / '.cache' class CacheManager: """缓存管理器""" def __init__(self, cache_dir: Optional[Union[str, Path]] = None, default_expiry_hours: int = 24): """初始化缓存管理器 Args: cache_dir: 缓存目录路径 default_expiry_hours: 默认过期时间(小时) """ resolved_dir = Path(cache_dir) if cache_dir else DEFAULT_CACHE_DIR self.cache_dir = resolved_dir self.cache_dir.mkdir(parents=True, exist_ok=True) self.default_expiry_hours = default_expiry_hours def get(self, key: str) -> Optional[Any]: """获取缓存数据 Args: key: 缓存键 Returns: 缓存的数据,如果不存在或已过期则返回None """ cache_file = self._get_cache_file(key) if not cache_file.exists(): return None try: with open(cache_file, 'r', encoding='utf-8') as f: cache_data = json.load(f) # 检查是否过期 if self._is_expired(cache_data): cache_file.unlink() # 删除过期缓存 return None return cache_data.get('data') except Exception as e: print(f"读取缓存失败 {key}: {e}") return None def set(self, key: str, data: Any, expiry_hours: Optional[int] = None): """设置缓存数据 Args: key: 缓存键 data: 要缓存的数据 expiry_hours: 过期时间(小时),None则使用默认值 """ cache_file = self._get_cache_file(key) expiry = expiry_hours if expiry_hours is not None else self.default_expiry_hours cache_data = { 'data': data, 'timestamp': datetime.now().isoformat(), 'expiry_hours': expiry } try: with open(cache_file, 'w', encoding='utf-8') as f: json.dump(cache_data, f, indent=2, ensure_ascii=False) except Exception as e: print(f"写入缓存失败 {key}: {e}") def delete(self, key: str): """删除缓存 Args: key: 缓存键 """ cache_file = self._get_cache_file(key) if cache_file.exists(): cache_file.unlink() def clear_all(self): """清除所有缓存""" for cache_file in self.cache_dir.glob('*.json'): cache_file.unlink() print(f"✓ 已清除所有缓存") def clear_expired(self): """清除过期缓存""" count = 0 for cache_file in self.cache_dir.glob('*.json'): try: with open(cache_file, 'r', encoding='utf-8') as f: cache_data = json.load(f) if self._is_expired(cache_data): cache_file.unlink() count += 1 except Exception: # 无法读取的缓存文件也删除 cache_file.unlink() count += 1 if count > 0: print(f"✓ 清除了 {count} 个过期缓存") def _get_cache_file(self, key: str) -> Path: """获取缓存文件路径 Args: key: 缓存键 Returns: 缓存文件路径 """ # 将key转换为安全的文件名 safe_key = key.replace('/', '_').replace('\\', '_').replace(':', '_') return self.cache_dir / f"{safe_key}.json" def _is_expired(self, cache_data: Dict) -> bool: """检查缓存是否过期 Args: cache_data: 缓存数据字典 Returns: 是否过期 """ try: timestamp = datetime.fromisoformat(cache_data['timestamp']) expiry_hours = cache_data.get('expiry_hours', self.default_expiry_hours) expiry_time = timestamp + timedelta(hours=expiry_hours) return datetime.now() > expiry_time except Exception: # 无法解析时间戳,认为已过期 return True def get_cache_info(self, key: str) -> Optional[Dict]: """获取缓存信息 Args: key: 缓存键 Returns: 缓存信息字典,包含timestamp和expiry_hours """ cache_file = self._get_cache_file(key) if not cache_file.exists(): return None try: with open(cache_file, 'r', encoding='utf-8') as f: cache_data = json.load(f) return { 'timestamp': cache_data.get('timestamp'), 'expiry_hours': cache_data.get('expiry_hours'), 'is_expired': self._is_expired(cache_data), 'file': str(cache_file) } except Exception as e: return None def list_caches(self) -> Dict[str, Dict]: """列出所有缓存 Returns: 缓存信息字典 """ caches = {} for cache_file in self.cache_dir.glob('*.json'): key = cache_file.stem info = self.get_cache_info(key) if info: caches[key] = info return caches