Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

邀请码生成算法 #20

Open
TFdream opened this issue Sep 27, 2018 · 0 comments
Open

邀请码生成算法 #20

TFdream opened this issue Sep 27, 2018 · 0 comments

Comments

@TFdream
Copy link
Owner

TFdream commented Sep 27, 2018

业务场景

某些App上有新用户邀请机制,新用户注册的时候输入邀请码后注册 后台会有优惠发放。

分析

此场景下要求生成的邀请码是全局唯一的,即每个用户对应的邀请码都是唯一。

我们可以转换一下思路:每个用户都有唯一的uid,可以将uid通过某种算法生成邀请码。

实现代码

/**
 * @author Ricky Fung
 */
@Service
public class InvitationCodeService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final char[] dict = new char[]{'1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z'};

    private static final int DICT_BIT_LENGTH = 5;

    private static final int DICT_MASK = (1 << DICT_BIT_LENGTH) - 1;

    /**
     * 纪元, 2018-08-01 00:00:00
     */
    private static final long epoch = new DateTime(2018, 8, 1, 0, 0, 0).getMillis();

    /**
     * 时间戳位数
     */
    private static final int TIMESTAMP_BIT_LENGTH = 34;

    private static final long TIMESTAMP_MASK = (1L << TIMESTAMP_BIT_LENGTH) - 1;

    /**
     * 随机数
     */
    private static final int RANDOM_BIT_LENGTH = 3;
    private static final long RANDOM_MASK = (1L << RANDOM_BIT_LENGTH) - 1;

    /**
     * 随机数左移位数
     */
    private static final int RANDOM_LEFT_SHIFT_BITS = TIMESTAMP_BIT_LENGTH;
    /**
     * userId 左移位数
     */
    private static final int USER_LEFT_SHIFT_BITS = TIMESTAMP_BIT_LENGTH + RANDOM_BIT_LENGTH;

    /**
     * 所能支持的最大user_id
     */
    private static final long MAX_USER_ID = (1L << (63 - USER_LEFT_SHIFT_BITS)) - 1;

    private static final int CODE_LENGTH = 13;

    /**
     * 不足则填充 零
     */
    private static final char PADDING_CHAR = '0';

    /**
     * 生成招财码
     * @param userId
     * @return
     */
    public String genCode(long userId) {
        String code = getUniqueCode(userId);
        return padding(code, PADDING_CHAR, CODE_LENGTH);
    }

    public String getUniqueCode(long userId) {
        //1.得到一个long值
        long num = getUniqueNum(userId).getUid();
        //2.转换为字符串
        StringBuilder sb = new StringBuilder(CODE_LENGTH);
        while (num != 0) {
            int i = (int) (num & DICT_MASK);
            sb.append(dict[i]);
            num = num >> DICT_BIT_LENGTH;
        }
        StringBuilder rs = sb.reverse();
        return rs.toString();
    }

    /**
     * 产生一个64bit的正整数
     * @param userId
     * @return
     */
    public FortuneUidVO getUniqueNum(long userId) {
        //bugfix: 修复超出范围后死循环导致OOM
        if (userId> MAX_USER_ID) {
            throw new IllegalArgumentException(String.format("userId:%s 超出最大id: %s范围了", userId, MAX_USER_ID));
        }
        //
        long millis = System.currentTimeMillis();
        long thread = Thread.currentThread().getId();
        //
        long uid = (userId << USER_LEFT_SHIFT_BITS | ((thread & RANDOM_MASK) << RANDOM_LEFT_SHIFT_BITS) | ((millis - epoch) & TIMESTAMP_MASK));
        return new FortuneUidVO(userId, thread, epoch, millis, uid);
    }

    private String padding(String str, char padding, int length) {
        if (str.length() >= length) {
            return str;
        }
        StringBuilder sb = new StringBuilder(length);
        if (str.length() < length) {    //填充
            int dist = length - str.length();
            for (int i=0; i<dist; i++) {
                sb.append(padding);
            }
        }
        sb.append(str);
        return sb.toString();
    }

}
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant