ph-utils
Version:
js 开发工具集,前后端都可以使用(commonjs和es module)
171 lines (170 loc) • 6.09 kB
JavaScript
/** 雪花ID, 推荐在全局构造一个对象用于生成id */
export class SnowflakeID {
/**
* 构造函数
*
* @param machineId 机器标识,默认为1
* @param epoch 时间戳起始值,默认为1288834974657
*/
constructor(machineId = 1, epoch = 1288834974657) {
/** 版本号, 默认为: 0 */
this.version = 0;
this.machineId = BigInt(machineId);
this.epoch = BigInt(epoch);
this._lastTimestamp = BigInt(0);
this._sequence = BigInt(0);
}
_getTimestamp() {
return BigInt(Date.now());
}
_waitNextMillis(cTimestamp) {
let timestamp = cTimestamp;
while (timestamp <= this._lastTimestamp) {
timestamp = BigInt(Date.now());
}
return timestamp;
}
/**
* 生成雪花ID
*
* @returns 返回生成的唯一ID字符串
* @throws 如果时钟回退,抛出错误
*/
generate() {
let cTimestamp = this._getTimestamp();
if (cTimestamp < this._lastTimestamp) {
throw new Error("Clock moved backwards");
}
if (cTimestamp === this._lastTimestamp) {
// TODO: increment sequence
// 雪花id,末尾: 12位序列号(二进制12个1等于16进制4095),最多4096个(从0到4095)
this._sequence = (this._sequence + 1n) & BigInt("0xFFF");
if (this._sequence === 0n) {
// 同一时间戳,序列号溢出,等待下一毫秒
cTimestamp = this._waitNextMillis(cTimestamp);
}
}
else {
this._sequence = BigInt(0);
}
this._lastTimestamp = cTimestamp;
const timeDiff = cTimestamp - this.epoch;
let id = (timeDiff << BigInt(22)) |
(this.machineId << BigInt(12)) |
this._sequence;
let idstr = id.toString().padStart(19, "0");
return `${this.version}${idstr}`;
}
parse(snowflakeID, epoch, includeVersion = true) {
let version = undefined;
if (includeVersion) {
snowflakeID = snowflakeID.substring(1);
version = snowflakeID.substring(0, 1);
}
let epochTime = epoch || Number(this.epoch);
const id = BigInt(snowflakeID);
// 1FFFFFFFFFF 就是二进制的41个1, 雪花id前面41为为时间戳,间隔部分
let timeDiff = (id >> BigInt(22)) & BigInt("0x1FFFFFFFFFF");
const flowTime = Number(timeDiff) + epochTime;
// 雪花id中间10位为机器标识, 0x3FF(二进制10个1) 进行位运算
const machineId = (id >> BigInt(12)) & BigInt("0x3FF");
const sequence = id & BigInt("0xFFF");
return {
value: snowflakeID,
timeOffset: timeDiff,
timestamp: flowTime,
machineId: machineId,
sequence: sequence,
epoch: epochTime,
version: version,
};
}
}
/** 将uuid转换为更简单的唯一标记id */
export class ShortUUID {
/**
* 构造函数,用于初始化字母表
* @param {string} [alphabet] - 可选参数,用于指定自定义字母表
* 如果提供了alphabet参数,则将其设置为实例的字母表属性
* 如果未提供alphabet参数,则使用默认值
*/
constructor(alphabet) {
this.alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
if (alphabet) {
this.alphabet = alphabet;
}
}
/**
* 将UUID字符串进行编码处理
* @param uuid - 需要编码的UUID字符串
* @returns 编码后的字符串
*/
encode(uuid, alphabet) {
// 将UUID字符串转换为整数
const uuidInt = this._uuidHexToInt(uuid);
// 将整数转换为特定格式的字符串
return this._intToString(uuidInt, alphabet);
}
/**
* 解码短UUID字符串,将其转换为UUID整数和十六进制格式
* @param shortUUID - 需要解码的短UUID字符串
* @returns 返回包含UUID整数和十六进制格式的对象
*/
decode(shortUUID, alphabet) {
const uuidInt = this._stringToInt(shortUUID, alphabet);
return {
uuidInt: uuidInt,
uuid: this._uuidIntToHex(uuidInt),
};
}
_stringToInt(str, alphabet) {
if (!alphabet)
alphabet = this.alphabet;
const alphabetlen = BigInt(alphabet.length);
let result = BigInt(0);
let multiplier = BigInt(1);
const strlen = str.length;
for (let i = strlen - 1; i >= 0; i--) {
const char = str[i];
const index = alphabet.indexOf(char);
if (index === -1) {
throw new Error(`Character "${char}" not found in alphabet`);
}
result += BigInt(index) * multiplier;
multiplier *= alphabetlen;
}
return result;
}
_intToString(uuidInt, alphabet) {
if (!alphabet)
alphabet = this.alphabet;
const alphabetlen = BigInt(alphabet.length);
let num = uuidInt;
const result = [];
// uuidInt = d₀ × 1(base的0次方) + d₁ × base + d₂ × base²...
while (num) {
const index = Number(num % alphabetlen);
num = num / alphabetlen;
result.push(alphabet.charAt(index));
}
// 倒序,保证高位在前
return result.reverse().join("");
}
_uuidHexToInt(uuid) {
const uuidHex = uuid.replaceAll("-", "");
return BigInt(`0x${uuidHex}`);
}
_uuidIntToHex(uuidInt) {
const uuidHexStr = uuidInt.toString(16);
if (!/^[0-9a-fA-F]{32}$/.test(uuidHexStr)) {
throw new Error("Invalid UUID string (must be 32 hex characters)");
}
return [
uuidHexStr.slice(0, 8),
uuidHexStr.slice(8, 12),
uuidHexStr.slice(12, 16),
uuidHexStr.slice(16, 20),
uuidHexStr.slice(20),
].join("-");
}
}