From 39e8723dbddeb55442cc5750e917a7ae7345efbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Sun, 5 Oct 2025 22:30:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(uuidv7):=20=E6=B7=BB=E5=8A=A0=20UUIDv7=20?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=99=A8=E5=8F=8A=E8=A7=A3=E6=9E=90=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 UUIDv7 生成器实现,支持基于时间戳、节点 ID 和序列号的唯一标识符生成。 包含初始化、生成、解析以及错误处理等相关逻辑,并提供字节和字符串两种形式的 序列化与反序列化方法。同时添加了对生成器未初始化和节点 ID 超限的错误定义。 --- serial_code/uuidv7/errors.go | 13 ++ serial_code/uuidv7/uuid.go | 225 +++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 serial_code/uuidv7/errors.go create mode 100644 serial_code/uuidv7/uuid.go diff --git a/serial_code/uuidv7/errors.go b/serial_code/uuidv7/errors.go new file mode 100644 index 0000000..0bb182e --- /dev/null +++ b/serial_code/uuidv7/errors.go @@ -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超出范围" +} diff --git a/serial_code/uuidv7/uuid.go b/serial_code/uuidv7/uuid.go new file mode 100644 index 0000000..2cf9268 --- /dev/null +++ b/serial_code/uuidv7/uuid.go @@ -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 // 主机编号容量为0~32 + 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) +}