知识库首页 知识库-世界 cc-REFERRAL_ATTRIBUTION_IMPLEMENTATION.md

cc REFERRAL ATTRIBUTION IMPLEMENTATION

本地来源:Knowledge/World/项目/文档/referral/cc-REFERRAL_ATTRIBUTION_IMPLEMENTATION.md

用户来源追踪与邀请奖励系统 - 完整实施文档

文档版本:v1.1 创建日期:2025-01-09 更新日期:2026-01-09 适用项目:seedvr2.net (Next.js 15 + Drizzle ORM + PostgreSQL) 支付系统:Stripe + Creem 双支付


目录

  1. 系统架构总览
  2. 优先级P0:UTM归因采集
  3. 优先级P1:邀请奖励系统
  4. 优先级P2:广告回传框架
  5. 数据库设计
  6. 环境变量配置
  7. 文件改动清单
  8. 实施步骤
  9. 防刷机制
  10. 测试验证

一、系统架构总览

1.1 三层追踪体系

``` ┌─────────────────────────────────────────────────────────────┐ │ 用户来源追踪体系 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌───────────┐ │ │ │ P0. UTM 归因 │ │ P1. 邀请奖励 │ │ P2. 广告 │ │ │ │ (数据分析基础) │ │ (用户增长) │ │ 回传 │ │ │ ├─────────────────┤ ├─────────────────┤ ├───────────┤ │ │ │ ?utm_source= │ │ ?invite=ABC123 │ │ gclid │ │ │ │ google │ │ │ │ fbclid │ │ │ │ ?utm_campaign= │ │ 注册:双方+基础奖励 │ │ │ │ │ │ summer_sale │ │ 首单:仅邀请人10%│ │ 付费后 │ │ │ │ │ │ 上限:1000 │ │ 回传转化 │ │ │ ├─────────────────┤ ├─────────────────┤ ├───────────┤ │ │ │ ✅ 自建 │ │ ✅ 自建 │ │ 需要调用 │ │ │ │ ❌ 不需要Consent │ │ ❌ 不需要Consent │ │ Meta/Google│ │ │ │ │ │ │ │ API │ │ │ │ 存储:Cookie │ │ 存储:数据库 │ │ ✅ 需要 │ │ │ │ │ │ │ │ Consent │ │ │ └─────────────────┘ └─────────────────┘ └───────────┘ │ │ │ │ IP地理定位方案:Cloudflare Headers(免费,0ms延迟) │ │ 支付系统:Stripe + Creem(两边都要处理邀请奖励) │ └─────────────────────────────────────────────────────────────┘ ```

1.2 数据流向

``` 用户访问(携带 ?invite=xxx 或 ?utm_source=xxx) ↓ ┌──────────────────────────────────────┐ │ Middleware │ │ 1. 提取 UTM 参数 → Cookie │ │ 2. 提取 invite 参数 → Cookie │ │ 3. 提取 Click IDs → Cookie │ │ 4. 获取 Cloudflare Geo Headers │ └──────────────────────────────────────┘ ↓ 用户注册(检查 invite Cookie) ↓ ┌──────────────────────────────────────┐ │ 注册时处理(auth.ts) │ │ 1. 验证邀请码有效性 │ │ 2. 创建 referral 记录 │ │ 3. 等待邮箱验证 │ └──────────────────────────────────────┘ ↓ 用户首单支付(Stripe/Creem Webhook) ↓ ┌──────────────────────────────────────┐ │ 支付成功时处理 │ │ 1. 检查是否为首单 │ │ 2. 检查是否有邀请关系 │ │ 3. 计算奖励积分(订单积分×10%) │ │ 4. 仅邀请人发放积分 │ │ 5. 更新 referral 记录 │ │ 6. [P2] 触发广告回传 │ └──────────────────────────────────────┘ ```

1.3 项目技术栈(seedvr2.net 实际配置)

组件 技术 说明
框架 Next.js 15 App Router
数据库 PostgreSQL + Drizzle ORM Neon 托管,使用 `drizzle` schema
认证 Better Auth 支持 Google/GitHub 登录
支付 Stripe + Creem 双支付系统
积分系统 已有完整实现 `src/credits/credits.ts`
积分类型 enum CREDIT_TRANSACTION_TYPE `src/credits/types.ts`
部署 Vercel 通过 Cloudflare CDN
ID 类型 uuid 所有表的 ID 都是 uuid 类型

二、优先级P0:UTM归因采集

2.1 功能说明

项目
目的 知道用户从哪个渠道来(分析用)
数据存储 Cookie + payment.metadata
是否需要数据库表 ❌ 不需要(存 Cookie 够用)
是否需要Consent ❌ 不需要(纯内部分析)

2.2 采集的参数

