Files
spider/internal/ingest/hashid.go
toom1996 fde57984e7 update
2026-09-20 00:44:47 +08:00

82 lines
2.0 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.

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)
}