招商资讯 > 资讯详情

带邀请功能的网站,通过落地页进行邀请,这个如何实现

2026-09-09 06:29:50

带邀请功能的网站,通过落地页进行邀请,这个如何实现

实现一个带邀请功能的网站,核心思路是通过一个可传播的链接,将“邀请者”和“被邀请者”的身份关联起来。整个过程可以拆解为几个关键环节,下面我会为你详细说明。

💡 核心工作流程

一个标准的邀请流程大致如下:

  1. 生成邀请:系统为邀请者(老用户)生成一个独一无二的邀请链接邀请码
  2. 分享邀请:邀请者通过微信、邮件等方式,将这个链接或码分享给朋友(潜在新用户)。
  3. 访问落地页:被邀请者点击链接,进入一个专门的邀请落地页。这个页面通常会展示邀请内容、相关活动信息以及一个注册/行动按钮。
  4. 注册与绑定:被邀请者在落地页完成注册或指定操作。系统会自动识别邀请链接中的参数,将新用户与邀请者的关系绑定起来。

⚙️ 后端实现:

后端是邀请功能的核心,负责处理数据逻辑。

  • 数据库设计:至少需要两张核心表。
  • 用户表 (users):存储所有用户信息,每个用户应有唯一的invite_code字段。
  • 邀请记录表 (invitations):记录每一次邀请行为。关键字段包括inviterid(邀请人)、inviteeid(被邀请人,可为空)、invitecode(邀请码)、status(状态:待使用/已使用/已过期)和expiresat(过期时间)。
    • 核心API接口:后端需要提供以下接口。
    • 生成邀请链接/码:为当前用户生成新的邀请码。
    • 验证邀请码:验证用户提交的邀请码是否有效(存在、未使用、未过期)。
    • 接受邀请:新用户注册时,调用此接口完成关系绑定。
    • 获取邀请信息:落地页加载时,通过邀请码获取邀请者信息用于页面展示。

    🎨 前端落地页实现:

    前端主要负责展示和交互。你可以使用React、Vue等任何前端框架。

    • 路由设计:为邀请落地页设计一个专门的路由,例如 /invite/:inviteCode。页面组件需要能从URL中提取出inviteCode参数。
    • 页面内容:落地页应包含:
    • 个性化问候:显示邀请者的信息,如“你的朋友[用户名]邀请你加入...”。
    • 行动召唤(CTA)按钮:一个醒目的“立即加入”、“接受邀请”或“注册”按钮。
    • 附加信息:根据需要展示活动详情、功能亮点等。
    • 核心交互逻辑
    1. 获取邀请信息:页面加载后,用inviteCode调用后端的“获取邀请信息”接口,动态渲染页面内容。
    2. 处理邀请接受:用户点击CTA按钮后,前端调用后端的“接受邀请”API。
    3. 处理结果:根据API返回结果,跳转到注册页面、显示成功/错误信息等。

    🔗 邀请链接的生成与分享

    • 链接格式https://你的域名/invite/唯一邀请码。这个“唯一邀请码”可以是随机生成的字符串(如UUID),也可以是对用户ID进行编码(如Base64)。
    • 分享方式:生成链接后,可以通过网页上的“复制链接”、“分享到社交平台”按钮,或通过后端集成的邮件服务(如Nodemailer)发送给朋友。

    🛡️ 安全与防刷机制

    • 邀请码有效期:为邀请码设置有效期(如7天),防止长期占用。
    • 使用次数限制:限制一个邀请码只能使用一次。
    • 频率限制:限制同一IP或邮箱在短时间内(如1天)只能领取一次邀请码。
    • 验证码:在关键操作(如领取邀请码)时,加入图形验证码或reCAPTCHA。
    • API鉴权:确保管理端接口和重要操作接口都有身份验证(如JWT)。

    好的,我将为你提供一个完整的、可运行的邀请功能实现示例,包含后端 API 和前端落地页。技术栈选用 Node.js + Express + MongoDB(后端)和 原生 HTML + JavaScript(前端),保证逻辑清晰,可直接用于开发参考。


    📁 项目结构

    纯文本invite-demo/
    ├── server.js                # 入口文件
    ├── package.json             # 依赖配置
    ├── models/
    │   ├── User.js              # 用户模型
    │   └── Invitation.js        # 邀请记录模型
    ├── routes/
    │   └── invite.js            # 邀请相关 API 路由
    └── public/
        └── invite.html          # 邀请落地页(前端)

    🔧 后端实现

    1. 安装依赖(package.json

    json{
      "name": "invite-demo",
      "version": "1.0.0",
      "description": "",
      "main": "server.js",
      "scripts": {
        "start": "node server.js"
      },
      "dependencies": {
        "express": "^4.18.2",
        "mongoose": "^7.0.3",
        "cors": "^2.8.5",
        "dotenv": "^16.0.3",
        "nanoid": "^3.3.6"   // 用于生成短邀请码
      }
    }

    2. 入口文件(server.js

    JavaScriptrequire('dotenv').config();
    const express = require('express');
    const cors = require('cors');
    const mongoose = require('mongoose');
    
    const app = express();
    app.use(cors());
    app.use(express.json());
    app.use(express.static('public')); // 托管前端静态文件
    
    // 连接 MongoDB(请替换为你的连接字符串)
    mongoose.connect('mongodb://localhost:27017/invite_demo')
      .then(() => console.log('MongoDB 连接成功'))
      .catch(err => console.error(err));
    
    // 加载邀请路由
    app.use('/api/invite', require('./routes/invite'));
    
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => console.log(`服务运行在 http://localhost:${PORT}`));

    3. 数据库模型

    用户模型(models/User.js

    JavaScriptconst mongoose = require('mongoose');
    
    const UserSchema = new mongoose.Schema({
      username: { type: String, required: true, unique: true },
      email: { type: String, required: true, unique: true },
      password: { type: String, required: true }, // 实际应加密
      inviteCode: { type: String, unique: true }   // 每个用户唯一的邀请码
    });
    
    module.exports = mongoose.model('User', UserSchema);

    邀请记录模型(models/Invitation.js

    JavaScriptconst mongoose = require('mongoose');
    
    const InvitationSchema = new mongoose.Schema({
      inviterId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
      inviteeId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, // 被邀请人,未注册则为空
      code: { type: String, unique: true, required: true },
      status: { 
        type: String, 
        enum: ['pending', 'accepted', 'expired'], 
        default: 'pending' 
      },
      expiresAt: { type: Date, required: true } // 过期时间
    }, { timestamps: true });
    
    module.exports = mongoose.model('Invitation', InvitationSchema);

    4. 邀请 API 路由(routes/invite.js

    JavaScriptconst express = require('express');
    const router = express.Router();
    const { nanoid } = require('nanoid');
    const User = require('../models/User');
    const Invitation = require('../models/Invitation');
    
    // ---------- 辅助函数 ----------
    // 验证邀请码是否有效(存在、未使用、未过期)
    async function validateInviteCode(code) {
      const invitation = await Invitation.findOne({ code }).populate('inviterId');
      if (!invitation) return { valid: false, message: '邀请码不存在' };
      if (invitation.status !== 'pending') return { valid: false, message: '邀请码已被使用' };
      if (new Date() > invitation.expiresAt) return { valid: false, message: '邀请码已过期' };
      return { valid: true, invitation };
    }
    
    // ---------- 1. 生成邀请码(需登录,这里用固定用户演示)----------
    // 实际使用时,请从 req.user 获取当前登录用户
    router.post('/generate', async (req, res) => {
      try {
        // 演示:假设当前用户 ID 为 "60f7c5d5a5a5a5a5a5a5a5a5"(请替换为真实ID)
        const inviterId = req.body.inviterId || '60f7c5d5a5a5a5a5a5a5a5a5';
        
        const inviter = await User.findById(inviterId);
        if (!inviter) {
          return res.status(404).json({ error: '邀请者不存在' });
        }
    
        // 生成唯一邀请码(8位字符)
        const code = nanoid(8);
        // 有效期7天
        const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
    
        const invitation = new Invitation({
          inviterId: inviter._id,
          code,
          expiresAt
        });
        await invitation.save();
    
        // 同时将邀请码存入用户文档(方便查询)
        inviter.inviteCode = code;
        await inviter.save();
    
        res.status(201).json({
          code,
          inviteLink: `http://localhost:3000/invite.html?code=${code}`
        });
      } catch (error) {
        console.error(error);
        res.status(500).json({ error: '生成邀请码失败' });
      }
    });
    
    // ---------- 2. 获取邀请信息(供落地页展示)----------
    router.get('/:code', async (req, res) => {
      try {
        const { code } = req.params;
        const result = await validateInviteCode(code);
        if (!result.valid) {
          return res.status(400).json({ error: result.message });
        }
        const { inviterId } = result.invitation;
        res.json({
          inviterName: inviterId.username,
          inviterAvatar: '', // 可扩展
          inviteMessage: `你的朋友 ${inviterId.username} 邀请你加入我们!`
        });
      } catch (error) {
        console.error(error);
        res.status(500).json({ error: '获取邀请信息失败' });
      }
    });
    
    // ---------- 3. 接受邀请(被邀请人注册)----------
    router.post('/accept', async (req, res) => {
      try {
        const { code, username, email, password } = req.body;
        // 1. 验证邀请码
        const result = await validateInviteCode(code);
        if (!result.valid) {
          return res.status(400).json({ error: result.message });
        }
        const { invitation } = result;
    
        // 2. 检查用户名/邮箱是否已被占用
        const existingUser = await User.findOne({ $or: [{ username }, { email }] });
        if (existingUser) {
          return res.status(400).json({ error: '用户名或邮箱已被注册' });
        }
    
        // 3. 创建新用户(密码未加密,生产环境需使用 bcrypt)
        const newUser = new User({
          username,
          email,
          password, // 实际应加密
          inviteCode: null // 新用户暂时没有自己的邀请码,可后续生成
        });
        await newUser.save();
    
        // 4. 更新邀请记录
        invitation.inviteeId = newUser._id;
        invitation.status = 'accepted';
        await invitation.save();
    
        // 5. 可选:给邀请者增加积分、奖励等逻辑
    
        res.status(201).json({ 
          message: '注册成功,已接受邀请',
          userId: newUser._id
        });
      } catch (error) {
        console.error(error);
        res.status(500).json({ error: '接受邀请失败' });
      }
    });
    
    module.exports = router;

    🎨 前端落地页(public/invite.html

    html<!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>好友邀请</title>
      <style>
        body { font-family: Arial, sans-serif; background: #f7f9fc; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
        .invite-card {
          background: white;
          padding: 2rem 3rem;
          border-radius: 16px;
          box-shadow: 0 10px 30px rgba(0,0,0,0.1);
          text-align: center;
          max-width: 400px;
          width: 100%;
        }
        .avatar { width: 80px; height: 80px; border-radius: 50%; background: #ccc; margin: 0 auto 1rem; display: flex; align-items: center; justify-content: center; font-size: 2rem; color: white; background: #4a90d9; }
        .invite-message { font-size: 1.2rem; color: #333; margin: 1rem 0; }
        .btn {
          background: #4a90d9;
          color: white;
          border: none;
          padding: 0.8rem 2rem;
          border-radius: 30px;
          font-size: 1rem;
          cursor: pointer;
          transition: 0.2s;
          margin-top: 1rem;
        }
        .btn:hover { background: #357abd; }
        .error { color: #d9534f; margin-top: 1rem; }
        .loading { color: #999; }
      </style>
    </head>
    <body>
      <div class="invite-card" id="inviteCard">
        <div class="avatar" id="avatar">👤</div>
        <h2 id="inviterName">加载中...</h2>
        <p class="invite-message" id="inviteMessage">正在获取邀请信息...</p>
        <button class="btn" id="acceptBtn">立即加入</button>
        <p id="message" class="error"></p>
      </div>
    
      <script>
        // 从 URL 获取邀请码
        const urlParams = new URLSearchParams(window.location.search);
        const code = urlParams.get('code');
    
        if (!code) {
          document.getElementById('inviteCard').innerHTML = '<p class="error">无效的邀请链接,缺少邀请码</p>';
        } else {
          // 获取邀请信息
          fetch(`http://localhost:3000/api/invite/${code}`)
            .then(res => res.json())
            .then(data => {
              if (data.error) {
                document.getElementById('inviteCard').innerHTML = `<p class="error">${data.error}</p>`;
                return;
              }
              document.getElementById('inviterName').textContent = data.inviterName || '好友';
              document.getElementById('inviteMessage').textContent = data.inviteMessage || '邀请你加入我们!';
            })
            .catch(err => {
              document.getElementById('inviteCard').innerHTML = `<p class="error">加载失败,请稍后重试</p>`;
              console.error(err);
            });
    
          // 点击“立即加入” -> 弹出注册表单(模拟)
          document.getElementById('acceptBtn').addEventListener('click', () => {
            // 为简化,我们使用 prompt 模拟填写注册信息,实际应跳转至注册页面
            const username = prompt('请输入用户名');
            if (!username) return;
            const email = prompt('请输入邮箱');
            if (!email) return;
            const password = prompt('请输入密码');
            if (!password) return;
    
            // 调用接受邀请接口
            fetch('http://localhost:3000/api/invite/accept', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ code, username, email, password })
            })
            .then(res => res.json())
            .then(data => {
              const msgEl = document.getElementById('message');
              if (data.error) {
                msgEl.textContent = '❌ ' + data.error;
              } else {
                msgEl.style.color = 'green';
                msgEl.textContent = '✅ ' + data.message + ',欢迎加入!';
                document.getElementById('acceptBtn').disabled = true;
              }
            })
            .catch(err => {
              document.getElementById('message').textContent = '网络错误,请重试';
              console.error(err);
            });
          });
        }
      </script>
    </body>
    </html>

    🚀 运行与测试步骤

    1. 安装依赖
    Shell   npm install
    1. 启动 MongoDB(本地需已安装,或使用云服务如 MongoDB Atlas)
    1. 初始化数据(手动插入一个测试用户,用于生成邀请码)
    • 在 MongoDB 中执行:
    JavaScript   use invite_demo;
       db.users.insertOne({
         _id: ObjectId("60f7c5d5a5a5a5a5a5a5a5a5"), // 固定ID,与代码中一致
         username: "张三",
         email: "zhangsan@example.com",
         password: "123456"
       });
    1. 启动服务
    Shell   npm start
    1. 生成邀请链接(用 Postman 或 curl 请求)
    Shell   POST http://localhost:3000/api/invite/generate
       Body: { "inviterId": "60f7c5d5a5a5a5a5a5a5a5a5" }

    返回示例:

    json   {
         "code": "XyZ7kLmP",
         "inviteLink": "http://localhost:3000/invite.html?code=XyZ7kLmP"
       }
    1. 访问落地页:将 inviteLink 复制到浏览器,即可看到邀请信息,点击按钮模拟注册。

    🔐 进一步优化建议

    • 用户认证:生成邀请码时应从 req.user 获取当前登录用户,而非前端传入 inviterId
    • 密码加密:使用 bcrypt 对密码哈希存储。
    • 参数校验:使用 Joiexpress-validator 校验输入。
    • 注册页面:将 prompt 替换为完整的注册表单(含表单校验、验证码等)。
    • 防刷机制:限制同一 IP 或邮箱每日领取次数。
    • 缓存:邀请信息可缓存在 Redis 中,减轻数据库压力。

DISCUSSION

评论

登录后参与
请使用网站前台用户账号登录后发表评论。

正在加载评论...


合作热线
18865460927
公司地址
山东省东营市垦利区兴隆路8-5号
产品咨询
产品咨询
Copyright © 2014-2026 东营码良软件开发 Inc. 版权所有鲁ICP备2025202136号-2 | 经营许可证编号:豫B2-20190103豫公网安备41019602002340