当前在线
--
0
在线访客数
(最近5分钟独立 IP)
最近5分钟访问情况
在线人数(按分钟桶去重 IP, 共 5 分钟)
今日流量
| 浏览量(PV) | 访客数(UV) | IP数 | 跳出率 | 平均访问时长 | |
|---|---|---|---|---|---|
| 今日 | -- | -- | -- | -- | -- |
| 昨日 | -- | -- | -- | -- | -- |
|
预计今 日 |
-- | -- | -- | -- | -- |
落地站点排行
加载中...
来路排行
加载中...
📖 统计系统技术文档(给后续维护的 AI / 工程师)
1️⃣ 系统总览
浏览器 SDK (pink.html)
↓ /s/t/pv?u=...&ref=...
流媒体服务器 stats_app.py (端口 5004 · stats-api.service)
├─ 实时写入 SQLite (/opt/ppvod/OKAY-影视web/数据/stats.db)
├─ 凌晨 0:05 触发 _sync_yesterday_to_render() → POST 到 Render
└─ 每小时整点 :05 触发 _sync_today_to_render() → 增量备份
↓
Render Console (Flask /ad/api/stats/sync)
├─ 写入 PostgreSQL(永久存档)
└─ HTTP 2xx 返回流媒体
↑
浏览器 ← console 路由 stats.py
├─ today → _fetch_stats() 调流媒体 SQLite (HTTP)
└─ < today → _get_*_from_pg() 读 Render PG
2️⃣ 数据存储(两层架构)
🟡 流媒体 SQLite (stats.db) — 仅当日实时缓存
| 表 | 用途 |
|---|---|
| daily_stats | 每日 PV / IP 总数 |
| site_stats | 落地分站(site_id = 完整 URL,含 path) |
| hourly_stats | 小时分布 |
| referer_stats | 来路(domain = scheme://host) |
| landing_stats / referer_url_stats | 完整 URL 维度(含 path) |
| daily_ips | 独立 IP 去重(哈希) |
| session_visitors / session_data | UV / 跳出率 / 时长 |
| ad_clicks / ad_click_ips | 广告位点击 + 自然日去重 |
🟣 Render PostgreSQL — 永久存档(历史日全部从这里读)
| 表 | 用途 |
|---|---|
| traffic_daily_stats | 每日 PV / IP(PK: stat_date) |
| traffic_daily_sites | 落地(PK: stat_date+site_id) |
| traffic_daily_referers | 来路(PK: stat_date+site+domain) |
| traffic_daily_session | UV / 跳出 / 时长 |
| ad_slot_daily_stats | 广告位日点击 |
| ad_advertiser_daily_stats | 广告主聚合 |
| ad_advertiser_conversions | 注册转化录入 |
| ad_url_advertiser_map | URL host → 广告主名(用户手动) |
| ad_campaigns | 广告位元数据(含 advertiser) |
3️⃣ 关键时间点(北京时间)
| 时机 | 动作 | 触发源 |
|---|---|---|
| 实时 | SQLite INSERT (PV+1, ad_clicks+1) | 埋点事件 |
| 每小时 :05 | _sync_today_to_render() 推当天增量到 PG(容灾备份) | stats_app 进程内线程 |
| 凌晨 0:05 |
_sync_yesterday_to_render()
推昨日全量到 PG → HTTP 2xx 立刻调 _cleanup_synced_sqlite(synced_date=yesterday) DELETE WHERE stat_date <= yesterday + VACUUM |
stats_app 进程内线程 |
| 用户请求 | date == today → 流媒体;< today → PG | 浏览器 fetch(逻辑切换) |
⚠️ 整套系统只有「凌晨 0:05」一个时间锚点(跨日切换最小定时器), 清理 / VACUUM /
路由切换都是逻辑触发,零 cron / systemd timer。
4️⃣ 关键文件位置
📍 流媒体服务器 (204.77.223.5:60445 别名 video)
/opt/ppvod/OKAY-影视web/stats_app.py — Flask 入口 + sync 线程
/opt/ppvod/OKAY-影视web/影视引擎/日志统计.py — SQLite 读取查询函数
/opt/ppvod/OKAY-影视web/数据/stats.db — SQLite 数据库文件
/etc/systemd/system/stats-api.service — 进程托管
📍 Console (Render web service)
路由/stats.py — /ad/api/stats/* 路由 + PG fallback
模板/stats.html — 本页 UI
引擎/shared/db_pool.py — get_db() 默认 RealDictCursor
📍 部署
scripts/sync-video.sh — push + 流媒体 git pull + 重启
stats-api
scripts/seo-ssh.sh video "cmd" — SSH 流媒体执行命令
Render auto-deploy on push to main
5️⃣ 常见故障排查
| 现象 | 排查路径 |
|---|---|
| 昨日 / 历史日数据全 0 |
①
curl /ad/api/stats/overview?date=YYYY-MM-DD,看 source 是 realtime / postgresql ② source=realtime 但 sites=[] → 流媒体 SQLite 已清,PG fallback 失败 ③ docker logs main-console | grep "PG.*查询失败",常见 KeyError(0/1) 是 RealDictCursor + row[N] 索引 ④ 修复方法:把 conn.cursor() 换成 _tuple_cursor(conn) |
| 今日数据没更新 |
① ssh video → systemctl status stats-api / nginx ② SDK PV 是否打到 stats_app(curl /ad/api/stats/overview,看 total.pv 涨没涨) ③ /opt/ppvod/OKAY-影视web/数据/stats.db 文件 mtime 是否在更新 |
| PG 没收到归档 |
①
journalctl -u stats-api --since today | grep
"[Sync]" ② 应看到 "✓ 2026-XX-XX → Render: 200" + "🧹 SQLite 清理 N 条" ③ Console 端 DATABASE_URL / STATS_SYNC_SECRET 是否对 |
| 广告位点击不更新 |
① ad_click_ips 表写入正常?(同 IP 同 slot 一天 1
次去重) ② 路由/__init__.py L3940 的 ad_click 端点是否被前端调用 |
| 来路 / 落地 URL 没 scheme |
① 历史数据(早于 2026-04-26 17:00 SDK 升级前)保持原样 ② 新数据应是 https://host[/path](_extract_domain / _extract_site_url) |
| 凌晨清理没生效(SQLite 越来越大) |
① 看 stats_app 日志是否有 "🧹 SQLite 清理 N 条" ② sync 失败(HTTP 非 2xx)会跳过清理 ③ Phase 1 / Phase 2 失败有专属日志:"⚠️ SQLite DELETE 失败" / "⚠️ VACUUM 失败" |
6️⃣ 关键数据语义
- site_id (落地): 完整 URL scheme://host/path(不含 query)。SDK 优先取 query 参数 u=, 缺失回退到 HTTP Referer 头
- domain (来路): scheme://full_host(含 www./m.,无 path)。受浏览器 referrer policy 限制,跨站只能拿到 origin
- 点击去重: (stat_date, ad_slot, ip_hash) 三元 PK,北京自然日切日重置
- 自来路过滤: 站内访问 (domain.host == site.host,含 www. / m.) 不计入来路(_BARE_HOST_SQL)
- 广告主分类: 优先 ad_campaigns.advertiser;次 ad_url_advertiser_map(用户改的兜底);最后 _auto_classify_url 自动判(直播/BC)
⚙️ 修改任何统计逻辑前请先读这份文档;改动后更新本文档对应章节
广告位点击
--
今日实时(来自服务器 SQLite)
--
加载中...