背景
#327 在内测阶段关闭了公开注册。现在重新开放注册,以及验证码登录自动建号。邀请码是注册阶段的可选拉新手段,不是登录后补填关系。
后端实现见 #357;账号中心 / 工作台入口见 #359、#362、#363。
产品规则
-
注册邀请码选填
POST /auth/register 的 invite_code 可选。
- 有效且未过期:注册赠送 + 被邀请人邀请奖励;邀请人奖励受日限额
- 缺省或空白:只发注册赠送
- 非法或不存在:不建号,「邀请码无效」
- 已过期:不建号,「邀请码已过期」(业务码 404)
-
邀请码只在注册时生效
没有登录后补填。错过注册填写,只能再通过邀请别人获得奖励。分享链接仍是 /?account=register&invite=XXXX。
-
验证码登录自动建号
POST /auth/login-by-code:未知邮箱建号并送注册积分;已有账号只登录。此接口不兑换邀请码。
-
邀请码有效期
生成后 QUOTA_INVITE_CODE_TTL_DAYS 天内有效,默认 30 天。过期后该码不能再用于注册。GET /quota/invite/code 若当前码已过期,自动签发新码(旧行保留)。
-
邀请码只增不删、全局唯一
每个 code 永不复用、不覆盖。轮换是插入新行,并把仍有效的旧码 expires_at 置为当前时间(立即失效,行仍在,已发生的邀请记录继续指向旧码)。
-
邀请人每日奖励上限(不是流水总入账上限)
未过期的码仍可被任意多名新用户注册使用,邀请关系照写。used_count 只计数、不封顶。
邀请人每个 UTC 自然日最多因 3 次成功邀请获得奖励(QUOTA_INVITE_REWARD_DAILY_LIMIT,默认 3 × 200 = 600)。第 4 人及以后:关系仍写入,被邀请人仍得邀请奖励,邀请人当日不再入账。次日按 UTC 零点重置。
计数口径:只数 windup_invite_record 里该邀请人当日新增的邀请关系,不扫描 windup_credit_transaction。因此注册赠送、生成扣费、以及后续付费购买 / 套餐兑换(应使用独立 CreditReason)都不会占用这 3 次 / 600 分。
-
积分
| 场景 |
新用户 |
邀请人 |
| 无邀请码注册 |
注册赠送 300 |
— |
| 有未过期邀请码注册(邀请人当日第 1–3 次) |
注册赠送 300 + 邀请奖励 200 |
邀请奖励 200 |
| 有未过期邀请码注册(邀请人当日第 4 次及以后) |
注册赠送 300 + 邀请奖励 200 |
0(关系仍写入) |
| 验证码登录新建号 |
注册赠送 300 |
— |
QUOTA_REGISTER_GIFT_AMOUNT 默认 300;QUOTA_INVITE_REWARD_AMOUNT 默认 200。
流水:REGISTER_GIFT / register:{user_id};INVITE_REWARD / invite:{invitee_id}:inviter 与 invite:{invitee_id}:invitee。邀请人超限时不写 *:inviter 那条流水。
-
形态
字符集 ABCDEFGHJKLMNPQRSTUVWXYZ23456789(排除 I/O/0/1),长度 4–16,生成长度 8,大小写规范化。
主流程
注册
├─ 无 invite_code ──► 建号 + 300
├─ 有效未过期 ──► 建号 + 300 + 被邀请人 200
│ ├─ 邀请人当日第 1–3 次 ──► 邀请人 +200(当日最多 600)
│ └─ 邀请人当日第 4 次及以后 ──► 邀请人 +0,关系仍写入
├─ 已过期 ──► 不建号,「邀请码已过期」
└─ 非法/不存在 ──► 不建号,「邀请码无效」
验证码登录
├─ 邮箱已存在 ──► 只登录
└─ 邮箱不存在 ──► 建号 + 300(不兑邀请码)
我的邀请码
GET /quota/invite/code 返回未过期码;没有或已过期则签发新行
POST /quota/invite/generate 插入新码,旧码立即过期但行保留
数据模型
windup_invite_code(历史码全部保留)
| 列 |
约束 |
说明 |
id |
PK |
|
user_id |
NOT NULL, 索引(非唯一) |
同一用户可有多行 |
code |
UNIQUE NOT NULL, String(16) |
全局唯一,只增不改 |
used_count |
NOT NULL, 默认 0 |
该码成功邀请次数 |
expires_at |
timestamptz NOT NULL |
过期时间;轮换时把旧行提前截断到现在 |
create_at / update_at |
timestamptz |
|
当前有效码 = 该用户 expires_at > now 中 id 最大的一行。
windup_invite_record
| 列 |
约束 |
说明 |
id |
PK |
|
inviter_id |
NOT NULL, 索引 |
|
invitee_id |
UNIQUE NOT NULL |
每人最多一次邀请关系(仅注册时写入) |
code |
NOT NULL |
注册时使用的码,可指向已过期历史行 |
create_at |
timestamptz |
日限额按此字段的 UTC 自然日计数 |
API
鉴权:register / send-code / login-by-code 白名单;invite 的 GET/POST generate 需登录。
已删除 POST /quota/invite/redeem(不再支持登录后补填)。
POST /auth/register
invite_code 可省略。过期码返回 message「邀请码已过期」、code=404。邀请人超日限额时仍建号,不返回错误。
POST /auth/login-by-code
未知邮箱建号并送注册积分。
GET /quota/invite/code
{
"code": "AB23CD45",
"used_count": 3,
"expires_at": "2026-09-17T01:02:03Z",
"create_at": "2026-08-18T01:02:03Z",
"update_at": "2026-08-18T01:02:03Z"
}
POST /quota/invite/generate
插入新码并立即过期旧码。响应同上。
业务错误文案
| 情况 |
message |
code |
| 非法或不存在 |
邀请码无效 |
400 |
| 已过期 |
邀请码已过期 |
404 |
| 邮箱已注册 |
邮箱已注册 |
400 |
| 账号封禁 |
账号已被封禁 |
400 |
前端分工(本 issue 不改前端)
| PR |
内容 |
| #362 |
账号中心展示邀请码与分享链接;需去掉补填,并处理过期链接 |
| #363 |
工作台入口提示(双方各得 200;可提示邀请人每日最多 3 次 / 600 分) |
不包含
- 运营后台批量发码
- 邀请排行 / 独立落地页
- 邀请人数终身上限
- 登录后补填邀请码
- 验证码登录时兑换邀请码
- 付费购买积分、套餐兑换积分(独立
CreditReason,不走邀请日限额)
验收
- 不带邀请码可注册,只发 300
- 未过期邀请码注册:新用户 500;邀请人当日第 1–3 次得 200
- 同一 UTC 日第 4 次邀请:仍建号、仍写关系、被邀请人仍得 200,邀请人当日不再 +200
- 过期码不建号,返回「邀请码已过期」
- 轮换后旧码行仍在且不能再注册;新码可用
GET 在旧码过期后签发新码,不覆盖旧行
login-by-code 未知邮箱建号送 300
- 无
POST /quota/invite/redeem
openapi.json 同步
相关
背景
#327 在内测阶段关闭了公开注册。现在重新开放注册,以及验证码登录自动建号。邀请码是注册阶段的可选拉新手段,不是登录后补填关系。
后端实现见 #357;账号中心 / 工作台入口见 #359、#362、#363。
产品规则
注册邀请码选填
POST /auth/register的invite_code可选。邀请码只在注册时生效
没有登录后补填。错过注册填写,只能再通过邀请别人获得奖励。分享链接仍是
/?account=register&invite=XXXX。验证码登录自动建号
POST /auth/login-by-code:未知邮箱建号并送注册积分;已有账号只登录。此接口不兑换邀请码。邀请码有效期
生成后
QUOTA_INVITE_CODE_TTL_DAYS天内有效,默认 30 天。过期后该码不能再用于注册。GET /quota/invite/code若当前码已过期,自动签发新码(旧行保留)。邀请码只增不删、全局唯一
每个
code永不复用、不覆盖。轮换是插入新行,并把仍有效的旧码expires_at置为当前时间(立即失效,行仍在,已发生的邀请记录继续指向旧码)。邀请人每日奖励上限(不是流水总入账上限)
未过期的码仍可被任意多名新用户注册使用,邀请关系照写。
used_count只计数、不封顶。邀请人每个 UTC 自然日最多因 3 次成功邀请获得奖励(
QUOTA_INVITE_REWARD_DAILY_LIMIT,默认 3 × 200 = 600)。第 4 人及以后:关系仍写入,被邀请人仍得邀请奖励,邀请人当日不再入账。次日按 UTC 零点重置。计数口径:只数
windup_invite_record里该邀请人当日新增的邀请关系,不扫描windup_credit_transaction。因此注册赠送、生成扣费、以及后续付费购买 / 套餐兑换(应使用独立CreditReason)都不会占用这 3 次 / 600 分。积分
QUOTA_REGISTER_GIFT_AMOUNT默认 300;QUOTA_INVITE_REWARD_AMOUNT默认 200。流水:
REGISTER_GIFT/register:{user_id};INVITE_REWARD/invite:{invitee_id}:inviter与invite:{invitee_id}:invitee。邀请人超限时不写*:inviter那条流水。形态
字符集
ABCDEFGHJKLMNPQRSTUVWXYZ23456789(排除 I/O/0/1),长度 4–16,生成长度 8,大小写规范化。主流程
数据模型
windup_invite_code(历史码全部保留)iduser_idcodeused_countexpires_atcreate_at/update_at当前有效码 = 该用户
expires_at > now中id最大的一行。windup_invite_recordidinviter_idinvitee_idcodecreate_atAPI
鉴权:register / send-code / login-by-code 白名单;invite 的 GET/POST generate 需登录。
已删除
POST /quota/invite/redeem(不再支持登录后补填)。POST /auth/registerinvite_code可省略。过期码返回 message「邀请码已过期」、code=404。邀请人超日限额时仍建号,不返回错误。POST /auth/login-by-code未知邮箱建号并送注册积分。
GET /quota/invite/code{ "code": "AB23CD45", "used_count": 3, "expires_at": "2026-09-17T01:02:03Z", "create_at": "2026-08-18T01:02:03Z", "update_at": "2026-08-18T01:02:03Z" }POST /quota/invite/generate插入新码并立即过期旧码。响应同上。
业务错误文案
前端分工(本 issue 不改前端)
不包含
CreditReason,不走邀请日限额)验收
GET在旧码过期后签发新码,不覆盖旧行login-by-code未知邮箱建号送 300POST /quota/invite/redeemopenapi.json同步相关