package utils import ( "encoding/json" "math/rand/v2" "strings" "videoweb/pub/common/netutil" "github.com/gosimple/slug" ) // BinarySearch BinarySearch func BinarySearch(s []string, t string, n int) (idx int) { idx = -1 if n == 0 { return } start, end := 0, n-1 for start <= end { mid := (start + end) >> 1 if s[mid] < t { start = mid + 1 } else if s[mid] > t { end = mid - 1 } else { idx = mid break } } return } var YesOrNo = map[bool]string{ true: "是", false: "否", } // ApplyImgCdn 尝试解析节点 Content JSON,若含 "img" 字段则对其拼接 CDN 后重新序列化。 // 不符合 {"img": "..."} 结构的 Content 原样返回。 func ApplyImgCdn(cdn, content string) string { if content == "" || cdn == "" { return content } var m map[string]interface{} if err := json.Unmarshal([]byte(content), &m); err != nil { return content // 非 JSON 对象,原样返回 } imgVal, ok := m["img"] if !ok { return content // 无 img 字段,原样返回 } imgStr, ok := imgVal.(string) if !ok || imgStr == "" { return content } m["img"] = netutil.MergeURL(cdn, imgStr) b, err := json.Marshal(m) if err != nil { return content } return string(b) } // 截取指定长度的字符串 func GetPrefix(s string, n int) string { runes := []rune(s) if len(runes) <= n { return s } return string(runes[:n]) } func GetRandomLetters(n int) string { const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, n) for i := range b { b[i] = letters[rand.IntN(len(letters))] } return string(b) } func GetSlugInfo(text string) string { return SanitizeSlug(slug.Make(text)) } // SanitizeSlug 去掉 slug 中的横杠等特殊符号,只保留字母和数字 func SanitizeSlug(s string) string { s = strings.TrimSpace(s) if s == "" { return "" } var b strings.Builder b.Grow(len(s)) for _, r := range s { if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' { b.WriteRune(r) } } return b.String() } // IsSlugAllDigits 判断 slug 是否全为数字 func IsSlugAllDigits(s string) bool { s = strings.TrimSpace(s) if s == "" { return false } for _, r := range s { if r < '0' || r > '9' { return false } } return true } // RandomSlugLength 随机 slug 长度 const RandomSlugLength = 8 // MakeRandomSlug 生成唯一的随机 slug(8 位小写字母);exists 返回 true 表示已被占用。 func MakeRandomSlug(exists func(string) bool) string { for { s := strings.ToLower(GetRandomLetters(RandomSlugLength)) if !exists(s) { return s } } } // GetSlugAbbr 取标题的拼音首字母缩写(中文每字取首字母,英文每词取首字母),全小写。 func GetSlugAbbr(text string) string { s := strings.ToLower(slug.Make(text)) var b strings.Builder newWord := true for _, r := range s { if r == '-' || r == '_' { newWord = true continue } if newWord && ((r >= 'a' && r <= 'z') || (r >= '0' && r <= '9')) { b.WriteRune(r) newWord = false } } return b.String() } // AbbrSlugMaxLen slug 最大长度(含碰撞时追加的随机后缀)。 const AbbrSlugMaxLen = 20 // MakeAbbrSlug 优先按标题拼音首字母缩写生成 slug;缩写为空或全数字时回退 5 位随机字母,碰撞时追加 3 位随机字母,整体长度不超过 AbbrSlugMaxLen。 func MakeAbbrSlug(source string, exists func(string) bool) string { abbr := GetSlugAbbr(source) if abbr == "" || IsSlugAllDigits(abbr) { abbr = strings.ToLower(GetRandomLetters(5)) } const suffixLen = 3 baseMax := AbbrSlugMaxLen - suffixLen if len(abbr) > baseMax { abbr = abbr[:baseMax] } final := abbr for exists(final) { final = abbr + strings.ToLower(GetRandomLetters(suffixLen)) } return final } // MakeUniqueSlug 生成唯一 slug;exists 返回 true 表示已被占用。生成结果不能全是数字。 func MakeUniqueSlug(source string, exists func(string) bool) string { pinyin := GetSlugInfo(source) if pinyin == "" { pinyin = GetRandomLetters(5) } else if IsSlugAllDigits(pinyin) { pinyin = pinyin + GetRandomLetters(3) } finalSlug := pinyin for exists(finalSlug) { finalSlug = pinyin + GetRandomLetters(3) } return finalSlug }