feat(admin): 数据总览按客迹视觉V1重做(周期卡/趋势双线/渠道条形/最近访问)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -78,8 +78,8 @@ def test_list_fields_and_default_bot_excluded(client, member_user):
|
||||
"device_type", "os", "browser", "language", "first_seen_at",
|
||||
"last_seen_at", "visit_count", "active_days", "session_count",
|
||||
"total_stay_ms", "first_source", "last_source", "first_landing_path",
|
||||
"online", "channel", "platform", "variant", "search_term", "resolution",
|
||||
"device_model"}
|
||||
"last_path", "online", "channel", "platform", "variant", "search_term",
|
||||
"resolution", "device_model"}
|
||||
assert row["country"] == "中国" and row["os"] == "Windows" and row["browser"] == "Chrome"
|
||||
assert row["session_count"] == 2
|
||||
assert row["total_stay_ms"] == 150000 # 120000+30000,有效停留口径
|
||||
@@ -146,6 +146,26 @@ def test_list_site_fields_and_site_filter(client, member_user):
|
||||
assert d["total"] == 1 and [x["visitor_id"] for x in d["list"]] == ["vb"] # 只剩本站访客
|
||||
|
||||
|
||||
def test_list_last_path(client, member_user):
|
||||
"""列表行 last_path:该访客最近一条事件的 path(Subquery 注解,无 N+1);
|
||||
无事件访客为空串。"""
|
||||
_auth(client, member_user)
|
||||
site = Site.objects.create(name="s", domains=["a.com"])
|
||||
v1 = _visitor(site, "v1", last_seen=timezone.now())
|
||||
v2 = _visitor(site, "v2", last_seen=timezone.now() - timedelta(hours=1))
|
||||
s1 = _session(site, v1, "s1", at=NOW - timedelta(hours=2))
|
||||
_event(site, v1, s1, "e1", "page_view", NOW - timedelta(hours=2), path="/old")
|
||||
_event(site, v1, s1, "e2", "page_view", NOW - timedelta(minutes=90), path="/stay")
|
||||
_event(site, v1, s1, "e3", "page_stay", NOW - timedelta(minutes=30), path="/stay",
|
||||
active_ms=1000) # occurred_at 早于 e2 → 不改判"最近"
|
||||
_event(site, v1, s1, "e4", "page_view", NOW, path="/newest")
|
||||
|
||||
d = client.get("/api/admin/visitors").json()["data"]
|
||||
by_id = {x["visitor_id"]: x for x in d["list"]}
|
||||
assert by_id["v1"]["last_path"] == "/newest" # 最近一条事件的 path
|
||||
assert by_id["v2"]["last_path"] == "" # 无事件 → 空串(前端显示 —)
|
||||
|
||||
|
||||
def test_list_site_not_found(client, member_user):
|
||||
_auth(client, member_user)
|
||||
site = Site.objects.create(name="s", domains=["a.com"])
|
||||
@@ -198,6 +218,7 @@ def test_detail_shape(client, member_user):
|
||||
assert d["session_count"] == 2 and d["total_stay_ms"] == 90000
|
||||
assert d["first_source"] == "google.com"
|
||||
assert d["last_source"] == "wechat" # 最近会话 s-new:referrer 空 → utm 兜底
|
||||
assert d["last_path"] == "/b" # 最近一条事件 e3 的 path
|
||||
assert [s["session_id"] for s in d["sessions"]] == ["s-new", "s-old"] # 最近在前
|
||||
srow = d["sessions"][0]
|
||||
assert set(srow) == {"session_id", "started_at", "ended_at", "referrer_domain", "utm",
|
||||
|
||||
Reference in New Issue
Block a user