/*
* Copyright 2025 mingliqiye
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ProjectName mingli-utils
* ModuleName mingli-utils.main
* CurrentFile UUID.java
* LastUpdate 2025-09-10 11:14:27
* UpdateUser MingLiPro
*/
package com.mingliqiye.utils.uuid;
import com.github.f4b6a3.uuid.UuidCreator;
import com.mingliqiye.utils.string.StringUtil;
import com.mingliqiye.utils.time.DateTime;
import com.mingliqiye.utils.time.DateTimeOffset;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
import java.util.Objects;
import lombok.Data;
/**
* UUID 工具类,用于生成、解析和操作 UUID。
* 支持时间戳型 UUID(版本1)以及标准 UUID 的创建与转换。
*
* @author MingLiPro
*/
@Data
public class UUID implements Serializable {
/**
* 内部封装的 java.util.UUID 实例
*/
private java.util.UUID uuid;
/**
* 构造一个由指定高位和低位组成的 UUID。
*
* @param msb 高64位
* @param lsb 低64位
*/
public UUID(long msb, long lsb) {
uuid = new java.util.UUID(msb, lsb);
}
/**
* 构造一个基于当前时间的时间戳型 UUID(版本1)。
* 由于springboot 默认使用此构造函数构造UUID 导致致命BUG 废弃
* 下个大版本删除
* @deprecated 请使用 UUID.getTimeBased()
*/
@Deprecated
public UUID() {
uuid = UUID.getTimeBased().GetUUID();
}
/**
* 使用给定的 java.util.UUID 对象构造一个新的 UUID 实例。
*
* @param uuid java.util.UUID 实例
*/
public UUID(java.util.UUID uuid) {
this.uuid = uuid;
}
/**
* 根据字符串表示的 UUID 构造一个新的 UUID 实例。
*
* @param uuid 字符串形式的 UUID
*/
public UUID(String uuid) {
this.uuid = java.util.UUID.fromString(uuid);
}
/**
* 获取一个基于当前时间生成的时间戳型 UUID(版本1)。
*
* @return 时间戳型 UUID
*/
public static UUID getTimeBased() {
return new UUID(UuidCreator.getTimeBased());
}
/**
* 将字节数组转换为 UUID 实例。
*
* @param bytes 表示 UUID 的 16 字节数据
* @return 新建的 UUID 实例
*/
public static UUID of(byte[] bytes) {
if (bytes == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
long msb = bb.getLong();
long lsb = bb.getLong();
return new UUID(msb, lsb);
}
/**
* 将字符串解析为 UUID 实例,如果解析失败则抛出 UUIDException。
*
* @param data UUID 字符串
* @return 解析后的 UUID 实例
* @throws UUIDException 如果解析失败
*/
public static UUID of(String data) {
if (data == null) {
return null;
}
try {
return new UUID(java.util.UUID.fromString(data));
} catch (Exception e) {
throw new UUIDException(e.getMessage(), e);
}
}
/**
* 将 UUID 转换为 16 字节的字节数组。
*
* @return 表示该 UUID 的字节数组
*/
public byte[] toBytes() {
if (this.uuid == null) {
return null;
}
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
/**
* 获取内部封装的 java.util.UUID 实例。
*
* @return java.util.UUID 实例
*/
public java.util.UUID GetUUID() {
return uuid;
}
/**
* 将 UUID 转换为字符串表示,默认使用小写格式。
*
* @return UUID 字符串
*/
public String toUUIDString() {
return toUUIDString(false);
}
/**
* 将 UUID 转换为字符串表示,并可选择是否使用大写。
*
* @param u 是否使用大写格式
* @return UUID 字符串
* @throws UUIDException 如果 uuid 为 null
*/
public String toUUIDString(boolean u) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (u) {
return uuid.toString().toUpperCase(Locale.ROOT);
}
return uuid.toString();
}
/**
* 计算此 UUID 的哈希码。
*
* @return 哈希码值
*/
@Override
public int hashCode() {
return Objects.hash(uuid);
}
/**
* 判断两个 UUID 是否相等。
*
* @param o 比较对象
* @return 如果相等返回 true,否则返回 false
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UUID uuid = (UUID) o;
return Objects.equals(this.uuid, uuid.uuid);
}
/**
* 返回此 UUID 的字符串表示,包含版本信息和时间戳(如果是版本1)。
*
* @return UUID 的详细字符串表示
*/
@Override
public String toString() {
if (uuid == null) {
return "UUID(null)";
}
return StringUtil.format(
"UUID(uuid={},version={})",
toUUIDString(true),
uuid.version()
);
}
/**
* 从时间戳型 UUID 中提取时间戳并转换为 DateTime 对象。
*
* @return 对应的 DateTime 对象;如果 uuid 为 null,则返回 null
*/
public DateTime getDateTime() {
if (uuid == null) {
return null;
}
if (uuid.version() != 1) {
return null;
}
return DateTime.of(uuid.timestamp() / 10_000).add(
DateTimeOffset.of(-141427L, ChronoUnit.DAYS)
);
}
/**
* 从时间戳型 UUID 中提取 MAC 地址,默认使用冒号分隔符。
*
* @return MAC 地址字符串
* @throws UUIDException 如果 uuid 为 null
*/
public String extractMACFromUUID() {
return extractMACFromUUID(null);
}
/**
* 从时间戳型 UUID 中提取 MAC 地址,并允许自定义分隔符。
*
* @param spec 分隔符字符,默认为 ":"
* @return MAC 地址字符串
* @throws UUIDException 如果 uuid 为 null
*/
public String extractMACFromUUID(String spec) {
if (uuid == null) {
throw new UUIDException("uuid is null : NullPointerException");
}
if (spec == null) {
spec = ":";
}
long leastSigBits = uuid.getLeastSignificantBits();
long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
byte[] macBytes = new byte[6];
// 提取 MAC 地址的每个字节
for (int i = 0; i < 6; i++) {
macBytes[5 - i] = (byte) (macLong >> (8 * i));
}
StringBuilder mac = new StringBuilder();
// 构造 MAC 地址字符串
for (int i = 0; i < 6; i++) {
mac.append(String.format("%02X", macBytes[i]));
if (i < 5) {
mac.append(spec);
}
}
return mac.toString();
}
}