|
|
|
@ -0,0 +1,127 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* https://www.mall4j.com/
|
|
|
|
|
*
|
|
|
|
|
* 未经允许,不可做商业用途!
|
|
|
|
|
*
|
|
|
|
|
* 版权所有,侵权必究!
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package com.yami.shop.common.util;
|
|
|
|
|
|
|
|
|
|
// 导入 Hutool 库中用于生成分布式唯一ID的 Snowflake 类,通常基于雪花算法实现
|
|
|
|
|
import cn.hutool.core.lang.Snowflake;
|
|
|
|
|
// 导入 Spring 框架用于实现依赖注入的注解以及将类标记为组件的注解,表明该类是受 Spring 管理的组件
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* IdUtil 是一个进制转换相关的工具类,主要提供了以下功能:
|
|
|
|
|
* 1. 支持在十进制和基于特定字符集(DICT)定义的进制之间进行转换,最大支持十进制和 DICT.length() 进制的相互转换操作。
|
|
|
|
|
* 2. 能够根据从数据库中获取的记录 ID(通常是较大范围的数值)生成对应的短网址编码,便于在一些场景下使用更简短的编码来表示较长的 ID。
|
|
|
|
|
* 3. 可以依据给定的短网址编码解析出其在数据库中对应的原始记录 ID,实现编码与原始 ID 的相互转换。
|
|
|
|
|
* 4. 借助注入的 Snowflake 实例,还能生成下一个短 ID(基于雪花算法生成唯一 ID 并转换为短网址编码形式)。
|
|
|
|
|
*
|
|
|
|
|
* @author xuliugen
|
|
|
|
|
* @date 2018/04/23
|
|
|
|
|
*/
|
|
|
|
|
@Component
|
|
|
|
|
public class IdUtil {
|
|
|
|
|
|
|
|
|
|
// 通过 Spring 的依赖注入机制,注入一个 Snowflake 实例,用于生成分布式唯一 ID(可能用于生成短 ID 的基础)
|
|
|
|
|
@Autowired
|
|
|
|
|
private Snowflake snowflake;
|
|
|
|
|
|
|
|
|
|
// 定义了一个包含数字和大小写字母(去除了容易混淆的部分字母)的字符串,作为自定义进制的字符集,用于进制转换操作
|
|
|
|
|
private static final String DICT = "0123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
|
|
|
// 计算出基于 DICT 字符集的进制数,即字符集的长度,用于进制转换过程中的计算
|
|
|
|
|
private static final int SEED = DICT.length();
|
|
|
|
|
// 定义了短网址编码的最小长度,用于在生成短网址编码时,如果长度不足则进行补位操作,保证编码长度符合一定要求
|
|
|
|
|
private static final int ID_MIN_LENGTH = 6;
|
|
|
|
|
/**
|
|
|
|
|
* 将 DICT 字符串转换为字符数组,方便在进制转换过程中通过索引快速获取对应字符,用于数字到字符的映射操作。
|
|
|
|
|
*/
|
|
|
|
|
private static final char[] CHARS = DICT.toCharArray();
|
|
|
|
|
/**
|
|
|
|
|
* 创建一个字符到数字的映射关系的 Map,用于在解析短网址编码时,根据字符快速获取对应的数字(在自定义进制下的数字表示),实现字符到数字的转换操作。
|
|
|
|
|
*/
|
|
|
|
|
private static final Map<Character, Integer> NUMBERS = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
// 静态代码块,用于初始化 NUMBERS 这个字符到数字的映射 Map,遍历 CHARS 字符数组,将每个字符与其对应的索引(在自定义进制下的数字)存入 Map 中
|
|
|
|
|
static {
|
|
|
|
|
int len = CHARS.length;
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
|
NUMBERS.put(CHARS[i], i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将给定的十进制数字(通常是较大范围的数值,如数据库记录 ID)转换为基于自定义进制(由 DICT 定义)的短网址编码字符串。
|
|
|
|
|
* 转换过程采用除基取余法,不断将十进制数除以自定义进制数(SEED),取余数作为对应字符在 DICT 中的索引,构建短网址编码字符串,
|
|
|
|
|
* 并且如果生成的编码长度小于最小长度要求(ID_MIN_LENGTH),会在前面补位(添加 DICT 中的第一个字符)以达到最小长度。
|
|
|
|
|
*
|
|
|
|
|
* @param id 要转换的十进制数字,通常表示数据库中记录的 ID,范围大致在 (1 - 56.8 billion),不过实际范围取决于具体业务场景
|
|
|
|
|
* @return 转换后的基于自定义进制的短网址编码字符串,例如可能返回类似 "RwTji8"、"GijT7Y" 等形式的字符串
|
|
|
|
|
*/
|
|
|
|
|
public static String encode(long id) {
|
|
|
|
|
// 创建一个可变的字符串构建器,用于逐步构建短网址编码字符串
|
|
|
|
|
StringBuilder shortUrl = new StringBuilder();
|
|
|
|
|
// 当传入的十进制数字大于 0 时,进行进制转换操作,采用除基取余法
|
|
|
|
|
while (id > 0) {
|
|
|
|
|
// 计算当前十进制数除以自定义进制数(SEED)的余数,将其转换为整数类型,该余数将作为在 DICT 中查找对应字符的索引
|
|
|
|
|
int r = (int) (id % SEED);
|
|
|
|
|
// 将根据余数获取到的对应字符插入到短网址编码字符串的开头(逆序构建编码字符串)
|
|
|
|
|
shortUrl.insert(0, CHARS[r]);
|
|
|
|
|
// 更新十进制数,将其除以自定义进制数,得到下一轮循环要处理的数字
|
|
|
|
|
id = id / SEED;
|
|
|
|
|
}
|
|
|
|
|
// 获取当前已经构建好的短网址编码字符串的长度
|
|
|
|
|
int len = shortUrl.length();
|
|
|
|
|
// 如果长度小于最小长度要求(ID_MIN_LENGTH),进行补位操作
|
|
|
|
|
while (len < ID_MIN_LENGTH) {
|
|
|
|
|
// 在短网址编码字符串的开头插入 DICT 中的第一个字符(通常是 '0')进行补位
|
|
|
|
|
shortUrl.insert(0, CHARS[0]);
|
|
|
|
|
// 更新长度
|
|
|
|
|
len++;
|
|
|
|
|
}
|
|
|
|
|
// 返回最终构建好的短网址编码字符串
|
|
|
|
|
return shortUrl.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据给定的短网址编码字符串(基于自定义进制)解析出其对应的十进制数字,该十进制数字通常对应数据库中的记录 ID。
|
|
|
|
|
* 解析过程是将短网址编码中的每个字符按照其在自定义进制下的权重(通过幂运算计算)转换为十进制数,再累加起来得到最终的十进制结果。
|
|
|
|
|
*
|
|
|
|
|
* @param key 要解析的短网址编码字符串,例如 "RwTji8"、"GijT7Y" 等形式的字符串,每个字符都代表自定义进制下的一位数字
|
|
|
|
|
* @return 解析出的十进制数字,对应数据库中记录的 ID
|
|
|
|
|
*/
|
|
|
|
|
public static long decode(String key) {
|
|
|
|
|
// 将传入的短网址编码字符串转换为字符数组,方便逐个字符进行处理
|
|
|
|
|
char[] shorts = key.toCharArray();
|
|
|
|
|
// 获取字符数组的长度,即短网址编码的长度
|
|
|
|
|
int len = shorts.length;
|
|
|
|
|
// 初始化用于累加计算的十进制数字为 0
|
|
|
|
|
long id = 0L;
|
|
|
|
|
// 遍历短网址编码的每个字符,从左到右(按照权重从高到低)进行解析计算
|
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
|
// 根据当前字符在 NUMBERS 映射 Map 中获取其对应的数字(在自定义进制下的数字表示),并乘以当前位置对应的权重(SEED 的幂次方),然后累加到结果中
|
|
|
|
|
id = id + (long) (NUMBERS.get(shorts[i]) * Math.pow(SEED, len - i - 1));
|
|
|
|
|
}
|
|
|
|
|
// 返回解析得到的十进制数字(数据库记录 ID)
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 借助注入的 Snowflake 实例生成一个分布式唯一 ID(基于雪花算法),然后将该唯一 ID 转换为基于自定义进制的短网址编码形式,
|
|
|
|
|
* 作为下一个短 ID 返回,可用于在业务中生成具有唯一性且简短的标识符,例如用于短链接、短编号等场景。
|
|
|
|
|
*
|
|
|
|
|
* @return 基于雪花算法生成唯一 ID 并转换后的短网址编码字符串,作为下一个短 ID
|
|
|
|
|
*/
|
|
|
|
|
public String nextShortId() {
|
|
|
|
|
return encode(snowflake.nextId());
|
|
|
|
|
}
|
|
|
|
|
}
|