新功能:个性化推荐算法

This commit is contained in:
吕新雨
2026-02-02 16:47:37 +08:00
parent 936094211b
commit 6dc4e2b943
119 changed files with 7427 additions and 357 deletions

62
server/app/api/limits.py Normal file
View File

@@ -0,0 +1,62 @@
from __future__ import annotations
import time
from dataclasses import dataclass, field
from typing import Dict, Tuple
from fastapi import HTTPException, Request
@dataclass
class FixedWindowRateLimiter:
"""
固定窗口限流(内存版)。
约束:
- 适用于单进程/单实例;多进程/多实例下不共享计数V1 可接受)
- 窗口粒度:按分钟 bucketwindow_seconds 建议为 60
"""
limit: int
window_seconds: int
_counters: Dict[Tuple[str, int], int] = field(default_factory=dict)
_last_gc_bucket: int = 0
def _bucket(self, now_ts: float) -> int:
return int(now_ts // float(self.window_seconds))
def _gc(self, current_bucket: int) -> None:
# 每隔一段时间清理一次,避免 dict 无限增长(保留最近 3 个 bucket
if self._last_gc_bucket == current_bucket:
return
self._last_gc_bucket = current_bucket
keep_from = current_bucket - 2
to_delete = [k for k in self._counters.keys() if k[1] < keep_from]
for k in to_delete:
self._counters.pop(k, None)
def allow(self, *, key: str, now_ts: float) -> None:
bucket = self._bucket(now_ts)
self._gc(bucket)
k = (str(key), int(bucket))
n = int(self._counters.get(k, 0)) + 1
self._counters[k] = n
if n > int(self.limit):
raise HTTPException(status_code=429, detail="rate_limited")
_reco_rate_limiter = FixedWindowRateLimiter(limit=10, window_seconds=60)
async def rate_limit_reco_by_ip(request: Request) -> None:
"""
推荐接口限流:按 IP1 分钟 10 次。
"""
ip = "unknown"
if request.client and request.client.host:
ip = str(request.client.host)
_reco_rate_limiter.allow(key=ip, now_ts=time.time())