订单号

ID的组成部分: 符号位、时间戳、序列号

符号位: 1bit,永远为0

时间戳: 31bit,以秒为单位,可以使用69年

序列号: 32bit,秒内的计数器,支持每秒产生2^32个不同ID

    private static final long BEGIN_TIMESTAMP = 1704067200L; // 初始时间因为是固定的,所以放在常量
    private static final int COUNT_BITS = 32; // 序列号的位数
    public static void main(String[] args) {
        // 初始时间
        LocalDateTime begin = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
        long beginSecond = begin.toEpochSecond(ZoneOffset.UTC);
        System.out.println("beginSecond = " + beginSecond);

        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        System.out.println("nowSecond = " + nowSecond);

        long timestamp = nowSecond - beginSecond;
        System.out.println("nowSecond - beginSecond = " + timestamp);

        // 获取当前日期 精确到天
        String yyyyMMdd = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // INCR key 因为key icr:order:年:月:日 自增每天都会重置不用担心会炸掉
        long increment = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + yyyyMMdd);

        long l = timestamp << COUNT_BITS | increment;

        /** 时间戳向左移32,右边拿来放序列号
         * ex: 100 << 5 | 2 = 100 * 2^5 + 2 = 3202
         * 高位:存储重要信息(如时间戳),通常占据更左侧的位置
         * 低位:存储次要信息(如递增计数器),通常占据更右侧的位置
         */
        l = timestamp << COUNT_BITS | 1L;
        System.out.println("l = " + l); 
        // 107577278518525953
        // 0001 0111 1110 0011 0000 1111 0111 0000 0000 0000 0000 0000 0000 0000 0001
        // 2 的0次方 * 1 = 1

        l = timestamp << COUNT_BITS | 2L;
        System.out.println("l = " + l); 
        // 107577278518525954
        // 0001 0111 1110 0011 0000 1111 0111 0000 0000 0000 0000 0000 0000 0000 0010
        // 2 的1次方 * 1 = 2
    }