新建 cache.py 缓存工具模块,封装 Redis 读写和容错降级。 订单详情/列表、客户列表、定价规则读取走缓存,写操作后精确清除。 Redis 不可用时自动降级查 DB,不影响正常业务。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
106 lines
2.9 KiB
Python
106 lines
2.9 KiB
Python
"""Redis 缓存工具模块。
|
||
|
||
提供 Redis 连接管理和缓存读写操作,所有操作在 Redis 不可用时静默降级。
|
||
被 OrderService、CustomerService、pricing 路由等调用。
|
||
"""
|
||
|
||
import hashlib
|
||
import json
|
||
import logging
|
||
|
||
import redis
|
||
|
||
from backend.app.core.config import get_settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
_redis_client: redis.Redis | None = None
|
||
|
||
|
||
def get_redis() -> redis.Redis | None:
|
||
"""获取 Redis 连接单例,连接失败时返回 None。"""
|
||
global _redis_client
|
||
if _redis_client is not None:
|
||
return _redis_client
|
||
try:
|
||
settings = get_settings()
|
||
_redis_client = redis.Redis(
|
||
host=settings.redis_host,
|
||
port=settings.redis_port,
|
||
db=settings.redis_db,
|
||
password=settings.redis_password or None,
|
||
decode_responses=True,
|
||
socket_connect_timeout=2,
|
||
socket_timeout=2,
|
||
)
|
||
_redis_client.ping()
|
||
logger.info("Redis 连接成功: %s:%s/%s", settings.redis_host, settings.redis_port, settings.redis_db)
|
||
return _redis_client
|
||
except Exception as exc:
|
||
logger.warning("Redis 连接失败,降级为无缓存模式: %s", exc)
|
||
_redis_client = None
|
||
return None
|
||
|
||
|
||
def cache_get(key: str):
|
||
"""读取缓存,返回反序列化后的 dict/list,未命中或异常返回 None。"""
|
||
try:
|
||
r = get_redis()
|
||
if r is None:
|
||
return None
|
||
raw = r.get(key)
|
||
if raw is None:
|
||
return None
|
||
return json.loads(raw)
|
||
except Exception:
|
||
return None
|
||
|
||
|
||
def cache_set(key: str, data, ttl: int = 60) -> None:
|
||
"""写入缓存,JSON 序列化后存储并设置 TTL(秒)。异常时静默跳过。"""
|
||
try:
|
||
r = get_redis()
|
||
if r is None:
|
||
return
|
||
r.set(key, json.dumps(data, ensure_ascii=False, default=str), ex=ttl)
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def cache_delete(key: str) -> None:
|
||
"""精确删除单个缓存 key。"""
|
||
try:
|
||
r = get_redis()
|
||
if r is not None:
|
||
r.delete(key)
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def cache_delete_pattern(pattern: str) -> None:
|
||
"""按模式批量删除缓存(SCAN + DELETE),如 "order:list:*"。"""
|
||
try:
|
||
r = get_redis()
|
||
if r is None:
|
||
return
|
||
cursor = 0
|
||
while True:
|
||
cursor, keys = r.scan(cursor=cursor, match=pattern, count=100)
|
||
if keys:
|
||
r.delete(*keys)
|
||
if cursor == 0:
|
||
break
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def make_cache_key(prefix: str, **params) -> str:
|
||
"""生成带参数哈希的缓存 key。
|
||
|
||
例: make_cache_key("order:list", user_id=1, status="draft")
|
||
→ "order:list:a1b2c3d4"
|
||
"""
|
||
raw = json.dumps(params, sort_keys=True, ensure_ascii=False, default=str)
|
||
suffix = hashlib.md5(raw.encode()).hexdigest()[:8]
|
||
return f"{prefix}:{suffix}"
|