feat(stats): buffer visits in memory and expose POST /api/visit + /api/stats

This commit is contained in:
kairee committed 2026-11-01 15:34:53 +08:00
1 parent cdd11823b9
commit 3c16caac1a
8 files changed
+905 -5

No files matched your search

+61 -1
View File
@@ -477,6 +477,65 @@ def test_about_payload():
db.close()
def test_stats_query():
with tempfile.TemporaryDirectory() as tmp:
db, q = setup(tmp)
try:
empty = q.stats(days=7)
assert empty["today"] == "2026-10-31"
assert empty["today_pv"] == 0 and empty["today_uv"] == 0
assert len(empty["series"]["dates"]) == 7
assert empty["series"]["dates"][-1] == "2026-10-31"
assert empty["total_pv"] == 0 and empty["total_uv"] == 0
assert empty["top_pages"] == []
assert empty["enabled"] is True
assert empty["pending"] is False
def insert(conn, day, ip, path, hits, first, last):
conn.execute(
"INSERT INTO visit(stat_date, ip, path, hits, first_at, last_at) "
"VALUES (?, ?, ?, ?, ?, ?)",
(day, ip, path, hits, first, last),
)
db.write(lambda conn: [
insert(conn, "2026-10-31", "1.1.1.1", "/home", 3, 1, 2),
insert(conn, "2026-10-31", "2.2.2.2", "/home", 1, 1, 1),
insert(conn, "2026-10-31", "1.1.1.1", "/videos", 2, 1, 1),
insert(conn, "2026-10-30", "1.1.1.1", "/home", 5, 1, 1),
insert(conn, "2026-09-01", "9.9.9.9", "/home", 100, 1, 1),
])
stats = q.stats(days=7)
assert stats["today_pv"] == 6
assert stats["today_uv"] == 2
assert stats["total_pv"] == 11 # 今天 6 + 昨天 5,区间外不计
assert stats["total_uv"] == 2
assert stats["series"]["pv"][-1] == 6
assert stats["series"]["pv"][-2] == 5
assert stats["series"]["uv"][-1] == 2
pages = {page["path"]: page for page in stats["top_pages"]}
assert pages["/home"]["pv"] == 9 and pages["/home"]["uv"] == 2
assert pages["/videos"]["pv"] == 2 and pages["/videos"]["uv"] == 1
assert stats["top_pages"][0]["path"] == "/home"
# 内存缓冲合并:不同键相加,重叠键去重合并
pending = [
{"stat_date": "2026-10-31", "ip": "3.3.3.3",
"path": "/about", "hits": 4, "first_at": 1, "last_at": 1},
{"stat_date": "2026-10-31", "ip": "1.1.1.1",
"path": "/home", "hits": 1, "first_at": 3, "last_at": 3},
]
merged = q.stats(days=7, pending=pending)
assert merged["today_pv"] == 11
assert merged["today_uv"] == 3
assert merged["pending"] is True
pages = {page["path"]: page for page in merged["top_pages"]}
assert pages["/about"]["pv"] == 4
assert pages["/home"]["pv"] == 10
finally:
db.close()
def main_test():
test_overview()
test_video_listing_filters_and_sorting()
@@ -487,9 +546,10 @@ def main_test():
test_duration_efficiency_queries()
test_year_filter_and_published_sort()
test_about_payload()
test_stats_query()
test_fans_custom_range_modes()
test_fans_candidates_system_and_export()
print("OK: Web 查询层总览/榜单/详情/UP/时长效率/粉丝/候选/系统测试全部通过")
print("OK: Web 查询层总览/榜单/详情/UP/时长效率/粉丝/候选/系统/访问统计测试全部通过")
if __name__ == "__main__":