package contentreviewser
import (
"sort"
"strings"
"91porn-server/models/v/contentreviewmod"
)
const (
markPrefix = ""
markSuffix = ""
)
// allTerms 把快照展开为一组待匹配的词
func allTerms(snaps []contentreviewmod.SensitiveWordSnap) []string {
seen := make(map[string]struct{})
out := make([]string, 0, len(snaps))
for _, s := range snaps {
if s.Word == "" {
continue
}
if _, ok := seen[s.Word]; ok {
continue
}
seen[s.Word] = struct{}{}
out = append(out, s.Word)
}
// 长词优先,避免短词先替换破坏长词的匹配
sort.Slice(out, func(i, j int) bool {
return len(out[i]) > len(out[j])
})
return out
}
// findHits 返回 input 中命中的词(去重,保持长→短顺序)
func findHits(input string, terms []string) []string {
if input == "" || len(terms) == 0 {
return nil
}
hits := make([]string, 0)
seen := make(map[string]struct{})
for _, t := range terms {
if t == "" {
continue
}
if _, ok := seen[t]; ok {
continue
}
if strings.Contains(input, t) {
seen[t] = struct{}{}
hits = append(hits, t)
}
}
return hits
}
// highlight 用 ... 包裹命中的词
// hits 已按长度降序;为避免再次包裹已 mark 的内容,分段替换
func highlight(input string, hits []string) string {
if input == "" || len(hits) == 0 {
return input
}
result := input
for _, h := range hits {
if h == "" {
continue
}
result = replaceNotInMark(result, h)
}
return result
}
// replaceNotInMark 在 input 中把 term 全部替换为 term,
// 但跳过已经包在 ... 内的片段(避免嵌套)
func replaceNotInMark(input, term string) string {
if term == "" {
return input
}
var b strings.Builder
i := 0
for i < len(input) {
// 已经在 mark 块里,整段原样输出
if strings.HasPrefix(input[i:], markPrefix) {
end := strings.Index(input[i:], markSuffix)
if end < 0 {
b.WriteString(input[i:])
return b.String()
}
b.WriteString(input[i : i+end+len(markSuffix)])
i += end + len(markSuffix)
continue
}
if strings.HasPrefix(input[i:], term) {
b.WriteString(markPrefix)
b.WriteString(term)
b.WriteString(markSuffix)
i += len(term)
continue
}
b.WriteByte(input[i])
i++
}
return b.String()
}
// matchAndHighlight 在 input 上一步到位:返回命中词列表 + 高亮后的文本
func matchAndHighlight(input string, terms []string) ([]string, string) {
hits := findHits(input, terms)
if len(hits) == 0 {
return nil, input
}
return hits, highlight(input, hits)
}