- 新增忘记密码页面与重置密码确认流程(前端+后端) - 修复注册验证码页登录跳转路由 - 新增用户搜索API(按邮箱查询) - 简化infra脚本,统一为app.sh - 补充密码重置与用户API测试覆盖 - 更新runtime文档与AGENTS配置
10 KiB
Invite Code Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: 在现有 OTP 注册链路中引入邀请码能力,支持用户自动生成专属邀请码、注册时可选填邀请码并记录邀请关系与使用次数。
Architecture: 采用数据库中心实现:通过 Alembic 新增 invite_codes 表、扩展 profiles 字段,并在 auth.users 的现有 trigger 函数中完成邀请码校验与记账,保证注册与邀请关系写入尽量原子。应用层只负责透传 invite_code 到 Supabase raw_user_meta_data。
Tech Stack: FastAPI, SQLAlchemy, Alembic, Supabase Auth, PostgreSQL PL/pgSQL, Pytest
Task 1: 更新注册请求 Schema(TDD)
Files:
- Modify:
backend/src/v1/auth/schemas.py - Modify:
backend/tests/integration/test_auth_routes.py
Step 1: Write the failing test
在 test_signup_start_returns_pending_response 基础上新增断言路径:请求体带 invite_code 时返回仍为 202,且未触发 422。
Step 2: Run test to verify it fails
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k signup_start_returns_pending_response -v
Expected: FAIL(invite_code 为额外字段或校验不通过)
Step 3: Write minimal implementation
在 VerificationCreateRequest 增加可选字段:
invite_code: str | None = Field(default=None, min_length=8, max_length=8)
Step 4: Run test to verify it passes
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k signup_start_returns_pending_response -v
Expected: PASS
Step 5: Commit
git add backend/src/v1/auth/schemas.py backend/tests/integration/test_auth_routes.py
git commit -m "feat: accept invite code in signup request"
Task 2: 透传 invite_code 到 Supabase metadata(TDD)
Files:
- Modify:
backend/src/v1/auth/gateway.py - Modify:
backend/tests/unit/v1/auth/test_auth_service.py
Step 1: Write the failing test
在 test_supabase_signup_passes_username_in_metadata 增加 invite_code 并断言:
assert captured_payload["data"] == {
"username": "demo",
"invite_code": "A1B2C3D4",
}
Step 2: Run test to verify it fails
Run: cd backend && uv run pytest tests/unit/v1/auth/test_auth_service.py -k metadata -v
Expected: FAIL(metadata 未包含 invite_code)
Step 3: Write minimal implementation
在 create_verification 中构建 metadata:
metadata = {"username": request.username}
if request.invite_code:
metadata["invite_code"] = request.invite_code
payload = {
"email": request.email,
"password": request.password,
"data": metadata,
}
Step 4: Run test to verify it passes
Run: cd backend && uv run pytest tests/unit/v1/auth/test_auth_service.py -k metadata -v
Expected: PASS
Step 5: Commit
git add backend/src/v1/auth/gateway.py backend/tests/unit/v1/auth/test_auth_service.py
git commit -m "feat: pass invite code through signup metadata"
Task 3: 新增 invite_codes 表与 profiles.referred_by(迁移先行)
Files:
- Create:
backend/alembic/versions/20260227_0006_invite_codes_and_profile_referral.py - Modify:
backend/src/models/profile.py - Create:
backend/src/models/invite_code.py - Modify:
backend/src/models/__init__.py
Step 1: Write the failing test
在 backend/tests/unit/database/test_profile_models.py 新增 referred_by 读写测试;新增 backend/tests/unit/database/test_invite_code_models.py 验证 InviteCode 基本创建与约束字段。
Step 2: Run test to verify it fails
Run: cd backend && uv run pytest tests/unit/database/test_profile_models.py tests/unit/database/test_invite_code_models.py -v
Expected: FAIL(字段/模型不存在)
Step 3: Write minimal implementation
-
Alembic 创建
invite_codes:code唯一索引owner_id外键到profiles.id(可空)status、used_count、max_usescheck 约束max_uses默认NULL(无限制)expires_at默认NULL(无限制)reward_configJSONB 默认{}- 启用 RLS(按项目默认 deny-all)
-
注意:本期不开放 invite_codes 表直接读取,用户邀请码通过 profile 聚合接口返回(后续实现)
-
Alembic 给
profiles增加referred_by+ 索引 + 外键 -
ORM 同步
Profile.referred_by与InviteCode模型
Step 4: Run test to verify it passes
Run: cd backend && uv run pytest tests/unit/database/test_profile_models.py tests/unit/database/test_invite_code_models.py -v
Expected: PASS
Step 5: Commit
git add backend/alembic/versions/20260227_0006_invite_codes_and_profile_referral.py backend/src/models/profile.py backend/src/models/invite_code.py backend/src/models/__init__.py backend/tests/unit/database/test_profile_models.py backend/tests/unit/database/test_invite_code_models.py
git commit -m "feat: add invite code schema and profile referral fields"
Task 4: 扩展注册 trigger 生成邀请码并消费邀请(TDD)
Files:
- Modify:
backend/alembic/versions/20260227_0006_invite_codes_and_profile_referral.py - Modify:
backend/tests/integration/test_auth_routes.py
Step 1: Write the failing test
新增集成测试(建议通过测试替身/fixture 验证行为):
- 注册不带邀请码时,profile 创建后存在 owner 邀请码
- 注册带有效邀请码时,
referred_by生效且used_count + 1
Step 2: Run test to verify it fails
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k invite -v
Expected: FAIL(触发器逻辑尚未实现)
Step 3: Write minimal implementation
在迁移 SQL 中:
- 新增 helper function:生成 8 位随机码(排除易混淆字符 0/O/1/I/L,冲突重试)
- 重建
public.create_profile_for_new_user():- 插入
profiles - 创建该用户专属
invite_codes(owner_id = NEW.id) - 读取
NEW.raw_user_meta_data ->> 'invite_code' - 校验邀请码状态/过期/次数
- 若有效:更新
profiles.referred_by,并used_count = used_count + 1
- 插入
Step 4: Run test to verify it passes
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k invite -v
Expected: PASS
Step 5: Commit
git add backend/alembic/versions/20260227_0006_invite_codes_and_profile_referral.py backend/tests/integration/test_auth_routes.py
git commit -m "feat: extend signup trigger for invite code generation and usage"
Task 5: 覆盖邀请码边界场景(TDD)
Files:
- Modify:
backend/tests/integration/test_auth_routes.py - Optional Modify:
backend/tests/e2e/test_auth_flow.py
Step 1: Write the failing test
新增场景测试:
- 邀请码不存在
- 邀请码 disabled
- 邀请码 expires_at 已过期
- 邀请码达到
max_uses
断言:注册仍成功(202/200 链路正常),仅邀请关系不建立。
Step 2: Run test to verify it fails
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k "invite and (expired or disabled or max_uses or invalid)" -v
Expected: FAIL
Step 3: Write minimal implementation
修正 trigger 判断顺序和条件,确保“邀请无效不影响注册”原则。
Step 4: Run test to verify it passes
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k invite -v
Expected: PASS
Step 5: Commit
git add backend/tests/integration/test_auth_routes.py backend/alembic/versions/20260227_0006_invite_codes_and_profile_referral.py
git commit -m "test: cover invite code edge cases in signup flow"
Task 6: 文档同步与运行手册更新
Files:
- Modify:
docs/runtime/runtime-route.md - Modify:
docs/runtime/runtime-runbook.md
Step 1: Write the failing test
无自动化测试;改为文档一致性检查清单(手工):
- 注册接口 request 字段包含
invite_code - 说明邀请码消费时机与“无效码不阻断注册”
Step 2: Run check to verify missing docs
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k signup_start -v
Expected: PASS(作为行为基线),文档尚未同步
Step 3: Write minimal implementation
- 更新
POST /auth/verifications请求字段 - 新增邀请码行为说明
- 在 runbook 变更日志添加本次改动记录
Step 4: Run check after docs update
Run: cd backend && uv run pytest tests/integration/test_auth_routes.py -k signup_start -v
Expected: PASS(行为与文档一致)
Step 5: Commit
git add docs/runtime/runtime-route.md docs/runtime/runtime-runbook.md
git commit -m "docs: document invite code behavior in signup flow"
Task 7: 全量验证与风险审查(L2)
Files:
- Verify only
Step 1: Run lint/type checks
Run:
cd backend && uv run ruff check src testscd backend && uv run basedpyright src
Expected: 全部通过
Step 2: Run test suites
Run:
cd backend && uv run pytest tests/unit -vcd backend && uv run pytest tests/integration -vcd backend && uv run pytest tests/e2e/test_auth_flow.py -v
Expected: 通过
Step 3: Run mandatory review gates for L2
refactor-cleaneragent:确认无死代码/重复代码code-revieweragent:检查 DB trigger、安全边界、可维护性
Expected: CRITICAL/HIGH 为 0
Step 4: Security-specific sanity checks
检查项:
- 未硬编码密钥
- SQL 逻辑无注入风险(trigger 中仅参数/列操作)
- 邀请码校验失败不泄露内部细节
Step 5: Commit verification evidence (if needed in docs/PR notes)
git add <updated verification notes if any>
git commit -m "chore: record invite code verification results"
交付验收标准
- 新用户注册后必有 1 条专属邀请码。
- 注册时传入有效邀请码会建立
profiles.referred_by并增加used_count。 - 无效邀请码不会阻断注册成功。
- 支持运营码(
owner_id IS NULL)与后续奖励扩展(reward_config)。 - 文档已同步,测试与检查通过。
备注
- 本需求触发 L2(数据库迁移 + trigger + 多文件大改),必须走双审查 gate。
- 不在本期实现运营后台批量发码 API;仅完成数据层与注册链路支撑。