#!/usr/bin/env python # -*- coding: utf-8 -*- """ 缓存管理模块 提供Redis缓存连接和操作功能 """ import logging import json from typing import Any, Optional, Union, Dict, List import redis from config.database_config import CacheConfig from utils.logger import get_logger # 获取日志记录器 logger = get_logger("data.cache") class RedisClient: """Redis客户端,封装常用的Redis操作""" _instance = None _redis_client = None def __new__(cls, *args, **kwargs): """单例模式,确保只有一个Redis连接""" if cls._instance is None: cls._instance = super(RedisClient, cls).__new__(cls) return cls._instance def __init__(self): """初始化Redis连接""" if self._redis_client is None: try: self._redis_client = redis.Redis( **CacheConfig.get_redis_connection_args() ) logger.info(f"Redis连接已建立:{CacheConfig.REDIS_HOST}:{CacheConfig.REDIS_PORT}/{CacheConfig.REDIS_DB}") except Exception as e: logger.error(f"Redis连接失败:{str(e)}") self._redis_client = None def get_client(self) -> redis.Redis: """获取Redis客户端""" return self._redis_client def add_prefix(self, key: str) -> str: """添加键前缀""" return f"{CacheConfig.REDIS_PREFIX}{key}" def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool: """ 设置缓存 Args: key: 缓存键 value: 缓存值,如果不是字符串会尝试JSON序列化 expire: 过期时间(秒) Returns: bool: 操作是否成功 """ try: if self._redis_client is None: return False # 添加前缀 prefixed_key = self.add_prefix(key) # 处理非字符串值 if not isinstance(value, (str, bytes, int, float)): value = json.dumps(value, ensure_ascii=False) # 设置缓存 self._redis_client.set(prefixed_key, value) # 设置过期时间 if expire is not None: self._redis_client.expire(prefixed_key, expire) return True except Exception as e: logger.error(f"设置缓存失败:{str(e)}") return False def get(self, key: str, default: Any = None) -> Any: """ 获取缓存 Args: key: 缓存键 default: 默认值(缓存不存在时返回) Returns: Any: 缓存值或默认值 """ try: if self._redis_client is None: return default # 添加前缀 prefixed_key = self.add_prefix(key) # 获取缓存 value = self._redis_client.get(prefixed_key) if value is None: return default # 尝试JSON反序列化 try: return json.loads(value) except (TypeError, json.JSONDecodeError): return value except Exception as e: logger.error(f"获取缓存失败:{str(e)}") return default def delete(self, key: str) -> bool: """ 删除缓存 Args: key: 缓存键 Returns: bool: 操作是否成功 """ try: if self._redis_client is None: return False # 添加前缀 prefixed_key = self.add_prefix(key) # 删除缓存 return bool(self._redis_client.delete(prefixed_key)) except Exception as e: logger.error(f"删除缓存失败:{str(e)}") return False def exists(self, key: str) -> bool: """ 检查缓存是否存在 Args: key: 缓存键 Returns: bool: 缓存是否存在 """ try: if self._redis_client is None: return False # 添加前缀 prefixed_key = self.add_prefix(key) # 检查缓存 return bool(self._redis_client.exists(prefixed_key)) except Exception as e: logger.error(f"检查缓存失败:{str(e)}") return False def expire(self, key: str, seconds: int) -> bool: """ 设置缓存过期时间 Args: key: 缓存键 seconds: 过期时间(秒) Returns: bool: 操作是否成功 """ try: if self._redis_client is None: return False # 添加前缀 prefixed_key = self.add_prefix(key) # 设置过期时间 return bool(self._redis_client.expire(prefixed_key, seconds)) except Exception as e: logger.error(f"设置缓存过期时间失败:{str(e)}") return False def ttl(self, key: str) -> int: """ 获取缓存剩余过期时间 Args: key: 缓存键 Returns: int: 剩余过期时间(秒),-1表示永不过期,-2表示键不存在 """ try: if self._redis_client is None: return -2 # 添加前缀 prefixed_key = self.add_prefix(key) # 获取过期时间 return self._redis_client.ttl(prefixed_key) except Exception as e: logger.error(f"获取缓存过期时间失败:{str(e)}") return -2 def hset(self, key: str, field: str, value: Any) -> bool: """ 设置哈希表字段 Args: key: 缓存键 field: 字段名 value: 字段值 Returns: bool: 操作是否成功 """ try: if self._redis_client is None: return False # 添加前缀 prefixed_key = self.add_prefix(key) # 处理非字符串值 if not isinstance(value, (str, bytes, int, float)): value = json.dumps(value, ensure_ascii=False) # 设置哈希表字段 self._redis_client.hset(prefixed_key, field, value) return True except Exception as e: logger.error(f"设置哈希表字段失败:{str(e)}") return False def hget(self, key: str, field: str, default: Any = None) -> Any: """ 获取哈希表字段 Args: key: 缓存键 field: 字段名 default: 默认值(字段不存在时返回) Returns: Any: 字段值或默认值 """ try: if self._redis_client is None: return default # 添加前缀 prefixed_key = self.add_prefix(key) # 获取哈希表字段 value = self._redis_client.hget(prefixed_key, field) if value is None: return default # 尝试JSON反序列化 try: return json.loads(value) except (TypeError, json.JSONDecodeError): return value except Exception as e: logger.error(f"获取哈希表字段失败:{str(e)}") return default # 导出Redis客户端单例 redis_client = RedisClient() def get_cache(): """获取Redis客户端""" return redis_client