feat(uuidv7): 添加 UUIDv7 生成器及解析功能

新增 UUIDv7 生成器实现,支持基于时间戳、节点 ID 和序列号的唯一标识符生成。
包含初始化、生成、解析以及错误处理等相关逻辑,并提供字节和字符串两种形式的
序列化与反序列化方法。同时添加了对生成器未初始化和节点 ID 超限的错误定义。
This commit is contained in:
徐涛
2025-10-05 22:30:22 +08:00
parent 9952b2677c
commit 39e8723dbd
2 changed files with 238 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package uuidv7
type UUIDv7GeneratorNotInitializedError struct{}
func (e *UUIDv7GeneratorNotInitializedError) Error() string {
return "UUIDv7生成器尚未初始化"
}
type UUIDv7NodeIDExceededError struct{}
func (e *UUIDv7NodeIDExceededError) Error() string {
return "UUIDv7节点ID超出范围"
}

225
serial_code/uuidv7/uuid.go Normal file
View File

@@ -0,0 +1,225 @@
package uuidv7
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"sync"
"time"
)
const (
epoch = 1645566142222 // 自定义纪元2022-02-22 22:22:22.222 UTC
nodeBits = 5 // 主机编号容量为032
sequenceBits = 18 // 每毫秒生成序列号最大为2^18-1即262144个
maxNodeID = (1 << nodeBits) - 1
maxSequence = (1 << sequenceBits) - 1
timestampShift = nodeBits + sequenceBits
nodeShift = sequenceBits
)
type UUIDv7Generator struct {
locker sync.Mutex
nodeID int64
lastTime int64
sequence int64
}
type UUIDv7Components struct {
Timestamp int64 // 毫秒时间戳(相对于自定义纪元)
NodeID int64 // 5位节点ID
Sequence int64 // 18位序列号
Version int // 版本号应为7
Variant int // 变体应为2表示10xx
RawBytes [16]byte // 原始字节
}
var generatorInstance *UUIDv7Generator
// 获取UUIDv7生成器实例。如果实例尚未初始化则返回错误。
func Generator() (*UUIDv7Generator, error) {
if generatorInstance == nil {
return nil, &UUIDv7GeneratorNotInitializedError{}
}
return generatorInstance, nil
}
// 指定一个主机编号并完成UUIDv7生成器的初始化。如果主机编号超出范围则返回错误。
func Initialize(hostSerial int64) error {
if hostSerial < 0 || hostSerial > maxNodeID {
return &UUIDv7NodeIDExceededError{}
}
generatorInstance = &UUIDv7Generator{
nodeID: hostSerial,
lastTime: epoch,
}
return nil
}
// 获取当前时间对应的新纪元毫秒时间戳。
func (g *UUIDv7Generator) Now() int64 {
return time.Now().UnixMilli() - epoch
}
// 按顺序生成一个UUIDv7序号组件。
func (g *UUIDv7Generator) Next() UUIDv7Components {
g.locker.Lock()
defer g.locker.Unlock()
now := g.Now()
if now < g.lastTime {
// 防止时间回拔的情况,发生回拔时,直接使用上一次的时间戳
now = g.lastTime
}
if now == g.lastTime {
g.sequence++
if g.sequence > maxSequence {
time.Sleep(time.Millisecond)
now = g.Now()
g.sequence = 0
}
} else {
g.sequence = 0
}
g.lastTime = now
return UUIDv7Components{
Timestamp: now,
NodeID: g.nodeID,
Sequence: g.sequence,
Version: 7,
Variant: 2,
}
}
// 读取随机字节
func (c *UUIDv7Components) readRandom(bytes []byte) error {
_, err := rand.Read(bytes)
return err
}
// 获取UUIDv7原始字节数组
func (c *UUIDv7Components) Bytes() [16]byte {
// 构造 UUID
var uuid [16]byte
// 时间戳48 位)
timestamp := uint64(c.Timestamp) << timestampShift
seqAndNode := (uint64(c.Sequence) << nodeShift) | uint64(c.NodeID)
// 写入高位时间戳
uuid[0] = byte(timestamp >> 56)
uuid[1] = byte(timestamp >> 48)
uuid[2] = byte(timestamp >> 40)
uuid[3] = byte(timestamp >> 32)
uuid[4] = byte(timestamp >> 24)
uuid[5] = byte(timestamp >> 16)
// 写入低位时间戳 + 版本号4 位)
uuid[6] = byte((timestamp >> 8) | 0x70) // version 7
uuid[7] = byte(timestamp)
// 写入 seq + node
uuid[8] = byte((seqAndNode >> 16) | 0x80) // variant 10xx
uuid[9] = byte(seqAndNode >> 8)
uuid[10] = byte(seqAndNode)
// 剩余位全为 0可扩展为更多节点 ID 或随机数)
randomBytes := make([]byte, 5)
if err := c.readRandom(randomBytes); err != nil {
// 如果随机数生成失败,使用时间相关值作为后备
t := time.Now().UnixNano()
randomBytes[0] = byte(t)
randomBytes[1] = byte(t >> 8)
randomBytes[2] = byte(t >> 16)
randomBytes[3] = byte(t >> 24)
randomBytes[4] = byte(c.Sequence)
}
copy(uuid[11:16], randomBytes)
return uuid
}
// 获取UUIDv7字符串
func (c *UUIDv7Components) String() string {
uuid := c.Bytes()
return fmt.Sprintf("%x-%x-%x-%x-%x",
uuid[0:4],
uuid[4:6],
uuid[6:8],
uuid[8:10],
uuid[10:16])
}
// 对解析后的UUIDv7进行比较按照时间戳、序列号、节点ID进行排序
func CompareUUIDv7(a, b *UUIDv7Components) int {
if a.Timestamp > b.Timestamp {
return 1
}
if a.Timestamp < b.Timestamp {
return -1
}
if a.Sequence > b.Sequence {
return 1
}
if a.Sequence < b.Sequence {
return -1
}
if a.NodeID > b.NodeID {
return -1
}
if a.NodeID < b.NodeID {
return 1
}
return 0
}
// 与另一个UUIDv7进行比较按照时间戳、序列号、节点ID进行排序
func (c *UUIDv7Components) Compare(b *UUIDv7Components) int {
return CompareUUIDv7(c, b)
}
// ParseUUIDv7FromBytes 从字节数组解析 UUIDv7
func ParseUUIDv7FromBytes(data [16]byte) (*UUIDv7Components, error) {
// 提取时间戳前48位
timestamp := (int64(data[0]) << 40) |
(int64(data[1]) << 32) |
(int64(data[2]) << 24) |
(int64(data[3]) << 16) |
(int64(data[4]) << 8) |
int64(data[5])
// 提取版本号第6字节的高4位
version := int(data[6] >> 4)
// 提取变体第8字节的高2位
variant := int(data[8] >> 6)
// 提取序列号和节点ID
// 第8字节的低2位 + 第9字节 + 第10字节的高3位组成23位
seqAndNode := (int64(data[8]&0x3F) << 16) | // 第8字节低6位
(int64(data[9]) << 8) | // 第9字节
int64(data[10]) // 第10字节
// 分离序列号高18位和节点ID低5位
sequence := seqAndNode >> 5
nodeID := seqAndNode & 0x1F
return &UUIDv7Components{
Timestamp: timestamp,
NodeID: nodeID,
Sequence: sequence,
Version: version,
Variant: variant,
RawBytes: data,
}, nil
}
// ParseUUIDv7FromString 从字符串解析 UUIDv7
func ParseUUIDv7FromString(uuidStr string) (*UUIDv7Components, error) {
// 移除连字符并验证长度
cleanStr := strings.ReplaceAll(uuidStr, "-", "")
if len(cleanStr) != 32 {
return nil, fmt.Errorf("无效的UUIDv7字符串长度")
}
// 解码十六进制字符串
bytes, err := hex.DecodeString(cleanStr)
if err != nil {
return nil, fmt.Errorf("无效的UUIDv7字符串格式: %v", err)
}
// 转换为数组
var uuidBytes [16]byte
copy(uuidBytes[:], bytes)
return ParseUUIDv7FromBytes(uuidBytes)
}