EasyAudioEncode/domain/uniqueid/bz.go
2025-12-25 17:01:46 +08:00

84 lines
2.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// uniqueid
// 的设计是用于生成全局唯一的 ID避免重复。
// 此库不考虑分布式,仅通过数据库主键索引来实现。
// 当 id 重复时,由业务端抛出错误即可
package uniqueid
import (
"context"
"crypto/rand"
"log/slog"
"math/big"
"time"
"git.lnton.com/lnton/pkg/orm"
"github.com/ixugo/goddd/pkg/hook"
)
const (
// 删除 o 和 i 的字符集,避免视觉混淆
LetterBytes36NoOI = "abcdefghjklmnpqrstuvwxyz0123456789"
LetterBytes72 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
LetterBytes36 = "abcdefghijklmnopqrstuvwxyz0123456789" // default
LetterBytes36Upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
type IDManager struct {
store UniqueIDStorer
// TODO: 可以初始化时读取数据库内的数量,判断重复因子,从而减少尝试或更换策略
letterBytes string // 随机字符串字符集
}
func NewIDManager(store UniqueIDStorer) *IDManager {
return &IDManager{
store: store,
letterBytes: LetterBytes36,
}
}
// SetLetterBytes 设置随机字符串字符集
func (m *IDManager) SetLetterBytes(letterBytes string) {
m.letterBytes = letterBytes
}
// UniqueID 获取唯一 id
func (m *IDManager) UniqueID(prefix string, length int) string {
cost := hook.UseTiming(time.Second)
defer cost()
// 如果在最低长度中,碰撞比较频繁,增加 1 位长度再试一次
for i := range 10 {
// 生成自定义长度随机数,通过数据库主键来防止碰撞,碰撞后再次尝试
for range 36 {
id := prefix + GenerateRandomString(m.letterBytes, length+i)
if err := m.store.Add(context.Background(), &UniqueID{ID: id}); err != nil {
slog.Error("UniqueID", "err", err)
continue
}
return id
}
}
slog.Error("UniqueID", "err", "超过最大循环次数,未获取到唯一 id")
return "unknown"
}
// UndoUniqueID 删除唯一 id
func (m *IDManager) UndoUniqueID(id string) error {
var uni UniqueID
return m.store.Del(context.Background(), &uni, orm.Where("id=?", id))
}
// GenerateRandomString 生成随机字符串
func GenerateRandomString(letterBytes string, length int) string {
lettersLength := big.NewInt(int64(len(letterBytes)))
result := make([]byte, length)
for i := 0; i < length; i++ {
idx, _ := rand.Int(rand.Reader, lettersLength)
result[i] = letterBytes[idx.Int64()]
}
return string(result)
}