```typescript // UTM 参数 const UTM_PARAMS = [ 'utm_source', // 来源(google, facebook, twitter...) 'utm_medium', // 媒介(cpc, organic, email, social...) 'utm_campaign', // 活动名称(summer_sale, black_friday...) 'utm_term', // 关键词(仅付费搜索用) 'utm_content', // 内容变体(banner_v1, text_link...) ] as const;

// 广告平台 Click IDs const CLICK_IDS = [ 'gclid', // Google Ads(90天有效) 'gbraid', // Google Ads iOS(30天有效) 'wbraid', // Google Ads Web-to-App(30天有效) 'fbclid', // Meta/Facebook(7天有效) 'msclkid', // Microsoft Ads(90天有效) 'ttclid', // TikTok Ads(28天有效) ] as const; ```

2.3 Cookie 设计

Cookie 名 用途 有效期 HttpOnly 说明
`attr_first` 首次触点 1年 永不覆盖
`attr_last` 末次触点 30分钟 每次有新数据时更新

2.4 Cookie 数据结构

```typescript interface AttributionData { utm: { utm_source?: string; utm_medium?: string; utm_campaign?: string; utm_term?: string; utm_content?: string; }; clickIds: { gclid?: string; fbclid?: string; msclkid?: string; // ... }; referrer: string | null; // 来源页面 referrerType: 'direct' | 'organic' | 'social' | 'referral' | 'paid'; landingPage: string; // 落地页路径 timestamp: number; // 时间戳 geo?: { country?: string; // 从 Cloudflare Headers 获取 city?: string; }; } ```

2.5 IP 地理定位方案

采用方案:Cloudflare Headers(免费,0ms延迟)

```typescript // 在 Middleware 中读取 const ip = request.headers.get('cf-connecting-ip') || request.headers.get('x-forwarded-for')?.split(',')[0] || request.headers.get('x-real-ip');

const geo = { country: request.headers.get('cf-ipcountry') // 如: CN, US || request.headers.get('x-vercel-ip-country'), city: request.headers.get('cf-ipcity') // 如: Shanghai || request.headers.get('x-vercel-ip-city'), }; ```

方案 成本 延迟 推荐度
Cloudflare Headers 免费 0ms ⭐⭐⭐⭐⭐
Vercel Geo Headers Pro计划免费 0ms ⭐⭐⭐⭐
MaxMind API 按量付费 +50ms ⭐⭐

三、优先级P1:邀请奖励系统

3.1 功能概述

项目
目的 用户增长 + 用户福利
奖励形式 积分(站内货币,不涉及提现)
层级 单层(A 邀请 B,B 再邀请 C 时 A 不获益)
是否需要第三方 ❌ 完全自建
是否需要Consent ❌ 不需要
裂变支持 ✅ 被邀请人可以成为邀请人

3.2 奖励规则(最终确认版)

注册奖励

角色 正常注册 被邀请注册 说明
邀请人(A) - +基础奖励 额外奖励(= INITIAL_CREDITS_AMOUNT)
被邀请人(B) 基础奖励 基础奖励 + 基础奖励 = 2×基础奖励 基础 + 额外

基础奖励基数读取 `INITIAL_CREDITS_AMOUNT`(只读配置)。

发放条件:被邀请人完成邮箱验证后发放

首单奖励

角色 奖励 计算公式 示例
邀请人(A) 订单积分 × 10% `Math.floor(orderCredits * 0.1)` 订单300积分 → +30
被邀请人(B) ❌ 无首单奖励 - -

重要规则: - ✅ 仅首单有奖励,续费不算 - ✅ 订阅和积分包购买都算首单 - ✅ 10% 向下取整(如 55 积分订单 → 5 积分奖励) - ✅ 单次上限 1,000 积分 - ✅ 奖励积分 30 天过期(和正常积分一样) - ✅ 首单奖励仅给邀请人,被邀请人无此奖励

3.3 邀请码设计

配置项 说明
格式 `u_` + 8位哈希 如 `u_a1b2c3d4`
字符集 `a-z0-9` 小写字母 + 数字
生成时机 用户注册时自动生成 每个用户都有
唯一性 数据库 unique 约束 冲突时重新生成

邀请码生成函数

```typescript // src/lib/referral.ts

function generateInviteCode(userId: string): string { // 方案:u_ + 用户ID的短哈希 const hash = crypto .createHash('md5') .update(userId + Date.now().toString()) .digest('hex') .slice(0, 8); return `u_\${hash}`; } ```

3.4 邀请链接设计

配置项
URL 格式 `https://domain.com?invite=u_a1b2c3d4`
参数名 `invite`
Cookie 名 `invite_code`
Cookie 有效期 30 天
落地页 无专门页面,直接跳首页

3.5 邀请管理页面

页面路径:`/[locale]/(protected)/settings/referral`

⚠️ 注意:seedvr2.net 使用 `(protected)` 路由组,不是 `(app)`

访问权限: - 已登录用户:显示完整邀请信息和统计 - 未登录用户:显示邀请规则说明(引导注册)


五、数据库设计

5.1 新增 referral 表

