今日頭條極速版邀請碼是多少?功能是怎麼實現?

xiyangyang001發表於2019-06-27

邀請碼(例如 C8RB8DXF )的特性

唯一性:確保每個使用者的邀請碼都是獨一無二的,這樣系統才能判定誰為邀請者,甚至可以根據邀請碼( C8RB8DXF )進行反向推導。

隨機性:不能讓使用者從邀請碼( C8RB8DXF )上輕易的看出生成的規則。

高效性:生成邀請碼( C8RB8DXF )的演算法不能過於複雜,或耗費過度系統資源。

簡潔性:使用者可以方便的輸入,記錄,辨別是否輸入錯誤等。

平時我們看到的邀請碼( C8RB8DXF )一般有兩種型別:純數字、數字+字母(通常大寫),而邀請碼的長度通常在8位左右( C8RB8DXF )就是為了滿足簡潔性。

隨機生成邀請碼( C8RB8DXF

無論是純數字還是數字加大寫字母形式,使用隨機演算法生成一個邀請碼然後判斷此隨機碼是否已經被使用,如果被使用則重新生成。這可能是最初步的思路,但此種方法弊端甚多。

以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防止它們兩兩混淆。

1

這樣,總共獲得了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陣列裡面的資料進行擴充,比如講小寫字母也新增進來。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69936270/viewspace-2648955/,如需轉載,請註明出處,否則將追究法律責任。

相關文章