feat: 原始上报数据存储(raw_batch 表/脱敏/访客关联/详情页 Tab/30天清理)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-16 15:42:06 +08:00
parent 1cadee16bd
commit 21ca43248a
10 changed files with 534 additions and 6 deletions
+29 -1
View File
@@ -13,9 +13,10 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from tracker.api.envelope import EnvelopeMixin, ok
from tracker.models import Event, Site, VisitSession, Visitor
from tracker.models import Event, RawBatch, Site, VisitSession, Visitor
PAGE_SIZE = 20
RAW_PAGE_SIZE = 10 # 原始批次 body 较大,页小一些
TIMELINE_DEFAULT_DAYS = 30
TIMELINE_MAX_DAYS = 90
TIMELINE_MAX_EVENTS = 1000
@@ -246,3 +247,30 @@ class VisitorSessionView(EnvelopeMixin, APIView):
return _not_found("session_not_found")
events = Event.objects.filter(session=session).order_by("occurred_at", "pk") # (session,occurred_at) 索引
return ok({**_session_summary(session), "events": _merge_page_stay(events)})
class VisitorRawBatchesView(EnvelopeMixin, APIView):
"""访客原始上报批次(raw_batch):排查"上报了为何没进报表"。
body 是脱敏后的整包 JSON 文本(敏感值已 [FILTERED]、被拒批次已截断),
前端以 <pre> 文本渲染(禁 v-html)。按 -received_at 倒序分页,走 (visitor,-received_at) 索引。
"""
def get(self, request, visitor_id):
visitor = _get_visitor(visitor_id)
if not visitor:
return _not_found("visitor_not_found")
qs = RawBatch.objects.filter(visitor=visitor)
total = qs.count()
try:
page = int(request.query_params.get("page", 1))
except (TypeError, ValueError):
page = 1
page = max(1, page)
batches = qs.order_by("-received_at", "-pk")[
(page - 1) * RAW_PAGE_SIZE: page * RAW_PAGE_SIZE]
return ok({"list": [{"id": b.id, "batch_id": b.batch_id, "status": b.status,
"event_count": b.event_count, "rejected_count": b.rejected_count,
"received_at": _iso(b.received_at), "body": b.body}
for b in batches],
"total": total, "page": page})