667cda566f
新手引导埋点的服务端: - 表 analytics_event(五维硬性列 + props JSON 扩展字段;event/device_id/user_id/session_id/created_at 带索引) - POST /api/v1/analytics/events:客户端批量上报(不鉴权、body 读可选 user_id、补 client_ip + server_at) - admin GET /admin/api/event-logs:列表 + 按 事件/设备/用户/会话/时间 筛选(offset 分页,照 list_feedbacks) - alembic migration 建表(autogenerate 顺带检出的 ad/cps 历史索引漂移已手动剔除) app 主后端 :8770 与 admin :8771 共用同一 SQLite,admin 同库直接查、无需跨库。 配套客户端五维上报 + admin 日志页(另两仓库 PR)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: zzhyyyyy <2685922758@qq.com> Reviewed-on: #81 Co-authored-by: zhuzihao <zhuzihao@wonderable.ai> Co-committed-by: zhuzihao <zhuzihao@wonderable.ai>
32 lines
1.3 KiB
Python
32 lines
1.3 KiB
Python
"""客户端埋点上报接口。
|
|
|
|
POST /api/v1/analytics/events — 批量接收新手引导(及后续)埋点,append 落 analytics_event 表。
|
|
**不强制登录**(未登录态也要采集行为):user_id 由客户端在 body 里可选带上,不靠 Bearer。
|
|
服务端补 client_ip(X-Forwarded-For)与 created_at(接收时间 = server_at)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Request
|
|
|
|
from app.api.deps import DbSession
|
|
from app.repositories import analytics as analytics_repo
|
|
from app.schemas.analytics import AnalyticsBatchIn, AnalyticsIngestOut
|
|
|
|
router = APIRouter(prefix="/api/v1/analytics", tags=["analytics"])
|
|
|
|
|
|
def _client_ip(request: Request) -> str:
|
|
"""取客户端 IP:生产经 nginx 反代优先 X-Forwarded-For 第一段,否则直连 IP(同 admin get_client_ip)。"""
|
|
xff = request.headers.get("x-forwarded-for")
|
|
if xff:
|
|
return xff.split(",")[0].strip()
|
|
return request.client.host if request.client else ""
|
|
|
|
|
|
@router.post("/events", response_model=AnalyticsIngestOut, summary="批量上报埋点事件")
|
|
def ingest_events(
|
|
batch: AnalyticsBatchIn, request: Request, db: DbSession
|
|
) -> AnalyticsIngestOut:
|
|
n = analytics_repo.record_batch(db, batch, client_ip=_client_ip(request))
|
|
return AnalyticsIngestOut(received=n)
|