
    ށi                        d Z ddlmZ ddlZddlmZ ddlmZ ddl	m
Z
 ddlmZ  ee          ZdZdZ G d	 d
          ZdS )uW  Redis async client wrapper for permission caching and pub/sub.

Redis 异步客户端封装模块。
主要职责：
1. 用户权限缓存 —— 避免每次请求都重新解析 JWT 构建 ACL 令牌
2. 任务消息发布/订阅 —— 用于 Celery 之外的轻量级任务通知
3. 健康检查 —— 供启动探针和就绪探针使用
    )annotationsN)Any)settings)
get_loggerzzm:perm:user:zzm:tasksc                      e Zd ZdZd"dZed#d            Zed$d	            Zd%d
Z	d&dZ
ddd'dZd(dZd)dZd*dZd+d,dZd-d!ZdS ).RedisClientu   Thin async wrapper around ``redis.asyncio.Redis``.

    对 redis.asyncio 的薄封装，提供权限缓存、Pub/Sub 消息和健康检查接口。
    clientaioredis.RedisreturnNonec                    || _         d S N_r)selfr	   s     9D:\work\zm-rag\backend\app\infrastructure\redis_client.py__init__zRedisClient.__init__!   s        'RedisClient'c                    t          t          dd          }t          j        t          j        d|          } | |          S )u   Build a client from the global application settings.

        连接池大小优先从 settings 读取，未配置则使用默认值 20。
        Pool size is read from settings if available, otherwise defaults to 20.
        redis_max_connections   T)decode_responsesmax_connections)getattrr   aioredisfrom_url	redis_url)clsr   r	   s      r   from_settingszRedisClient.from_settings$   sI     "(,CRHH"!+
 
 

 s6{{r   c                    | j         S r   r   r   s    r   rawzRedisClient.raw5   s	    wr   c                H   K   | j                                          d {V  d S r   )r   acloser"   s    r   closezRedisClient.close9   s0      gnnr   user_idstrdict[str, Any] | Nonec                   K   t            | }| j                            |           d{V }|dS 	 t          j        |          S # t          j        t          f$ r  t                              d|           Y dS w xY w)zRetrieve cached permission data for *user_id*.

        Returns ``None`` on cache miss.  The cached value is expected to be
        a dict with an ``acl_tokens`` key (list of prefixed IDs).
        Nredis_bad_perm_cache)r'   )	_PERM_PREFIXr   getjsonloadsJSONDecodeError	TypeErrorloggerwarning)r   r'   keyr#   s       r   get_user_permissionsz RedisClient.get_user_permissions>   s       (w((GKK$$$$$$$$;4	:c??"$i0 	 	 	NN17NCCC44	s   A 2A=<A=N)ttlpermissionsdict[str, Any]r6   
int | Nonec                  K   t            | }|pt          j        }| j                            |t          j        |d          |           d{V  dS )zECache *permissions* for *user_id* with a TTL (default from settings).Fensure_ascii)exN)r,   r   redis_permissions_ttlr   setr.   dumps)r   r'   r7   r6   r4   s        r   set_user_permissionsz RedisClient.set_user_permissionsN   sf       (w((3X3gkk#tz+EJJJskSSSSSSSSSSSr   c                \   K   | j                             t           |            d{V  dS )z0Remove the cached permissions for a single user.N)r   deleter,   )r   r'   s     r   invalidate_user_permissionsz'RedisClient.invalidate_user_permissionsZ   s=      gnn7g7788888888888r   task_payloadc                   K   | j                             t          t          j        |d                     d{V  t
                              dt          |                                                     dS )z3Publish a task message to the shared Redis channel.Fr;   Nredis_task_published)payload_keys)	r   publish_TASK_CHANNELr.   r@   r2   debuglistkeys)r   rE   s     r   publish_taskzRedisClient.publish_task`   so      goomTZSX-Y-Y-YZZZZZZZZZ+$|?P?P?R?R:S:STTTTTr   aioredis.client.PubSubc                |   K   | j                                         }|                    t                     d{V  |S )u~  返回已订阅任务频道的 Pub/Sub 对象。
        Return an async pub/sub object subscribed to the task channel.

        Usage::

            pubsub = await redis_client.subscribe_tasks()
            async for message in pubsub.listen():
                if message["type"] == "message":
                    data = json.loads(message["data"])
                    ...
        N)r   pubsub	subscriberJ   )r   rQ   s     r   subscribe_taskszRedisClient.subscribe_taskse   sD       !!}---------r      nametimeoutintc                :    | j                             ||          S )u  返回一个 Redis 分布式锁（异步上下文管理器），用于保护并发写入关键区。

        Return a Redis distributed lock (async context manager) for protecting
        critical sections against concurrent writes.

        调用方式: async with redis_client.lock("my_lock", timeout=30): ...
        注意: 这是同步方法，底层 raw.lock() 本身返回异步上下文管理器，无需 await。
        )rV   )r   lock)r   rU   rV   s      r   rY   zRedisClient.lockw   s     w||D'|222r   boolc                h   K   	 | j                                          d{V S # t          $ r Y dS w xY w)z1Return ``True`` if the Redis server is reachable.NF)r   ping	Exceptionr"   s    r   r\   zRedisClient.ping   sO      	''''''' 	 	 	55	s   # 
11)r	   r
   r   r   )r   r   )r   r
   )r   r   )r'   r(   r   r)   )r'   r(   r7   r8   r6   r9   r   r   )r'   r(   r   r   )rE   r8   r   r   )r   rO   )rT   )rU   r(   rV   rW   )r   rZ   )__name__
__module____qualname____doc__r   classmethodr    propertyr#   r&   r5   rA   rD   rN   rS   rY   r\    r   r   r   r      s0        
       [     X   
   * 
T 
T 
T 
T 
T 
T9 9 9 9U U U U
   $	3 	3 	3 	3 	3     r   r   )ra   
__future__r   r.   typingr   redis.asyncioasyncior   
app.configr   app.utils.loggerr   r^   r2   r,   rJ   r   rd   r   r   <module>rk      s     # " " " " "                          ' ' ' ' ' '	H		 n n n n n n n n n nr   