You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于经纬度生成4位验证码?满足同城ADD CODE短时无冲突需求

嘿,这个需求挺贴合线下场景的,咱们来聊聊怎么用4位验证码搞定这个问题,核心思路是把4位码的唯一性约束缩小到「城市+短时间窗口」这个局部范围内,毕竟你不需要全局唯一,只要同一城市同一时段不重复就行。下面是几个可落地的方案:

核心思路铺垫

4位码总共10000种组合,只要每个城市在设定的时段(比如5分钟)内的并发生成请求数不超过10000,就完全可以做到无冲突。先明确几个前置规则:

  • 每个ADD CODE绑定城市ID+时间窗口,过期自动失效(比如5分钟后就不能用了)
  • 校验好友时,除了比对ADD CODE和距离,还要确认双方的时间窗口是否重叠(或完全匹配)
具体实现方案

1. 基于城市+时间窗口的随机池方案

这是最容易实现的方案:

  • 把时间切成固定窗口(比如5分钟),用城市ID + 时间窗口标识作为唯一键(比如city_123_window_1699992000,其中时间窗口标识是当前时间戳除以300取整)
  • 用Redis的Set结构存储该键下已使用的4位码,生成时随机生成一个4位字符串,检查是否在Set里:
    • 如果不在,就把它加入Set,并给这个键设置过期时间(等于窗口时长)
    • 如果存在,就重新生成,直到找到未使用的码
  • 给重试加个上限(比如5次),如果还是冲突,直接返回"当前请求过多,请稍后再试"(这种情况在并发远低于10000时几乎不会发生)

示例伪代码:

function generateAddCode(cityId) {
  const window = Math.floor(Date.now() / 300000); // 5分钟窗口,单位毫秒
  const redisKey = `add_code:${cityId}:${window}`;
  let retry = 5;
  while (retry > 0) {
    const code = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
    const isNew = await redis.sadd(redisKey, code);
    if (isNew) {
      await redis.expire(redisKey, 300); // 5分钟后自动清理
      return code;
    }
    retry--;
  }
  throw new Error('无法生成可用验证码,请稍后重试');
}

2. 城市内自增循环码方案

如果担心随机生成的重试问题,可以用自增逻辑:

  • 每个城市维护一个自增计数器,从0000开始到9999循环,每次生成码时取当前计数器值,然后原子性+1
  • 同样绑定时间窗口,每个窗口开始时计数器重置为0000
  • 集群环境下要用Redis的INCR原子操作来保证计数器唯一,避免多实例冲突

这个方案的好处是完全不会有冲突,只要同一窗口内的请求数不超过10000,就稳定可用。

3. 优化体验的小技巧

如果想进一步降低冲突概率或提升用户输入体验:

  • 排除易混淆数字:比如去掉0、1、6、9,只用2-5、7-8,这样虽然可用码降到6^4=1296种,但用户输入错误率大幅降低,适合并发不高的场景
  • 动态调整窗口时长:比如根据城市的实时请求量,自动缩短或延长窗口(比如热门城市用3分钟窗口,冷门城市用10分钟),平衡冲突概率和码的有效期
校验环节的补充逻辑

好友匹配时,除了比对ADD CODE和城市ID,还要:

  1. 检查B输入的ADD CODE是否还在有效期内(即对应的时间窗口是否未过期)
  2. 确认A和B的位置距离符合要求
  3. 匹配成功后,记得把这个ADD CODE标记为已使用,避免被其他用户重复利用

内容的提问来源于stack exchange,提问作者Elm Liu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:27:54