"""邀请归因 API:邀请码、小程序码、分享/扫码归因、邀请统计。""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel, Field from ...infrastructure.repositories import Database from ...services import invite_service from ..dependencies import get_current_user, get_db router = APIRouter(prefix="/invite", tags=["邀请归因"]) class ShareRequest(BaseModel): channel: str = Field(default="share_app", description="share_app|share_timeline|qrcode|link") scene: str = Field(default="", description="场景:test_result|profile|home等") env: str = Field(default="trial", description="trial|release") class ScanRequest(BaseModel): inviter: str = Field(description="邀请人ID或邀请码") scene: str = Field(default="", description="扫码场景值") env: str = Field(default="trial", description="trial|release") class QrcodeRequest(BaseModel): scene: str = Field(default="home", description="场景:home|test_result|profile") page: str = Field(default="pages/index/index", description="小程序页面路径") env: str = Field(default="trial", description="trial|release") width: int = Field(default=430, description="小程序码宽度") @router.get("/me", summary="获取我的邀请码和统计") async def get_my_invite(user: dict = Depends(get_current_user), db: Database = Depends(get_db)): stats = await invite_service.get_invite_stats(db, user["id"]) return stats @router.get("/qrcode", summary="生成带邀请参数的小程序码") async def get_invite_qrcode( scene: str = "home", page: str = "pages/index/index", env: str = "trial", width: int = 430, user: dict = Depends(get_current_user), db: Database = Depends(get_db), ): result = await invite_service.get_wxacode(db, user["id"], scene=scene, page=page, env=env, width=width) if not result.get("success"): raise HTTPException(status_code=500, detail=f"生成小程序码失败: {result.get('error')}") # 返回 base64 图片 import base64 img_b64 = base64.b64encode(result["image"]).decode() return { "image": f"data:image/png;base64,{img_b64}", "scene": result["scene"], "invite_code": result["invite_code"], } @router.post("/share", summary="记录分享行为") async def record_share( req: ShareRequest, request: Request, user: dict = Depends(get_current_user), db: Database = Depends(get_db), ): ip = request.client.host if request.client else "" await invite_service.record_share(db, user["id"], channel=req.channel, scene=req.scene, env=req.env, ip=ip) return {"success": True} @router.post("/scan", summary="记录扫码行为(无需登录)") async def record_scan(req: ScanRequest, request: Request, db: Database = Depends(get_db)): ip = request.client.host if request.client else "" await invite_service.record_scan(db, req.inviter, channel="qrcode", scene=req.scene, env=req.env, ip=ip) return {"success": True} @router.get("/invitees", summary="获取我邀请的用户列表") async def get_invitees( user: dict = Depends(get_current_user), db: Database = Depends(get_db), ): invitees = await db.users.get_invitees(user["id"]) from ...infrastructure.oss import resolve_url return { "total": len(invitees), "list": [ { "id": u["id"], "nickname": u.get("nickname", ""), "avatar": resolve_url(u.get("avatar", "")), "phone": u.get("phone", ""), "invited_at": u.get("invited_at", ""), "created_at": u.get("created_at", ""), } for u in invitees ], }