实战-全局唯一邀请码功能实现

简介: 实战-全局唯一邀请码功能实现

无论什么APP需要做推广功能,而推广功能多多少少都离不开邀请码。被邀请用户下载APP登录时输入邀请码,邀请码所有者将获得一定的好处,比如积分奖励、现金奖励或者免费试用(VIP)等特权。一套优秀的邀请码生成机制不仅确保全局唯一性,还需要考虑到性能等问题。本篇文章带大家探究一下基于Java的邀请码生成。

邀请码实现

邀请码的特性

  • 唯一性:确保每个用户的邀请码都是独一无二的,这样系统才能判定谁为邀请者,甚至可以根据邀请码进行反向推导。
  • 随机性:不能让用户从邀请码上轻易的看出生成的规则。
  • 高效性:生成邀请码的算法不能过于复杂,或耗费过度系统资源。
  • 简洁性:用户可以方便的输入,记录,辨别是否输入错误等。

平时我们看到的邀请码一般有两种类型:纯数字、数字+字母(通常大写),而邀请码的长度通常在6位左右就是为了满足简洁性。

随机生成邀请码

无论是纯数字还是数字加大写字母形式,使用随机算法生成一个邀请码然后判断此随机码是否已经被使用,如果被使用则重新生成。这可能是最初步的思路,但此种方法弊端甚多。

以6为随机数为例说明。6位随机数取0-9共10个数字,生成邀请码的范围为000000-999999,总数为10的6次方,也就是100万。试想一下,如果有50万的用户,那么采用随机数的生成,每次生成的重复概率将在50%以上,而且会越来越重复率越高,多么可怕的性能损耗。

当然,在用户量比较少的情况下此种方法不是完全不可行。可以通过数据库或redis预先生成一批邀请码,当注册新用户或用户使用邀请码的时候将邀请码分配给对应的用户。此种补漏的方法虽然解决了一部分性能的问题,但从根本上还是需要消耗数据库或redis资源,时间维度和空间维度都有一定的损耗。

base编码方式实现

在网络传输中,最常用的base编码是base64编码,那么我们就借鉴一下base64的编码思路来生成邀请码。

一般来说生成一个用户的邀请码需要一个唯一的输入参数,这里就用用户的ID(长整型数)来作为输入参数,输出结果为6为数字+大写字母。同时,通过邀请码可以反推出用户的ID。

首先,指定6位邀请码的数据格式:

6位邀请码:0-9十个数字,26个大写字母,在这其中再去除掉0和1,O和I防止它们两两混淆。

这样,总共获得了32个可用字符。那么能生成的邀请码总数为32的6次方,也就是1073741824个。10亿+个邀请码,在业务初期足够用户使用,如果随着业务的发展可对位数进行扩充。

一般情况用户ID或用户编号都为长整型数且递增,那么现在我们将用户ID映射成一个6位的base32编码。

/**
 * 邀请码生成器,基本原理:<br/>
 * 1)入参用户ID:1 <br/>
 * 2)使用自定义进制转换之后为:V <br/>
 * 3)转换未字符串,并在后面添加'A':VA <br/>
 * 4)在VA后面再随机补足4位,得到:VAHKHE <br/>
 * 5)反向转换时以'A'为分界线,'A'后面的不再解析 <br/>
 *
 * @author zzs
 */
public class ShareCodeUtils {
    /**
     * 自定义进制(0,1没有加入,容易与o,l混淆),数组顺序可进行调整增加反推难度,A用来补位因此此数组不包含A,共31个字符。
     */
    private static final char[] BASE = new char[]{'H', 'V', 'E', '8', 'S', '2', 'D', 'Z', 'X', '9', 'C', '7', 'P',
            '5', 'I', 'K', '3', 'M', 'J', 'U', 'F', 'R', '4', 'W', 'Y', 'L', 'T', 'N', '6', 'B', 'G', 'Q'};
    /**
     * A补位字符,不能与自定义重复
     */
    private static final char SUFFIX_CHAR = 'A';
    /**
     * 进制长度
     */
    private static final int BIN_LEN = BASE.length;
    /**
     * 生成邀请码最小长度
     */
    private static final int CODE_LEN = 6;
    /**
     * ID转换为邀请码
     *
     * @param id
     * @return
     */
    public static String idToCode(Long id) {
        char[] buf = new char[BIN_LEN];
        int charPos = BIN_LEN;
        // 当id除以数组长度结果大于0,则进行取模操作,并以取模的值作为数组的坐标获得对应的字符
        while (id / BIN_LEN > 0) {
            int index = (int) (id % BIN_LEN);
            buf[--charPos] = BASE[index];
            id /= BIN_LEN;
        }
        buf[--charPos] = BASE[(int) (id % BIN_LEN)];
        // 将字符数组转化为字符串
        String result = new String(buf, charPos, BIN_LEN - charPos);
        // 长度不足指定长度则随机补全
        int len = result.length();
        if (len < CODE_LEN) {
            StringBuilder sb = new StringBuilder();
            sb.append(SUFFIX_CHAR);
            Random random = new Random();
            // 去除SUFFIX_CHAR本身占位之后需要补齐的位数
            for (int i = 0; i < CODE_LEN - len - 1; i++) {
                sb.append(BASE[random.nextInt(BIN_LEN)]);
            }
            result += sb.toString();
        }
        return result;
    }
    /**
     * 邀请码解析出ID<br/>
     * 基本操作思路恰好与idToCode反向操作。
     *
     * @param code
     * @return
     */
    public static Long codeToId(String code) {
        char[] charArray = code.toCharArray();
        long result = 0L;
        for (int i = 0; i < charArray.length; i++) {
            int index = 0;
            for (int j = 0; j < BIN_LEN; j++) {
                if (charArray[i] == BASE[j]) {
                    index = j;
                    break;
                }
            }
            if (charArray[i] == SUFFIX_CHAR) {
                break;
            }
            if (i > 0) {
                result = result * BIN_LEN + index;
            } else {
                result = index;
            }
        }
        return result;
    }
    public static void main(String[] args) {
        String code = idToCode(1L);
        System.out.println(code);
        System.out.println(codeToId(code));
    }
}

