🐛 修复下载历史重复计数

This commit is contained in:
2026-06-05 16:34:07 +08:00
parent 87d8f522a6
commit 85af153b38
3 changed files with 37 additions and 7 deletions
+14 -1
View File
@@ -3,7 +3,7 @@ import json
import os import os
import time import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Optional from typing import Any, Dict, List, Optional, Tuple
from core.response import APIResponse from core.response import APIResponse
from core.storage import FileStorageInterface, storages from core.storage import FileStorageInterface, storages
@@ -82,6 +82,18 @@ class FileService:
def _file_metadata_key(self, file_id: int) -> str: def _file_metadata_key(self, file_id: int) -> str:
return f"{self.FILE_METADATA_KEY_PREFIX}{file_id}" return f"{self.FILE_METADATA_KEY_PREFIX}{file_id}"
def _dedupe_download_stats(self, stats: List[TransferStats]) -> List[TransferStats]:
deduped: List[TransferStats] = []
recent_downloads: Dict[Tuple[Optional[int], str, int], datetime] = {}
for stat in stats:
key = (stat.file_code_id, stat.ip or "", int(stat.size or 0))
previous = recent_downloads.get(key)
if previous and (stat.created_at - previous).total_seconds() <= 5:
continue
recent_downloads[key] = stat.created_at
deduped.append(stat)
return deduped
async def _delete_file_code(self, file_code: FileCodes): async def _delete_file_code(self, file_code: FileCodes):
try: try:
name = ( name = (
@@ -1289,6 +1301,7 @@ class FileService:
download_stats = await TransferStats.filter( download_stats = await TransferStats.filter(
action="download", file_code_id=file_code.id action="download", file_code_id=file_code.id
).order_by("created_at") ).order_by("created_at")
download_stats = self._dedupe_download_stats(download_stats)
timeline.append( timeline.append(
{ {
"key": "retrieved", "key": "retrieved",
+16 -1
View File
@@ -189,10 +189,25 @@ async def analytics(start: str = "", end: str = ""):
end_day = parse_day(end, now.replace(hour=0, minute=0, second=0, microsecond=0)) end_day = parse_day(end, now.replace(hour=0, minute=0, second=0, microsecond=0))
start_day = parse_day(start, end_day - datetime.timedelta(days=29)) start_day = parse_day(start, end_day - datetime.timedelta(days=29))
end_exclusive = end_day + datetime.timedelta(days=1) end_exclusive = end_day + datetime.timedelta(days=1)
stats = await TransferStats.filter( raw_stats = await TransferStats.filter(
created_at__gte=start_day, created_at__gte=start_day,
created_at__lt=end_exclusive, created_at__lt=end_exclusive,
).order_by("created_at", "id")
stats = []
recent_downloads = {}
for item in raw_stats:
if item.action == "download":
dedupe_key = (
item.file_code_id or item.code or "",
item.ip or "",
int(item.size or 0),
) )
previous = recent_downloads.get(dedupe_key)
if previous and (item.created_at - previous).total_seconds() <= 5:
continue
recent_downloads[dedupe_key] = item.created_at
stats.append(item)
files = await FileCodes.all() files = await FileCodes.all()
file_map = {item.code: item for item in files} file_map = {item.code: item for item in files}
+4 -2
View File
@@ -292,6 +292,7 @@ async def select_file(data: SelectFileModel, ip: str = Depends(ip_limit["error"]
return APIResponse(code=404, detail=file_code) return APIResponse(code=404, detail=file_code)
assert isinstance(file_code, FileCodes) assert isinstance(file_code, FileCodes)
if file_code.text is not None:
await update_file_usage(file_code) await update_file_usage(file_code)
await record_transfer_stat("download", file_code, file_code.size, ip) await record_transfer_stat("download", file_code, file_code.size, ip)
return APIResponse(detail=await build_select_detail(file_code, file_storage)) return APIResponse(detail=await build_select_detail(file_code, file_storage))
@@ -303,10 +304,11 @@ async def download_file(key: str, code: str, ip: str = Depends(ip_limit["error"]
if await get_select_token(code) != key: if await get_select_token(code) != key:
ip_limit["error"].add_ip(ip) ip_limit["error"].add_ip(ip)
raise HTTPException(status_code=403, detail="下载鉴权失败") raise HTTPException(status_code=403, detail="下载鉴权失败")
has, file_code = await get_code_file_by_code(code, False) has, file_code = await get_code_file_by_code(code)
if not has: if not has:
return APIResponse(code=404, detail="文件不存在") return APIResponse(code=404, detail=file_code)
assert isinstance(file_code, FileCodes) assert isinstance(file_code, FileCodes)
await update_file_usage(file_code)
await record_transfer_stat("download", file_code, file_code.size, ip) await record_transfer_stat("download", file_code, file_code.size, ip)
return ( return (
APIResponse(detail=file_code.text) APIResponse(detail=file_code.text)