File size: 5,502 Bytes
bdcdaf4
d0c18f0
bdcdaf4
 
 
d0c18f0
 
 
bdcdaf4
d0c18f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bdcdaf4
 
 
d0c18f0
 
 
bdcdaf4
 
 
d0c18f0
 
 
bdcdaf4
d0c18f0
 
 
bdcdaf4
d0c18f0
 
 
 
 
 
bdcdaf4
 
d0c18f0
 
 
 
bdcdaf4
d0c18f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
"""
文件缓存核心逻辑 - LRU缓存 + 后台清理
"""

import time
import asyncio
from collections import OrderedDict
from typing import Optional, Tuple

from app.utils.url_input_handler import resolve_content, encode_text_to_base64, decode_base64_to_text, encode_bytes_to_base64

MAX_CACHE_SIZE = 100
CLEANUP_INTERVAL = 60


class LRUCache:
    """LRU缓存,自动淘汰最久未使用的条目"""

    def __init__(self, max_size: int = MAX_CACHE_SIZE):
        self._store: OrderedDict[str, dict] = OrderedDict()
        self._max_size = max_size
        self._cleanup_task: Optional[asyncio.Task] = None
        self._lock = asyncio.Lock()  # 线程安全锁

    async def start_cleanup(self):
        """启动后台清理任务"""
        if self._cleanup_task is None:
            self._cleanup_task = asyncio.create_task(self._cleanup_loop())

    async def stop_cleanup(self):
        """停止后台清理任务"""
        if self._cleanup_task is not None:
            self._cleanup_task.cancel()
            try:
                await self._cleanup_task
            except asyncio.CancelledError:
                pass
            self._cleanup_task = None

    async def _cleanup_loop(self):
        """定期清理过期条目"""
        while True:
            await asyncio.sleep(CLEANUP_INTERVAL)
            await self._cleanup_expired()

    async def _cleanup_expired(self):
        """清理所有过期条目"""
        async with self._lock:
            now = time.time()
            expired = [k for k, v in self._store.items() if v["expires_at"] < now]
            for k in expired:
                del self._store[k]

    async def set(self, key: str, value: str, ttl_seconds: int) -> dict:
        """设置缓存,自动淘汰 LRU 条目"""
        async with self._lock:
            if key in self._store:
                del self._store[key]

            while len(self._store) >= self._max_size:
                self._store.popitem(last=False)

            self._store[key] = {
                "value": value,
                "expires_at": time.time() + ttl_seconds,
            }
            self._store.move_to_end(key)
        return {"success": True, "message": f"缓存已设置,TTL {ttl_seconds}秒"}

    async def get(self, key: str) -> Optional[dict]:
        """获取缓存,更新 LRU位置"""
        async with self._lock:
            entry = self._store.get(key)
            if not entry:
                return None

            if time.time() > entry["expires_at"]:
                del self._store[key]
                return None

            self._store.move_to_end(key)
            return entry

    async def delete(self, key: str) -> bool:
        """删除缓存"""
        async with self._lock:
            if key in self._store:
                del self._store[key]
                return True
            return False

    async def stats(self) -> dict:
        """获取缓存统计"""
        async with self._lock:
            return {
                "size": len(self._store),
                "max_size": self._max_size,
            }


# 全局缓存实例
_cache: Optional[LRUCache] = None


def get_cache() -> LRUCache:
    """获取全局缓存实例"""
    global _cache
    if _cache is None:
        _cache = LRUCache()
    return _cache


async def init_cache():
    """初始化缓存并启动清理任务"""
    cache = get_cache()
    await cache.start_cleanup()


async def shutdown_cache():
    """关闭缓存清理任务"""
    global _cache
    if _cache is not None:
        await _cache.stop_cleanup()


async def do_set(key: str, value_base64: str, ttl_seconds: int = 3600) -> dict:
    """设置缓存值"""
    cache = get_cache()
    return await cache.set(key, value_base64, ttl_seconds)


async def do_get(key: str) -> dict:
    """获取缓存值"""
    cache = get_cache()
    entry = await cache.get(key)
    if not entry:
        return {"success": False, "message": f"缓存键 {key} 不存在或已过期"}
    return {"success": True, "value": entry["value"], "message": "缓存获取成功"}


async def do_delete(key: str) -> dict:
    """删除缓存值"""
    cache = get_cache()
    if await cache.delete(key):
        return {"success": True, "message": f"缓存键 {key} 已删除"}
    return {"success": False, "message": f"缓存键 {key} 不存在"}


async def do_set_text(key: str, text_content: str, ttl_seconds: int = 3600) -> dict:
    """设置文本缓存 - 自动编码为 base64"""
    value_base64 = encode_text_to_base64(text_content)
    return await do_set(key, value_base64, ttl_seconds)


async def do_get_text(key: str) -> dict:
    """获取文本缓存 - 自动从 base64 解码"""
    result = await do_get(key)
    if result["success"]:
        try:
            text = decode_base64_to_text(result["value"])
            return {"success": True, "text": text, "message": "获取成功"}
        except Exception as e:
            return {"success": False, "message": f"解码失败: {e}"}
    return result


async def do_set_from_url(key: str, url: str, ttl_seconds: int = 3600) -> dict:
    """从 URL 下载文件并缓存"""
    try:
        content, source = await resolve_content(None, url)
        value_base64 = encode_bytes_to_base64(content)
        result = await do_set(key, value_base64, ttl_seconds)
        result["source"] = source
        return result
    except ValueError as e:
        return {"success": False, "message": f"URL获取失败: {e}"}