c386103809
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
47 lines
2.1 KiB
Python
47 lines
2.1 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
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
|
|
from app.api.deps import DbSession
|
|
from app.repositories import analytics as analytics_repo
|
|
from app.repositories import analytics_selfstat as selfstat_repo
|
|
from app.schemas.analytics import AnalyticsBatchIn, AnalyticsIngestOut
|
|
from app.schemas.analytics_selfstat import SelfStatBatchIn, SelfStatIngestOut
|
|
|
|
router = APIRouter(prefix="/api/v1/analytics", tags=["analytics"])
|
|
logger = logging.getLogger("shagua.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)
|
|
|
|
|
|
@router.post("/selfstat", response_model=SelfStatIngestOut, summary="上报自报计数快照")
|
|
def ingest_selfstat(batch: SelfStatBatchIn, db: DbSession) -> SelfStatIngestOut:
|
|
try:
|
|
snap_id = selfstat_repo.record_selfstat(db, batch)
|
|
except Exception: # noqa: BLE001 — 计数链路要稳,落库失败不裸奔 500,记日志回明确错误
|
|
logger.exception("selfstat ingest failed device=%s epoch=%s", batch.device_id, batch.epoch_id)
|
|
raise HTTPException(status_code=503, detail="selfstat ingest failed") from None
|
|
return SelfStatIngestOut(snapshot_id=snap_id)
|