高性能(无需判重)批量生成优惠券码方案

UUID方案:将uuid分成等份,转成16进制即可。(代码里有11位和8位数的券码代码参考)

雪花id方案:实现思路很简单,生成雪花id(可根据需求,换成使用uuid的方案,测试代码里有两种方法),将雪花id的值转换成自定义的55进制,长度会大大缩短,且基于雪花id,所以不需要判断去重。目前我们公司接受券码不超过12位就行,所以没有再对其进一步优化。

话不多说,直接上代码,欢迎各位大佬留言指正

优惠券码生成工具类CouponCodeUtil2(基于uuid,测试使用,注意修改):

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class CouponCodeUtil2 {

    private static final char[] FIX_STR = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
            'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n',
            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '2', '3', '4', '5', '6', '7', '8', '9'};

    private static final String STR = new String(FIX_STR);

    private static String strToSplitHex16(String uuid) {
        StringBuilder shortBuffer = new StringBuilder();
        //我们这里想要保证券码为11位,所以32位uuid加了一位随机数,再分成11等份;(如果是8位券码,则32位uuid分成8等份进行计算即可)
        for (int i = 0; i < 11; i++) {
            String str = uuid.substring(i * 3, i * 3 + 3);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(FIX_STR[x % FIX_STR.length]);
        }
        return shortBuffer.toString();
    }

    private static String strToSplitHex16V2(String uuid) {
        StringBuilder shortBuffer = new StringBuilder();
        //8位券码,则32位uuid分成8等份进行计算即可
        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(FIX_STR[x % FIX_STR.length]);
        }
        return shortBuffer.toString();
    }

    /**
     * 产生优惠券编码的方法
     *
     * @return
     */
    public static String generateCouponCode() {
        String uuid = UUIDGenerator.get() + new Random().nextInt(10);
        return strToSplitHex16(uuid);
    }

    public static String generateCouponCodeV2() {
        String uuid = UUIDGenerator.get();
        return strToSplitHex16V2(uuid);
    }

    public static void main(String[] args) {
        System.out.println("---------------- 分割线 -----------------------");
        /*基于uuid生成:因为基于雪花id产生的券码,被猜的可能性较大*/
        Set<String> hashSet = new HashSet<>(10000000);
        for (int i = 0; i < 10000000; i++) {
            //产生的券码
            String key = generateCouponCodeV2();
            if (hashSet.contains(key)) {
                System.out.println(key);
                System.out.println("我重复了-------------====================-----------------");
            } else {
                hashSet.add(key);
            }
        }
        System.out.println("---------------- 分割线 -----------------------");
    }

优惠券码生成工具类CouponCodeUtil1(基于雪花id,测试使用,注意修改):

import java.util.HashSet;
import java.util.Set;

public class CouponCodeUtil1 {

    private static final char[] FIX_STR = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
            'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n',
            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '2', '3', '4', '5', '6', '7', '8', '9'};

    private static final String STR = new String(FIX_STR);

    public static String fFToStr(long i) {
        StringBuilder sb = new StringBuilder();
        //55进制
        while (i >= 55) {
            long a = i % 55;
            i /= 55;
            sb.append(FIX_STR[Math.toIntExact(a)]);
        }
        sb.append(FIX_STR[Math.toIntExact(i)]);
        return sb.reverse().toString();
    }

    public static long strToFF(String str) {
        long r = 0L;
        char[] chars = str.toCharArray();
        for (int i = chars.length - 1, j = 0; i >= 0; i--, j++) {
            r += STR.indexOf(chars[i]) * Math.pow(55, j);
        }
        //todo 此处有坑,会有精度丢失问题,有时候转换出来的long值貌似会和原值不一样。。
        return r;
    }

    /**
     * 产生券码的方法
     *
     * @return
     */
    public static String generateCouponCode() {
        return fFToStr(SnowFlakeUtils.nextId());
    }

    public static void main(String[] args) {
        System.out.println("---------------- 分割线 -----------------------");
        Set<String> hashSet = new HashSet<>(10);
        for (int i = 0; i < 10; i++) {
            //产生的券码
            String key = generateCouponCode();
            if (hashSet.contains(key)) {
                System.out.println(key);
                System.out.println("我重复了-------------====================-----------------");
            } else {
                hashSet.add(key);
            }
        }
        System.out.println("---------------- 分割线 -----------------------");
        /*long nextId = SnowFlakeUtils.nextId();
        System.out.println(nextId);
        //转成55进制后的字符(即我们需要的券码)
        System.out.println(fFToStr(nextId));
        //55进制字符转换成10进制的值 todo 此处有坑,会有精度丢失问题,有时候转换出来的long值貌似会和原值不一样。。
        System.out.println(strToFF(fFToStr(nextId)));
        //理论上说,一个id为20位的10进制数值,用55进制不会超过12位字符。
        System.out.println(Math.pow(10, 20) >= Math.pow(55, 12));*/
    }

}

雪花id工具类:(测试用,注意修改)

public class SnowFlakeUtils {
    /**
     * 起始的时间戳
     */
    private final static long twepoch = 1557825652094L;
 
    /**
     * 每一部分占用的位数
     */
    private final static long workerIdBits = 5L;
    private final static long datacenterIdBits = 5L;
    private final static long sequenceBits = 12L;
 
    /**
     * 每一部分的最大值
     */
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final static long maxSequence = -1L ^ (-1L << sequenceBits);
 
    /**
     * 每一部分向左的位移
     */
    private final static long workerIdShift = sequenceBits;
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    private final static long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;
 
    private static long datacenterId = 1; // 数据中心ID
    private final static long workerId = Long.valueOf(0L); // 机器ID
    private static long sequence = 0L; // 序列号
    private static long lastTimestamp = -1L; // 上一次时间戳
 
    public static synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0L) {
                timestamp = tilNextMillis();
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
 
        return (timestamp - twepoch) << timestampShift // 时间戳部分
                | datacenterId << datacenterIdShift // 数据中心部分
                | workerId << workerIdShift // 机器标识部分
                | sequence; // 序列号部分
    }
 
    private static long tilNextMillis() {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
 
    private static long timeGen() {
        return System.currentTimeMillis();
    }
 
    public static void main(String[] args) {
        System.out.println(nextId());
    }

}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值