```typescript // src/db/schema.ts 新增 // ⚠️ 重要:seedvr2.net 使用 drizzleSchema 命名空间 + uuid 类型(与现有 user 表一致)

// 在 schema.ts 文件顶部已有: // const drizzleSchema = pgSchema('drizzle');

export const referral = drizzleSchema.table('referral', { id: uuid('id').defaultRandom().primaryKey(),

// 邀请关系(uuid 类型与 user.id 一致) inviterId: uuid('inviter_id').notNull().references(() => user.id, { onDelete: 'cascade' }), inviteeId: uuid('invitee_id').notNull().references(() => user.id, { onDelete: 'cascade' }), inviteCode: text('invite_code').notNull(),

// 奖励统计 inviterReward: integer('inviter_reward').default(0).notNull(), inviteeReward: integer('invitee_reward').default(0).notNull(),

// 状态追踪 status: text('status').default('pending').notNull(), // pending | verified | rewarded emailVerified: boolean('email_verified').default(false).notNull(), firstOrderPaid: boolean('first_order_paid').default(false).notNull(), firstOrderId: uuid('first_order_id'),

// 防刷信息 inviteeIp: text('invitee_ip'), inviteeDevice: text('invitee_device'),

// 时间 createdAt: timestamp('created_at').defaultNow().notNull(), verifiedAt: timestamp('verified_at'), firstOrderAt: timestamp('first_order_at'), }, (table) => ({ referralInviterIdx: index('referral_inviter_idx').on(table.inviterId), referralInviteeIdx: index('referral_invitee_idx').on(table.inviteeId), referralCodeIdx: index('referral_code_idx').on(table.inviteCode), referralStatusIdx: index('referral_status_idx').on(table.status), })); ```

5.2 修改 user 表

```typescript // src/db/schema.ts 修改 user 表,添加字段 // ⚠️ 注意:seedvr2.net user 表使用 uuid 类型

export const user = drizzleSchema.table("user", { // ... 现有字段 ...

// 新增:邀请系统 inviteCode: text('invite_code').unique(), invitedBy: uuid('invited_by'), invitedAt: timestamp('invited_at'), }, (table) => ({ // ... 现有索引 ... userInviteCodeIdx: index('user_invite_code_idx').on(table.inviteCode), })); ```

5.3 新增积分类型

```typescript // src/credits/types.ts 修改 CREDIT_TRANSACTION_TYPE // ⚠️ 注意:seedvr2.net 使用 enum 形式,不是 const

export enum CREDIT_TRANSACTION_TYPE { // ... 现有类型 ...

// 新增:邀请奖励 REFERRAL_REGISTER_INVITER = 'REFERRAL_REGISTER_INVITER', REFERRAL_REGISTER_INVITEE = 'REFERRAL_REGISTER_INVITEE', REFERRAL_PURCHASE_INVITER = 'REFERRAL_PURCHASE_INVITER', } ```


六、环境变量配置

6.1 邀请奖励系统(P1)

```bash

功能开关

REFERRAL_ENABLED=true

奖励配置

REFERRAL_PURCHASE_RATE=0.10 # 首单奖励比例(10%,仅邀请人获得) REFERRAL_MAX_REWARD=1000 # 单次奖励上限 REFERRAL_REWARD_EXPIRE_DAYS=30 # 奖励积分过期天数

邀请码配置

REFERRAL_CODE_PREFIX=u_ REFERRAL_CODE_LENGTH=8

Cookie 配置

REFERRAL_COOKIE_NAME=invite_code REFERRAL_COOKIE_MAX_AGE=2592000 # 30天 ```


七、文件改动清单

7.1 新建文件

文件路径 用途 优先级
`src/lib/referral.ts` 邀请系统核心逻辑 P1
`src/lib/attribution.ts` 归因数据读取工具 P0
`src/app/[locale]/(protected)/settings/referral/page.tsx` 邀请管理页面 P1
`src/app/[locale]/(protected)/settings/referral/layout.tsx` 页面布局 P1
`src/app/(default)/(protected)/settings/referral/page.tsx` 默认语言版本 P1
`src/app/api/referral/validate/route.ts` 验证邀请码 API P1

7.2 修改文件

文件路径 改动内容 优先级
`src/middleware.ts` 添加 UTM/invite 参数捕获 P0
`src/db/schema.ts` 添加 referral 表 + user 表新字段 P1
`src/lib/auth.ts` 注册时处理邀请关系 P1
`src/credits/types.ts` 添加邀请奖励积分类型(enum) P1
`src/payment/provider/stripe.ts` 首单支付时发放邀请奖励 P1
`src/payment/provider/creem-utils.ts` 首单支付时发放邀请奖励 P1
`src/routes.ts` 添加 SettingsReferral 路由 P1
`messages/en.json` 邀请页面英文文案 P1
`messages/zh.json` 邀请页面中文文案 P1

修订记录

版本 日期 修改内容
v1.0 2025-01-09 初始版本
v1.1 2026-01-09 适配 seedvr2.net:使用 drizzleSchema + uuid、enum 积分类型、(protected) 路由组

本文档为站内渲染。原始文件本地路径:saas/source/knowledge-world/Knowledge-World-项目-文档-referral-cc-REFERRAL_ATTRIBUTION_IMPLE-672046.md(仅本地保留,不入库不部署)