# -*- coding: utf-8 -*- """基础设施层 · Redis 异步封装(缓存 / 限流 / 分布式锁)。""" from __future__ import annotations import asyncio from typing import Any, Optional from redis.asyncio import Redis from .. import config class Cache: """redis.asyncio 封装:连接池 / get / set / incr / 分布式锁(SET NX EX)。""" def __init__(self, url: str | None = None) -> None: self._redis: Optional[Redis] = None self._url = url or config.REDIS_URL async def connect(self) -> None: if self._redis is None: self._redis = Redis.from_url(self._url, decode_responses=True) async def close(self) -> None: if self._redis is not None: await self._redis.aclose() self._redis = None async def get(self, key: str) -> str | None: if self._redis is None: return None return await self._redis.get(key) async def set(self, key: str, value: str, ttl: int | None = None) -> None: if self._redis is None: return await self._redis.set(key, value, ex=ttl) async def delete(self, key: str) -> None: if self._redis is None: return await self._redis.delete(key) async def incr(self, key: str) -> int: if self._redis is None: return 0 return int(await self._redis.incr(key)) async def lock(self, key: str, ttl: int = 30) -> bool: """分布式锁:SET key token NX EX ttl(成功拿到锁返回 True)。""" if self._redis is None: return True # 无 Redis 时降级为总是通过(单机部署) token = f"{asyncio.get_event_loop().time()}" ok = await self._redis.set(key, token, nx=True, ex=ttl) return bool(ok) async def unlock(self, key: str) -> None: if self._redis is None: return await self._redis.delete(key)