update
This commit is contained in:
81
internal/ingest/hashid.go
Normal file
81
internal/ingest/hashid.go
Normal file
@ -0,0 +1,81 @@
|
||||
package ingest
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 以下 Feistel + base62 编码与后台 internal/pkg/hashid 完全一致,
|
||||
// 仅实现编码侧(爬虫只需把品牌数字主键编码为 brand_uid 上送,无需解码)。
|
||||
// 盐值必须 == 后台 HASHID_SECRET,否则生成的 brand_uid 后台解码不出正确主键。
|
||||
|
||||
const (
|
||||
hidAlphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
hidBase = 62
|
||||
hidMinLen = 8
|
||||
)
|
||||
|
||||
var hidKeys [4]uint32
|
||||
|
||||
// InitHashID 用与后台 HASHID_SECRET 相同的盐值初始化编码密钥。
|
||||
// secret 为空时回落后台内置默认盐(仅防顺序枚举,不安全)。
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
// hidFeistel 32-bit 平衡 Feistel 网络,encrypt=true 加密。
|
||||
func hidFeistel(v uint32, encrypt bool) 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 + hidKeys[idx%4]
|
||||
return uint16((f ^ (f >> 16)) & 0xffff)
|
||||
}
|
||||
if encrypt {
|
||||
nl, nr := r, uint16(l)^round(r)
|
||||
l, r = nl, nr
|
||||
} else {
|
||||
nl, nr := uint16(l)^round(l), l
|
||||
l, r = nl, nr
|
||||
}
|
||||
}
|
||||
return uint32(l)<<16 | uint32(r)
|
||||
}
|
||||
|
||||
func encodeNum(n uint32) string {
|
||||
x := hidFeistel(n, true)
|
||||
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 串(与后台 brand_uid 同算法)。
|
||||
func EncodeBrand(id uint32) string {
|
||||
return encodeNum(id)
|
||||
}
|
||||
Reference in New Issue
Block a user