4d392f44f5
- user.invite_code 列 + invite_relation 表(invitee_user_id 唯一 = 幂等防重复发奖) - GET /api/v1/invite/me(我的码+分享链接+战绩)、POST /api/v1/invite/bind - 复用 wallet.grant_coins 同事务发币;自邀屏蔽 / 无效码 / 新用户闸(注册72h内才发) - alembic 迁移(11a1d08c6f55 -> invite_code_and_relation)+ 8 个测试(钱路/幂等/并发兜底全覆盖) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Reviewed-on: #24
47 lines
2.0 KiB
Python
47 lines
2.0 KiB
Python
"""好友邀请关系表(注册即生效)。
|
|
|
|
一行 = 一次成功的邀请绑定。`invitee_user_id` 唯一 = 一个被邀请人只能被归因一次
|
|
(天然幂等键,防重复发奖,仿 ad_reward.trans_id 思路)。`channel` 记归因来源
|
|
(clipboard 自动 / manual 手动填),便于后续分析"剪贴板自动捕获率"。
|
|
|
|
邀请码本身是 `user.invite_code`(一人一稳定码,懒生成),不在这张表里。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class InviteRelation(Base):
|
|
__tablename__ = "invite_relation"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
inviter_user_id: Mapped[int] = mapped_column(
|
|
Integer, ForeignKey("user.id"), index=True, nullable=False
|
|
)
|
|
# 被邀请人唯一:一个人只能被邀一次(幂等键,防重复发奖)
|
|
invitee_user_id: Mapped[int] = mapped_column(
|
|
Integer, ForeignKey("user.id"), unique=True, index=True, nullable=False
|
|
)
|
|
# 归因来源:clipboard(剪贴板自动) / manual(手动填码)
|
|
channel: Mapped[str] = mapped_column(String(16), nullable=False, default="clipboard")
|
|
# 状态:effective(注册即生效)。预留:以后改"完成首单才生效"时用 pending/effective
|
|
status: Mapped[str] = mapped_column(String(16), nullable=False, default="effective")
|
|
# 本次给邀请人 / 被邀请人各发的金币(记账留痕,便于对账)
|
|
inviter_coin: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
invitee_coin: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True, nullable=False
|
|
)
|
|
|
|
def __repr__(self) -> str: # pragma: no cover
|
|
return (
|
|
f"<InviteRelation inviter={self.inviter_user_id} "
|
|
f"invitee={self.invitee_user_id} {self.status}>"
|
|
)
|