update
This commit is contained in:
@ -124,3 +124,29 @@ func Decode(s string) (uint32, error) {
|
||||
}
|
||||
return decodeStr(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
// 类型前缀:让同一数字主键在不同实体下得到不同的编码串,前端 /item/[id] 可凭前缀区分实体类型,
|
||||
// 从而把 runway / street snap 等详情统一收口到同一路由而无需担心 id 撞车。
|
||||
const (
|
||||
TypeRunway = 'r' // 走秀 / 文章 lookbook(brand_runway 表)
|
||||
TypeSnap = 's' // 街拍(street_snap 表)
|
||||
)
|
||||
|
||||
// EncodeTyped 在编码串前拼一个类型字符前缀(如 "r"+base62 串)。前缀字符取自 base62 字母表,
|
||||
// 不参与 Feistel 还原、仅作类型标识。DecodeTyped 负责剥离前缀并还原数字主键。
|
||||
func EncodeTyped(t byte, id uint32) string {
|
||||
ensure()
|
||||
return string(t) + encodeNum(id)
|
||||
}
|
||||
|
||||
// DecodeTyped 还原带类型前缀的编码串,返回 (类型字符, 数字主键, error)。
|
||||
// 调用方通常忽略类型(路由已确定实体),只取数字主键查库;旧的无前缀串仍用 Decode。
|
||||
func DecodeTyped(s string) (byte, uint32, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if len(s) < 2 {
|
||||
return 0, 0, errors.New("invalid typed hashid")
|
||||
}
|
||||
t := s[0]
|
||||
n, err := decodeStr(s[1:])
|
||||
return t, n, err
|
||||
}
|
||||
|
||||
@ -40,3 +40,36 @@ func TestInvalid(t *testing.T) {
|
||||
t.Fatal("含非法字符应返回错误")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTyped 验证带类型前缀的编解码:同数字不同前缀得到不同串,且能正确还原类型与数字主键。
|
||||
func TestTyped(t *testing.T) {
|
||||
Init("test-salt")
|
||||
|
||||
r := EncodeTyped(TypeRunway, 5)
|
||||
s := EncodeTyped(TypeSnap, 5)
|
||||
if r == s {
|
||||
t.Fatalf("同数字不同前缀应得到不同串: runway=%q snap=%q", r, s)
|
||||
}
|
||||
if r[0] != TypeRunway || s[0] != TypeSnap {
|
||||
t.Fatalf("前缀字符不正确: runway=%q snap=%q", r, s)
|
||||
}
|
||||
|
||||
tr, nr, err := DecodeTyped(r)
|
||||
if err != nil || tr != TypeRunway || nr != 5 {
|
||||
t.Fatalf("DecodeTyped(runway) 失败: type=%c num=%d err=%v", tr, nr, err)
|
||||
}
|
||||
ts, ns, err := DecodeTyped(s)
|
||||
if err != nil || ts != TypeSnap || ns != 5 {
|
||||
t.Fatalf("DecodeTyped(snap) 失败: type=%c num=%d err=%v", ts, ns, err)
|
||||
}
|
||||
|
||||
// 无前缀旧串仍可用 Decode 还原(兼容历史链接)
|
||||
old := Encode(5)
|
||||
if no, err := Decode(old); err != nil || no != 5 {
|
||||
t.Fatalf("旧无前缀串解码失败: num=%d err=%v", no, err)
|
||||
}
|
||||
// 长度不足 2 视为非法
|
||||
if _, _, err := DecodeTyped("r"); err == nil {
|
||||
t.Fatal("长度不足 2 的 typed 串应返回错误")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user