转盘组件设计与防刷方案

转盘抽奖组件的设计要点和前端防刷策略

问题

如何设计一个转盘抽奖组件?需要和业务方确认哪些技术细节?如何从前端角度实现防刷?

解答

组件设计考虑

功能需求

明确抽奖逻辑、转盘样式和动画效果。确定是否需要音效、中奖弹窗、历史记录等功能。

技术选型

根据需求选择实现方式:

  • CSS3 动画:适合简单的旋转效果,性能好
  • Canvas:适合复杂的绘图需求,灵活度高
  • SVG:适合需要缩放和交互的场景

数据处理

设计与后端的交互流程:

// 抽奖请求
async function drawLottery() {
  const response = await fetch('/api/lottery/draw', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Request-ID': generateRequestId() // 请求唯一标识
    },
    body: JSON.stringify({
      activityId: 'xxx',
      userId: 'xxx',
      timestamp: Date.now()
    })
  });
  
  const result = await response.json();
  // result: { prizeId, prizeName, angle, ... }
  return result;
}

用户体验

  • 加载状态:显示 loading 动画
  • 错误处理:网络异常、次数不足等提示
  • 动画流畅:使用 requestAnimationFrame 优化
  • 防止重复点击:抽奖过程中禁用按钮

业务方确认的技术细节

抽奖规则

  • 奖品种类、库存数量
  • 中奖概率配置
  • 每日/每人抽奖次数限制
  • 奖品兑换方式和有效期

后端接口规范

// 接口示例
POST /api/lottery/draw
Request: {
  activityId: string,
  userId: string,
  timestamp: number,
  sign: string // 签名
}

Response: {
  code: number,
  data: {
    prizeId: string,
    prizeName: string,
    angle: number, // 转盘停止角度
    remainCount: number // 剩余次数
  }
}

防刷策略

  • 前端限制:按钮防抖、次数校验
  • 后端限制:IP 频率限制、用户维度限制
  • 验证机制:滑块验证、短信验证码

奖品发放

  • 实物奖品:收集收货地址、身份验证
  • 虚拟奖品:优惠券、积分等自动发放
  • 发放时机:即时发放或审核后发放

前端防刷方案

限制抽奖次数

class LotteryController {
  constructor() {
    this.isDrawing = false;
    this.remainCount = 0;
  }
  
  async draw() {
    // 防止重复点击
    if (this.isDrawing) {
      return;
    }
    
    // 检查次数
    if (this.remainCount <= 0) {
      showToast('抽奖次数不足');
      return;
    }
    
    this.isDrawing = true;
    
    try {
      const result = await drawLottery();
      this.remainCount = result.remainCount;
      await this.startAnimation(result.angle);
    } finally {
      this.isDrawing = false;
    }
  }
}

请求签名

function generateSign(params, secret) {
  // 参数排序
  const sortedKeys = Object.keys(params).sort();
  const str = sortedKeys.map(key => `${key}=${params[key]}`).join('&');
  
  // 生成签名(实际应使用更安全的算法)
  return md5(str + secret);
}

// 使用
const params = {
  activityId: 'xxx',
  userId: 'xxx',
  timestamp: Date.now()
};
params.sign = generateSign(params, SECRET_KEY);

设备指纹

// 收集设备信息生成唯一标识
function getDeviceFingerprint() {
  const info = {
    userAgent: navigator.userAgent,
    language: navigator.language,
    platform: navigator.platform,
    screenResolution: `${screen.width}x${screen.height}`,
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
  };
  
  return hashCode(JSON.stringify(info));
}

验证码集成

async function drawWithCaptcha() {
  // 触发验证码
  const captchaToken = await showCaptcha();
  
  // 带上验证码 token 请求
  const response = await fetch('/api/lottery/draw', {
    method: 'POST',
    body: JSON.stringify({
      // ... 其他参数
      captchaToken
    })
  });
}

关键点

  • 前端防刷只是辅助手段,真正的防刷逻辑必须在后端实现
  • 使用请求签名、时间戳、设备指纹等多重验证提高安全性
  • 关键接口需要限流、防重放攻击,配合验证码机制
  • 与业务方明确抽奖规则、接口规范、奖品发放流程
  • 优化用户体验:loading 状态、错误提示、防止重复点击