e1bd0e3ef7
改了什么:新增新版大盘日期窗口聚合、广告收益拆分、比价耗时字段、美团 CPS 拉单入库与大盘展示,并同步反馈审核和邀请奖励下线口径。 验证:python -m pytest tests/test_admin_read.py tests/test_admin_write.py tests/test_compare_record.py tests/test_invite.py tests/test_feedback.py -q。
74 lines
3.2 KiB
Python
74 lines
3.2 KiB
Python
"""邀请相关 API 收发模型。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class InviteInfoOut(BaseModel):
|
|
invite_code: str # 我的邀请码
|
|
share_url: str # 落地页链接(含 ?ref=),前端据此生成二维码 + 复制分享
|
|
invited_count: int # 已成功邀请人数
|
|
coins_earned: int # 邀请金币已下线,保留字段兼容客户端(恒为 0)
|
|
|
|
|
|
class LandingTrackIn(BaseModel):
|
|
"""落地页 dl.html 访问时上报的指纹(用于剪贴板归因失败时兜底)。"""
|
|
ref: str = Field(..., min_length=4, max_length=16, description="邀请码(落地页 ?ref=)")
|
|
screen: str = Field("", max_length=32, description="屏幕分辨率,如 1080x2400")
|
|
# IP / UA 服务端从 HTTP 头自动拿,无需 JS 上报
|
|
|
|
|
|
class LandingTrackOut(BaseModel):
|
|
status: str # ok / invalid_code / no_ip
|
|
|
|
|
|
class FingerprintIn(BaseModel):
|
|
"""客户端登录后剪贴板归因失败时上报的设备指纹。
|
|
|
|
跨端匹配字段:浏览器落地页存的同名字段 == 客户端 Build.MODEL / DisplayMetrics 算的值。
|
|
IP 服务端从 HTTP 头拿,不在这里。
|
|
"""
|
|
screen: str = Field("", max_length=32)
|
|
device_model: str = Field("", max_length=64)
|
|
|
|
|
|
class BindInviteIn(BaseModel):
|
|
# invite_code: 可选 — 走指纹兜底时为空(channel=fingerprint)
|
|
invite_code: str | None = Field(
|
|
None, max_length=16, description="邀请码;走指纹兜底时为空"
|
|
)
|
|
channel: str = Field(
|
|
"clipboard", max_length=16,
|
|
description="归因来源:clipboard(首启读剪贴板自动) / manual(手动填) / fingerprint(指纹兜底)",
|
|
)
|
|
# 当 invite_code 为空、channel=fingerprint 时,后端用这组指纹反查
|
|
fingerprint: FingerprintIn | None = None
|
|
|
|
|
|
class BindInviteOut(BaseModel):
|
|
status: str # success / already_bound / invalid_code / self_invite / not_eligible / fp_not_found
|
|
coins_awarded: int # 邀请金币已下线,保留字段兼容客户端(恒为 0)
|
|
message: str # 给前端直接展示的文案
|
|
|
|
|
|
class InviteeItem(BaseModel):
|
|
"""被邀请人列表的一条(邀请页小窗 / 完整列表页共用)。
|
|
|
|
display_name / avatar_url 的"降级兜底"在后端算好(见 repositories/invite.get_invitees):
|
|
名字 = 昵称 → 微信昵称 → 脱敏手机号(前端拿不到完整号,必须后端脱敏);
|
|
头像 = 用户头像 → 微信头像 → null(前端拿到 null 画默认色块)。
|
|
"""
|
|
display_name: str # 已兜底好的显示名(真名/脱敏号)
|
|
avatar_url: str | None = None # 头像 URL;null = 前端画默认色块
|
|
coins: int # 邀请金币已下线,保留字段兼容客户端(恒为 0)
|
|
invited_at: datetime # 邀请绑定时间(前端转"今天/3天前")
|
|
|
|
|
|
class InviteeListOut(BaseModel):
|
|
"""GET /invite/invitees 响应:被邀请人列表 + 分页。"""
|
|
items: list[InviteeItem]
|
|
total: int # 我邀请的总人数(用于"共 N 人")
|
|
has_more: bool # 还有下一页吗(完整列表页滚到底加载更多)
|