This commit is contained in:
toom1996
2026-09-16 21:31:32 +08:00
parent d8f667e391
commit a939848e84
3 changed files with 303 additions and 0 deletions

87
internal/ingest/hashid.go Normal file
View File

@ -0,0 +1,87 @@
package ingest
import (
"crypto/sha256"
"encoding/binary"
"strings"
)
// 本文件是 backend_v2/internal/pkg/hashid 中「无类型编码(品牌等)」的忠实移植,
// 仅用于 spider 离线 Mock 模式下生成 brand_uid(真实上报路径下 brand_uid 直接来自后台接口,无需自编码)。
// 算法:SHA256(盐) 派生 4 个子密钥 → 32-bit 平衡 Feistel 网络 → base62 定长(minLen=8) 序列化。
// 默认盐与后台一致:hashid_secret 为空时后台走 "fashion-archive-default-salt-change-me"。
const (
hidAlphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
hidBase = 62
hidMinLen = 8
)
var hidKeys [4]uint32
// InitHashID 用部署级盐值初始化混淆密钥。盐为空时使用与后台相同的内置默认盐。
func InitHashID(secret string) {
if secret == "" {
secret = "fashion-archive-default-salt-change-me"
}
h := sha256.Sum256([]byte(secret))
for i := 0; i < 4; i++ {
hidKeys[i] = binary.BigEndian.Uint32(h[i*4 : i*4+4])
}
}
// feistel 32-bit 平衡 Feistel 网络(全局密钥版,品牌等无类型实体用)。
func feistel(v uint32, encrypt bool) uint32 {
return feistelK(v, encrypt, hidKeys)
}
func feistelK(v uint32, encrypt bool, k [4]uint32) uint32 {
const rounds = 8
l, r := uint16(v>>16), uint16(v&0xffff)
for i := 0; i < rounds; i++ {
idx := i
if !encrypt {
idx = rounds - 1 - i
}
round := func(h uint16) uint16 {
f := uint32(h)*0x9E3779B1 + k[idx%4]
return uint16((f ^ (f >> 16)) & 0xffff)
}
if encrypt {
nl := r
nr := l ^ round(r)
l, r = nl, nr
} else {
nl := r ^ round(l)
nr := l
l, r = nl, nr
}
}
return uint32(l)<<16 | uint32(r)
}
// numToBase62 把 32-bit 值序列化为定长(minLen)base62 串,高位在前。
func numToBase62(x uint32) string {
var sb strings.Builder
for x > 0 {
sb.WriteByte(hidAlphabet[x%hidBase])
x /= hidBase
}
if sb.Len() == 0 {
sb.WriteByte(hidAlphabet[0])
}
runes := []rune(sb.String())
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
out := string(runes)
if len(out) < hidMinLen {
out = strings.Repeat("0", hidMinLen-len(out)) + out
}
return out
}
// EncodeBrand 把数字品牌主键编码为对外 hashid 串(与后台 hashid.Encode 同算法、同盐)。
func EncodeBrand(id uint32) string {
return numToBase62(feistel(id, true))
}