This commit is contained in:
root
2026-10-16 13:51:00 +08:00
parent 6bf949949c
commit 8fc899d0a9
3830 changed files with 11228 additions and 188377 deletions
+45
View File
@@ -0,0 +1,45 @@
"""推广归因路由(/invite/* + H5 中间页 /r/{code})
三段式归因:分享口令(主)→ H5 点击埋点模糊匹配(辅)→ 注册手填邀请码(兜底)
"""
import redis.asyncio as aioredis
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.response import ok
from app.database import get_db
from app.redis_client import get_redis
from app.schemas.invite import InviteClickReq, InviteMatchReq
from app.services import invite_service
router = APIRouter(prefix="/invite", tags=["推广归因"])
router_page = APIRouter(tags=["推广落地页"], include_in_schema=False)
def _client_ip(request: Request) -> str:
# Nginx 反代后取真实 IP
return request.headers.get("X-Real-IP") or (request.client.host if request.client else "")
@router.post("/click")
async def invite_click(req: InviteClickReq, request: Request,
redis: aioredis.Redis = Depends(get_redis)):
"""H5 中间页点击埋点(打开页面即上报,记录 code+IP+UA,24h 有效)"""
ua = request.headers.get("User-Agent", "")
return ok(await invite_service.record_click(redis, req.code.upper(), _client_ip(request), ua))
@router.post("/match")
async def invite_match(req: InviteMatchReq, request: Request,
redis: aioredis.Redis = Depends(get_redis)):
"""App 首启归因匹配:按 IP+时间就近匹配点击记录,结果按 device_id 服务端缓存"""
ua = request.headers.get("User-Agent", "")
return ok(await invite_service.match_and_cache(redis, req.device_id, _client_ip(request), ua))
@router_page.get("/r/{code}", response_class=HTMLResponse)
async def invite_landing(code: str, db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis)):
"""推广落地页(复制口令 + 下载入口,打开即自动埋点)"""
return await invite_service.render_landing_page(db, redis, code.upper())