以上方法通过31个字符长度的数组,外加一个分割字符“A”,完成了6位邀请码的生成过程。同时,根据生成的邀请码又可以反推出用户ID(或用户编号)。此种方法简单高效,又确保了根据每个用户ID生成的邀请码的唯一性。

当然,可以通过打乱BASE数组中字符的顺序让产生的邀请码更加随机一些。如果想让ID的复杂度更高,可以先将ID补全为指定位数,然后给它在指定位置加“盐”或者调换位置等方式进行处理。但需要保证加盐或调换之后ID的唯一性。

业务扩充

当业务不断发展,如果10亿的邀请码依旧无法满足业务需求,则可进行以下方式进行扩充:

  • 将邀请码位数进行扩充,比如变为7位、8位或更多位。
  • 将BASE数组里面的数据进行扩充,比如讲小写字母也添加进来。

Github源代码下载:源码下载。



目录
相关文章
|
算法 NoSQL Java
实战-全局唯一邀请码功能实现
实战-全局唯一邀请码功能实现
1546 0
|
JavaScript 小程序
6.3【微信小程序全栈开发课程】记录页面(三)--记录数据组件初始化
1、功能介绍 在记录页面对记录数据进行展示,每条记录数据后面都有编辑按钮,点击编辑按钮,出现修改文本框,点击修改按钮后,文本框会自动收起
205 0
6.3【微信小程序全栈开发课程】记录页面(三)--记录数据组件初始化
|
SQL 移动开发 JavaScript
借用 疯狂秀才 的页面,修改了一下自然框架后台管理的页面。
  不知道为什么,就是不喜欢extJS,昨天看到了疯狂秀才的页面,大家都说好,那我就借鉴一下吧。下载源码,加到aspx里面。运行,居然有js错误。一模一样的呀,怎么出错了?仔仔细细看了n遍,终于发现了不同的地方——多了一个表单 。
645 0
|
9月前
|
Go
区域代理分红商城系统开发指南教程/步骤功能/方案逻辑/源码项目
The development of regional proxy dividend distribution mall system involves multiple aspects such as proxy dividend function and electronic mall system development. The following is an overview of the steps for developing a regional agent dividend distribution mall system
|
8月前
|
JavaScript
大世界项目21——如何实现更换头像的功能
大世界项目21——如何实现更换头像的功能
|
Java 数据库 开发者
用户模块之注册功能分析|学习笔记
快速学习用户模块之注册功能分析
|
小程序 JavaScript
4.2【微信小程序全栈开发课程】个人中心(二)--显示随机语句
上节课已经将用户的微信头像、微信昵称显示出来了,这节课,我们在微信昵称的下面实现随机语句的显示 也就是挑选10条励志的句子,用户每次切换到个人中心页面,都会随机显示这10条句子中的一条
233 0
4.2【微信小程序全栈开发课程】个人中心(二)--显示随机语句
|
小程序 前端开发 开发者
6.1【微信小程序全栈开发课程】记录页面(一)--添加记录页面
这一章,将在首页生成的记录,也就是将records数据表中的数据显示出来
464 0
6.1【微信小程序全栈开发课程】记录页面(一)--添加记录页面
|
数据采集 小程序 定位技术
[笔记]微信小程序开发《三》框架基础:小程序生命周期、全局配置、页面配置。
[笔记]微信小程序开发《三》框架基础:小程序生命周期、全局配置、页面配置。
|
JSON 前端开发 数据库
注册功能实现 | 学习笔记
快速学习 注册功能实现
106 0

热门文章

最新文章