|
|
@@ -0,0 +1,142 @@
|
|
|
+package cn.seecoder.common.util;
|
|
|
+
|
|
|
+import java.util.Random;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author PuHong Weng
|
|
|
+ * @date 2021/5/6
|
|
|
+ * @description:
|
|
|
+ * 参考 https://blog.csdn.net/wo541075754/article/details/81673517 实现
|
|
|
+ * 用来生成项目邀请码
|
|
|
+ * 1)入参用户ID:1 <br/>
|
|
|
+ * 2)使用自定义进制转换之后为:V <br/>
|
|
|
+ * 3)转换未字符串,并在后面添加'A':VA <br/>
|
|
|
+ * 4)在VA后面再随机补足4位,得到:VAHKHE <br/>
|
|
|
+ * 5)反向转换时以'A'为分界线,'A'后面的不再解析 <br/>
|
|
|
+ */
|
|
|
+public class ShareCodeUtil {
|
|
|
+ /**
|
|
|
+ * 自定义进制(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';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Z补位字符,不能与自定义重复,用来分割 有效时间 和 id
|
|
|
+ */
|
|
|
+ private static final char TIME_CHAR = 'Z';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 进制长度
|
|
|
+ */
|
|
|
+ private static final int BIN_LEN = BASE.length;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成邀请码最小长度
|
|
|
+ */
|
|
|
+ private static final int CODE_LEN = 16;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ID转换为邀请码
|
|
|
+ * 邀请码包含了id和创建时间两个有效信息
|
|
|
+ * @param id
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String idToCode(Long id) {
|
|
|
+
|
|
|
+ String idCode = encode(id);
|
|
|
+
|
|
|
+ String timeCode = encode(System.currentTimeMillis());
|
|
|
+
|
|
|
+ String result = timeCode + TIME_CHAR + idCode;
|
|
|
+
|
|
|
+ // 长度不足指定长度则随机补全
|
|
|
+ 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 长度为2的long数组,第一个值为id,第二个值为code生成时间
|
|
|
+ */
|
|
|
+ public static Long[] codeToId(String code) {
|
|
|
+ Long time = decode(code.substring(0, code.indexOf(TIME_CHAR)));
|
|
|
+ Long id = decode(code.substring(code.indexOf(TIME_CHAR)+1, code.indexOf(SUFFIX_CHAR)));
|
|
|
+ return new Long[]{id,time};
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将输入的id加密成code
|
|
|
+ * @param id
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static String encode(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)];
|
|
|
+ // 将字符数组转化为字符串
|
|
|
+ return new String(buf, charPos, BIN_LEN - charPos);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将输入的id加密成code
|
|
|
+ * @param code
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static Long decode(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 (i > 0) {
|
|
|
+ result = result * BIN_LEN + index;
|
|
|
+ } else {
|
|
|
+ result = index;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+// public static void main(String[] args) {
|
|
|
+// String code = ShareCodeUtil.idToCode(12L);
|
|
|
+// System.out.println(code);
|
|
|
+// Arrays.stream(ShareCodeUtil.codeToId(code)).forEach(x-> System.out.println(x));
|
|
|
+//
|
|
|
+// }
|
|
|
+}
|