Files
DjangoAdmin_Flask_EleVue/utils/redis_client.py
T
DjangoAdmin DevandClaude 15f2a5e097 chore: 基线提交(重构前代码快照)
- git 初始化 + .gitignore 补全(根 .env / logs / instance / __pycache__ 等)
- 修复 utils/.gitignore 误将整个 utils/ 源码层忽略的问题,补入全部工具源码(含 ip2region.xdb)
- 含后端全部代码:application / extensions / utils / config / migrations
- 含前端 ui/(后续整体迁走)
- 不含根 .env(含密钥,本地保留)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 12:12:06 +08:00

312 lines
10 KiB
Python

"""
Redis客户端封装模块
=============================================================
提供Redis操作的统一封装,支持字符串、哈希、列表、集合、有序集合等数据类型。
自动处理序列化/反序列化,业务代码无需关心底层存储格式。
使用方式:
from utils.redis_client import redis
redis.set('key', 'value', expire=3600)
value = redis.get('key')
"""
import json
import pickle
from typing import Any, Union, List, Dict, Tuple
from extensions.redis import redis_client
class RedisClient:
"""
Redis客户端封装类
提供丰富的Redis操作方法,支持字符串、哈希、列表、集合、有序集合等数据类型
"""
def __init__(self, client=None):
"""
初始化Redis客户端
Args:
client: FlaskRedis实例,如果为None则使用全局redis_client
"""
self.client = client or redis_client
# ==================== 基础方法 ====================
def set(self, key: str, value: Any, expire: int = None, nx: bool = False, xx: bool = False) -> bool:
"""设置键值对"""
serialized_value = self._serialize(value)
if nx and xx:
raise ValueError("nx和xx不能同时为True")
if nx:
result = self.client.setnx(key, serialized_value)
if expire and result:
self.client.expire(key, expire)
return result
elif xx:
result = self.client.set(key, serialized_value, xx=True)
if expire and result:
self.client.expire(key, expire)
return result
else:
if expire:
return self.client.setex(key, expire, serialized_value)
else:
return self.client.set(key, serialized_value)
def get(self, key: str, default: Any = None, deserialize: bool = True) -> Any:
"""获取键值"""
data = self.client.get(key)
if data is None:
return default
if deserialize:
return self._deserialize(data)
return data
def delete(self, *keys: str) -> int:
"""删除一个或多个键"""
if not keys:
return 0
return self.client.delete(*keys)
def exists(self, *keys: str) -> int:
"""检查键是否存在"""
if not keys:
return 0
return self.client.exists(*keys)
def expire(self, key: str, seconds: int) -> bool:
"""设置键的过期时间"""
return self.client.expire(key, seconds)
def expireat(self, key: str, timestamp: int) -> bool:
"""设置键在指定时间戳过期"""
return self.client.expireat(key, timestamp)
def ttl(self, key: str) -> int:
"""获取键的剩余过期时间"""
return self.client.ttl(key)
def keys(self, pattern: str = '*') -> List[str]:
"""获取匹配模式的所有键(使用SCAN,避免KEYS阻塞)"""
result = []
cursor = 0
while True:
cursor, batch = self.client.scan(cursor=cursor, match=pattern, count=100)
result.extend(batch)
if cursor == 0:
break
return result
# ==================== 哈希表操作 ====================
def hset(self, name: str, key: str, value: Any) -> int:
"""设置哈希表中的字段值"""
serialized_value = self._serialize(value)
return self.client.hset(name, key, serialized_value)
def hget(self, name: str, key: str, deserialize: bool = True) -> Any:
"""获取哈希表中的字段值"""
data = self.client.hget(name, key)
if data is None:
return None
if deserialize:
return self._deserialize(data)
return data
def hgetall(self, name: str, deserialize: bool = True) -> Dict[str, Any]:
"""获取哈希表中所有字段和值"""
data = self.client.hgetall(name)
if not data:
return {}
if deserialize:
return {k.decode(): self._deserialize(v) for k, v in data.items()}
return {k.decode(): v for k, v in data.items()}
def hdel(self, name: str, *keys: str) -> int:
"""删除哈希表中的字段"""
if not keys:
return 0
return self.client.hdel(name, *keys)
def hincrby(self, name: str, key: str, amount: int = 1) -> int:
"""为哈希表中的字段值增加指定数值"""
return self.client.hincrby(name, key, amount)
# ==================== 列表操作 ====================
def lpush(self, name: str, *values: Any) -> int:
"""从列表左侧插入元素"""
serialized_values = [self._serialize(v) for v in values]
return self.client.lpush(name, *serialized_values)
def rpush(self, name: str, *values: Any) -> int:
"""从列表右侧插入元素"""
serialized_values = [self._serialize(v) for v in values]
return self.client.rpush(name, *serialized_values)
def lpop(self, name: str, deserialize: bool = True) -> Any:
"""从列表左侧弹出元素"""
data = self.client.lpop(name)
if data is None:
return None
if deserialize:
return self._deserialize(data)
return data
def rpop(self, name: str, deserialize: bool = True) -> Any:
"""从列表右侧弹出元素"""
data = self.client.rpop(name)
if data is None:
return None
if deserialize:
return self._deserialize(data)
return data
def lrange(self, name: str, start: int, end: int, deserialize: bool = True) -> List[Any]:
"""获取列表指定范围的元素"""
data = self.client.lrange(name, start, end)
if not data:
return []
if deserialize:
return [self._deserialize(item) for item in data]
return [item for item in data]
def llen(self, name: str) -> int:
"""获取列表长度"""
return self.client.llen(name)
# ==================== 集合操作 ====================
def sadd(self, name: str, *values: Any) -> int:
"""向集合中添加元素"""
serialized_values = [self._serialize(v) for v in values]
return self.client.sadd(name, *serialized_values)
def srem(self, name: str, *values: Any) -> int:
"""从集合中移除元素"""
serialized_values = [self._serialize(v) for v in values]
return self.client.srem(name, *serialized_values)
def smembers(self, name: str, deserialize: bool = True) -> set:
"""获取集合中所有元素"""
data = self.client.smembers(name)
if not data:
return set()
if deserialize:
return {self._deserialize(item) for item in data}
return {item for item in data}
def sismember(self, name: str, value: Any) -> bool:
"""判断元素是否在集合中"""
serialized_value = self._serialize(value)
return self.client.sismember(name, serialized_value)
# ==================== 有序集合操作 ====================
def zadd(self, name: str, mapping: Dict[Any, float]) -> int:
"""向有序集合中添加元素"""
serialized_mapping = {self._serialize(k): v for k, v in mapping.items()}
return self.client.zadd(name, serialized_mapping)
def zrange(self, name: str, start: int, end: int, withscores: bool = False,
deserialize: bool = True) -> Union[List[Any], List[Tuple[Any, float]]]:
"""获取有序集合指定范围的元素(按分数升序)"""
data = self.client.zrange(name, start, end, withscores=withscores)
if not data:
return []
if withscores:
if deserialize:
return [(self._deserialize(item[0]), item[1]) for item in data]
return [(item[0], item[1]) for item in data]
else:
if deserialize:
return [self._deserialize(item) for item in data]
return list(data)
def zrevrange(self, name: str, start: int, end: int, withscores: bool = False,
deserialize: bool = True) -> Union[List[Any], List[Tuple[Any, float]]]:
"""获取有序集合指定范围的元素(按分数降序)"""
data = self.client.zrevrange(name, start, end, withscores=withscores)
if not data:
return []
if withscores:
if deserialize:
return [(self._deserialize(item[0]), item[1]) for item in data]
return [(item[0], item[1]) for item in data]
else:
if deserialize:
return [self._deserialize(item) for item in data]
return list(data)
# ==================== 高级操作 ====================
def pipeline(self):
"""获取管道对象,用于批量执行命令"""
return self.client.pipeline()
def incr(self, key: str, amount: int = 1) -> int:
"""增加键的值(用于计数器)"""
return self.client.incrby(key, amount)
def decr(self, key: str, amount: int = 1) -> int:
"""减少键的值"""
return self.client.decrby(key, amount)
def lock(self, key: str, timeout: int = 10, blocking: bool = True,
blocking_timeout: int = None) -> Any:
"""获取分布式锁"""
return self.client.lock(key, timeout=timeout, blocking=blocking,
blocking_timeout=blocking_timeout)
# ==================== 私有方法 ====================
def _serialize(self, value: Any) -> Union[str, bytes]:
"""序列化值"""
if isinstance(value, str):
return value
elif isinstance(value, (int, float, bool)):
return str(value)
elif isinstance(value, (dict, list, tuple)):
return json.dumps(value, ensure_ascii=False)
else:
return pickle.dumps(value)
def _deserialize(self, value: Union[str, bytes]) -> Any:
"""反序列化值"""
if not value:
return None
if isinstance(value, bytes):
try:
value = value.decode('utf-8')
except UnicodeDecodeError:
try:
return pickle.loads(value)
except Exception:
return value
try:
return json.loads(value)
except (json.JSONDecodeError, TypeError):
return value
# 创建全局Redis客户端实例
redis = RedisClient()