# 邀请码系统设计文档 > **版本**:v1.0 > **最后更新**:2026-05-13 > **状态**:✅ 已实现 --- ## 📋 目录 1. [系统概述](#系统概述) 2. [核心功能](#核心功能) 3. [技术实现](#技术实现) 4. [数据库设计](#数据库设计) 5. [业务流程](#业务流程) 6. [API接口](#api接口) 7. [前端实现](#前端实现) --- ## 系统概述 ### 功能定位 邀请码系统是一个用户增长工具,通过邀请返现机制激励用户推广平台,实现用户裂变增长。 ### 核心特性 - ✅ 用户注册时自动生成唯一邀请码 - ✅ 支持小程序码扫码邀请 - ✅ 邀请关系自动绑定 - ✅ 防重复绑定和自我邀请 - ✅ 邀请统计和返现管理 --- ## 核心功能 ### 1. 邀请码生成规则 **格式规范**: - 长度:6位 - 字符集:数字 0-9 + 大写字母 A-Z(共36个字符) - 示例:`A3X9K2`, `B7M4N1`, `5K8P2Z` **生成时机**: - 用户注册时自动生成 - 微信授权登录(新用户) - 微信手机号登录(新用户) - 开发模式自动注册 **唯一性保证**: - 生成后检查数据库是否存在 - 如果重复则递归重新生成 - 理论上可生成 36^6 = 2,176,782,336 个不同的邀请码 ### 2. 邀请关系绑定 **绑定时机**: - ✅ 用户注册时绑定 - ❌ 用户登录时不绑定(老用户) **绑定规则**: 1. 只有新注册用户才能绑定邀请关系 2. 每个用户只能被邀请一次(防重复绑定) 3. 用户不能使用自己的邀请码(防自我邀请) 4. 邀请码必须存在且有效 **绑定流程**: ``` 扫码进入 → 存储邀请码 → 用户注册 → 验证邀请码 → 创建邀请关系 → 更新统计 ``` ### 3. 邀请统计 **统计维度**: - `totalInvites`:累计邀请人数 - `totalOrders`:被邀请人累计订单数 - `totalCashback`:累计返现金额 - `availableBalance`:可用余额 - `withdrawnAmount`:已提现金额 **更新时机**: - 邀请关系创建时:`totalInvites +1` - 被邀请人下单时:`totalOrders +1`, `totalCashback += 返现金额` - 提现时:`availableBalance -= 提现金额`, `withdrawnAmount += 提现金额` --- ## 技术实现 ### 后端实现(NestJS) #### 核心服务方法 **1. 生成邀请码** ```typescript // apps/server/src/modules/app/auth/auth.service.ts private generateInviteCode(): string { const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; let code = ''; for (let i = 0; i < 6; i++) { code += chars.charAt(Math.floor(Math.random() * chars.length)); } return code; } ``` **2. 创建用户邀请码** ```typescript private async createUserInviteCode(userId: number): Promise { // 1. 查找启用的邀请返现活动 const activity = await this.activityRepo .createQueryBuilder('activity') .where('activity.type = :type', { type: 'invite_cashback' }) .andWhere('activity.enabled = :enabled', { enabled: true }) .orderBy('activity.id', 'DESC') .getOne(); if (!activity) { this.logger.warn('没有启用的邀请返现活动,跳过生成邀请码'); return; } // 2. 生成邀请码并检查唯一性 const inviteCode = this.generateInviteCode(); const existing = await this.inviteStatsRepo.findOne({ where: { inviteCode }, }); if (existing) { // 递归重新生成 return this.createUserInviteCode(userId); } // 3. 创建邀请统计记录 const inviteStats = this.inviteStatsRepo.create({ activityId: activity.id, userId, inviteCode, totalInvites: 0, totalOrders: 0, totalCashback: 0, availableBalance: 0, withdrawnAmount: 0, }); await this.inviteStatsRepo.save(inviteStats); } ``` **3. 处理邀请关系绑定** ```typescript private async handleInviteCode(inviteeId: number, inviteCode: string): Promise { if (!inviteCode) return; // 1. 检查用户是否已被邀请过 const existingInvitation = await this.invitationRepo.findOne({ where: { inviteeId }, }); if (existingInvitation) { this.logger.log(`用户已被邀请过: userId=${inviteeId}`); return; } // 2. 查找邀请人 const inviterStats = await this.inviteStatsRepo.findOne({ where: { inviteCode }, }); if (!inviterStats) { this.logger.warn(`邀请码不存在: ${inviteCode}`); return; } // 3. 防止自我邀请 if (inviterStats.userId === inviteeId) { this.logger.warn(`用户不能使用自己的邀请码: userId=${inviteeId}`); return; } // 4. 创建邀请关系 const invitation = this.invitationRepo.create({ activityId: inviterStats.activityId, inviterId: inviterStats.userId, inviteeId, inviteCode, }); await this.invitationRepo.save(invitation); // 5. 更新邀请人统计 inviterStats.totalInvites += 1; await this.inviteStatsRepo.save(inviterStats); this.logger.log(`邀请关系绑定成功: inviter=${inviterStats.userId}, invitee=${inviteeId}`); } ``` **4. 注册流程集成** ```typescript async register(dto: RegisterDto) { // 1. 创建用户 const user = await this.userRepo.save(newUser); // 2. 创建用户账户 await this.createUserAccount(user.id); // 3. 生成用户邀请码 await this.createUserInviteCode(user.id); // 4. 处理邀请关系绑定 if (dto.inviteCode) { await this.handleInviteCode(user.id, dto.inviteCode); } // 5. 返回token return this.generateToken(user); } ``` #### 依赖注入配置 **AuthModule** ```typescript // apps/server/src/modules/app/auth/auth.module.ts @Module({ imports: [ TypeOrmModule.forFeature([ User, UserAccount, MktInvitation, MktUserInviteStats, MktActivity, ]), JwtModule.registerAsync({...}), ], controllers: [AuthController], providers: [AuthService], exports: [AuthService, JwtModule], }) export class UserAuthModule {} ``` --- ## 数据库设计 ### 1. 邀请关系表(mkt_invitations) ```sql CREATE TABLE `mkt_invitations` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `activity_id` int NOT NULL COMMENT '活动ID', `inviter_id` int NOT NULL COMMENT '邀请人用户ID', `invitee_id` int NOT NULL COMMENT '被邀请人用户ID', `invite_code` varchar(20) NOT NULL COMMENT '邀请码', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_invitee` (`invitee_id`), KEY `idx_inviter` (`inviter_id`), KEY `idx_activity` (`activity_id`), KEY `idx_invite_code` (`invite_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='邀请关系表'; ``` **字段说明**: - `inviter_id`:邀请人ID - `invitee_id`:被邀请人ID(唯一索引,保证一个用户只能被邀请一次) - `invite_code`:使用的邀请码 ### 2. 用户邀请统计表(mkt_user_invite_stats) ```sql CREATE TABLE `mkt_user_invite_stats` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `activity_id` int NOT NULL COMMENT '活动ID', `user_id` int NOT NULL COMMENT '用户ID', `invite_code` varchar(20) NOT NULL COMMENT '邀请码', `total_invites` int NOT NULL DEFAULT '0' COMMENT '累计邀请人数', `total_orders` int NOT NULL DEFAULT '0' COMMENT '被邀请人累计订单数', `total_cashback` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '累计返现金额', `available_balance` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '可用余额', `withdrawn_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '已提现金额', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_user_activity` (`user_id`, `activity_id`), UNIQUE KEY `uk_invite_code` (`invite_code`), KEY `idx_activity` (`activity_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户邀请统计表'; ``` **字段说明**: - `invite_code`:用户的邀请码(唯一索引) - `total_invites`:累计邀请人数 - `total_cashback`:累计返现金额 - `available_balance`:可用余额(可提现) - `withdrawn_amount`:已提现金额 ### 3. 营销活动表(mkt_activities) ```sql CREATE TABLE `mkt_activities` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `type` varchar(50) NOT NULL COMMENT '活动类型:invite_cashback-邀请返现', `name` varchar(100) NOT NULL COMMENT '活动名称', `description` text COMMENT '活动描述', `config` json COMMENT '活动配置(JSON格式)', `enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用', `start_time` timestamp NULL COMMENT '开始时间', `end_time` timestamp NULL COMMENT '结束时间', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_type` (`type`), KEY `idx_enabled` (`enabled`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='营销活动表'; ``` **活动配置示例**: ```json { "firstOrderRate": 0.05, // 首单返现比例 5% "normalOrderRate": 0.03, // 持续订单返现比例 3% "minOrderAmount": 100, // 最低订单金额 "maxCashback": 500 // 单笔最高返现 } ``` --- ## 业务流程 ### 完整邀请流程 ``` ┌─────────────┐ │ 用户A注册 │ └──────┬──────┘ │ ▼ ┌─────────────────────┐ │ 生成邀请码: A3X9K2 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 创建邀请统计记录 │ └─────────────────────┘ ⋮ ┌─────────────────────┐ │ 用户B扫描A的小程序码│ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 小程序首页获取scene │ │ 参数: i=A3X9K2 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 存储到本地Storage │ │ pendingInviteCode │ └─────────────────────┘ ⋮ ┌─────────────────────┐ │ 用户B注册账号 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 生成B的邀请码: │ │ B7M4N1 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 读取本地邀请码 │ │ A3X9K2 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 验证邀请码有效性 │ │ - 邀请码存在 │ │ - 非自我邀请 │ │ - 未被邀请过 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 创建邀请关系记录 │ │ inviter: A │ │ invitee: B │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 更新A的邀请统计 │ │ totalInvites +1 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 清除本地邀请码 │ └─────────────────────┘ ⋮ ┌─────────────────────┐ │ 用户B下单支付 │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 计算返现金额 │ │ - 首单: 5% │ │ - 持续: 3% │ └──────┬──────────────┘ │ ▼ ┌─────────────────────┐ │ 更新A的邀请统计 │ │ - totalOrders +1 │ │ - totalCashback += │ │ - availableBalance +=│ └─────────────────────┘ ``` --- ## API接口 ### 1. 用户注册(带邀请码) **接口**:`POST /api/app/auth/register` **请求参数**: ```typescript { phone: string; // 手机号 code: string; // 验证码 password?: string; // 密码(可选) nickname?: string; // 昵称(可选) inviteCode?: string; // 邀请码(可选) } ``` **响应**: ```typescript { token: string; // JWT token user: { id: number; phone: string; nickname: string; // ... } } ``` ### 2. 微信授权登录(带邀请码) **接口**:`POST /api/app/auth/login/wechat` **请求参数**: ```typescript { code: string; // 微信授权code nickname?: string; // 昵称 avatar?: string; // 头像 inviteCode?: string; // 邀请码(新用户) } ``` ### 3. 获取用户邀请信息 **接口**:`GET /api/app/invite/stats` **响应**: ```typescript { inviteCode: string; // 我的邀请码 totalInvites: number; // 累计邀请人数 totalOrders: number; // 被邀请人订单数 totalCashback: number; // 累计返现 availableBalance: number; // 可用余额 withdrawnAmount: number; // 已提现金额 } ``` ### 4. 获取邀请列表 **接口**:`GET /api/app/invite/list` **响应**: ```typescript { list: [ { inviteeId: number; // 被邀请人ID inviteeName: string; // 被邀请人昵称 inviteeAvatar: string; // 被邀请人头像 orderCount: number; // 订单数 cashbackAmount: number; // 返现金额 createdAt: string; // 邀请时间 } ], total: number; } ``` --- ## 前端实现 ### 小程序端(uni-app) #### 1. 首页获取邀请码 **文件**:`apps/miniapp/src/pages/index/index.vue` ```typescript import { onLoad } from '@dcloudio/uni-app'; onLoad((options) => { // 处理小程序码 scene 参数 if (options.scene) { try { const decodedScene = decodeURIComponent(options.scene); const params = new URLSearchParams(decodedScene); const inviteCode = params.get('i'); if (inviteCode) { // 存储邀请码到本地 uni.setStorageSync('pendingInviteCode', inviteCode); console.log('保存邀请码:', inviteCode); } } catch (error) { console.error('解析scene参数失败:', error); } } }); ``` #### 2. 注册时传递邀请码 **文件**:`apps/miniapp/src/pages/register/index.vue` ```typescript const handleRegister = async () => { // 读取本地存储的邀请码 const inviteCode = uni.getStorageSync('pendingInviteCode'); // 调用注册接口 const res = await register({ phone: form.phone, code: form.code, password: form.password, inviteCode, // 传递邀请码 }); // 注册成功后清除邀请码 if (inviteCode) { uni.removeStorageSync('pendingInviteCode'); } // 保存token并跳转 uni.setStorageSync('token', res.token); uni.switchTab({ url: '/pages/index/index' }); }; ``` #### 3. 邀请海报页面 **文件**:`apps/miniapp/src/pages/invite/poster.vue` **功能**: - 显示用户邀请码 - 生成小程序码(二维码) - 展示返现规则 - 保存海报图片 - 复制邀请码 **关键代码**: ```vue ``` #### 4. API封装 **文件**:`apps/miniapp/src/api/user/auth.ts` ```typescript // 注册 export function register(data: { phone: string; code: string; password?: string; nickname?: string; inviteCode?: string; }) { return post('/api/app/auth/register', data); } // 微信授权登录 export function loginByWechat( code: string, nickname?: string, avatar?: string, inviteCode?: string ) { return post('/api/app/auth/login/wechat', { code, nickname, avatar, inviteCode, }); } ``` **文件**:`apps/miniapp/src/api/user/invite.ts` ```typescript // 获取邀请统计 export function getInviteStats() { return get('/api/app/invite/stats'); } // 获取邀请列表 export function getInviteList(params: { page: number; pageSize: number; }) { return get('/api/app/invite/list', params); } ``` --- ## 测试场景 ### 功能测试 #### 1. 邀请码生成测试 - [ ] 用户注册后自动生成邀请码 - [ ] 邀请码格式正确(6位,0-9A-Z) - [ ] 邀请码唯一性(不重复) - [ ] 微信授权登录新用户生成邀请码 #### 2. 邀请关系绑定测试 - [ ] 扫码进入小程序,邀请码正确存储 - [ ] 注册时邀请码正确传递 - [ ] 邀请关系正确创建 - [ ] 邀请人统计正确更新(totalInvites +1) - [ ] 防重复绑定(已被邀请的用户不能再次绑定) - [ ] 防自我邀请(不能使用自己的邀请码) - [ ] 无效邀请码处理(邀请码不存在) #### 3. 邀请统计测试 - [ ] 邀请人数统计正确 - [ ] 订单数统计正确 - [ ] 返现金额计算正确 - [ ] 可用余额更新正确 #### 4. 边界情况测试 - [ ] 没有启用的邀请活动时的处理 - [ ] 邀请码生成冲突时的重试机制 - [ ] 并发注册时的邀请码唯一性 - [ ] 老用户登录时不处理邀请码 --- ## 注意事项 ### 安全性 1. **邀请码唯一性**:必须确保邀请码在数据库中唯一 2. **防刷机制**:需要防止恶意刷邀请关系 3. **数据一致性**:邀请统计数据需要与实际订单数据保持一致 ### 性能优化 1. **邀请码索引**:在 `invite_code` 字段上建立唯一索引 2. **查询优化**:邀请列表查询需要分页和索引优化 3. **缓存策略**:用户邀请统计可以使用 Redis 缓存 ### 扩展性 1. **多活动支持**:系统支持多个邀请活动同时进行 2. **返现规则配置**:返现比例可在活动配置中灵活调整 3. **邀请码格式**:可以根据需要调整邀请码长度和字符集 --- ## 更新日志 | 日期 | 版本 | 说明 | |------|------|------| | 2026-05-13 | v1.0 | 初始版本,完成邀请码系统设计和实现 | --- **维护团队**:开发团队 **最后更新**:2026-05-13