后端: Go(Gin+GORM+MySQL+Redis) - 17张业务表(yz_pw_前缀), 自动迁移 - JWT认证(3小时) + 盐+MD5密码 + 图形验证码 - 班级CRUD/加入(8人姓名验证/邀请码)/审核/30天自动清理 - 系统配置(16项)/菜单管理(动态路由)/数据统计 - 文件MD5去重/数据隔离/账号封禁/敏感词DFA检测 前端: Vue3+Vite+Element Plus+Less+ECharts+FontAwesome - 动态路由(数据库菜单驱动) - 蓝白配色, H5响应式 - 登录/注册/忘记密码/个人中心 - 班级创建向导/详情/加入/列表 - 管理后台: 审核/配置/菜单/统计/用户/敏感词 数据库: MySQL 10.31.100.3:3306/photowall 管理员: hero920103 / 920103
126 lines
2.4 KiB
Go
126 lines
2.4 KiB
Go
package sensitive
|
|
|
|
import (
|
|
"photowall/internal/model"
|
|
"strings"
|
|
"sync"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// DFA 敏感词过滤器
|
|
type Filter struct {
|
|
root *trieNode
|
|
mu sync.RWMutex
|
|
ready bool
|
|
}
|
|
|
|
type trieNode struct {
|
|
children map[rune]*trieNode
|
|
isEnd bool
|
|
word string
|
|
}
|
|
|
|
var globalFilter = &Filter{root: newNode()}
|
|
|
|
func newNode() *trieNode {
|
|
return &trieNode{children: make(map[rune]*trieNode)}
|
|
}
|
|
|
|
// Init 从数据库加载敏感词
|
|
func Init(db *gorm.DB) {
|
|
var words []model.SensitiveWord
|
|
db.Where("enabled = ?", true).Find(&words)
|
|
globalFilter.mu.Lock()
|
|
defer globalFilter.mu.Unlock()
|
|
globalFilter.root = newNode()
|
|
for _, w := range words {
|
|
globalFilter.addWord(w.Word)
|
|
}
|
|
globalFilter.ready = true
|
|
}
|
|
|
|
// Reload 重新加载敏感词
|
|
func Reload(db *gorm.DB) {
|
|
Init(db)
|
|
}
|
|
|
|
func (f *Filter) addWord(word string) {
|
|
node := f.root
|
|
for _, ch := range []rune(word) {
|
|
if _, ok := node.children[ch]; !ok {
|
|
node.children[ch] = newNode()
|
|
}
|
|
node = node.children[ch]
|
|
}
|
|
node.isEnd = true
|
|
node.word = word
|
|
}
|
|
|
|
// Check 检测文本中是否包含敏感词,返回第一个匹配的词
|
|
func Check(text string) (bool, string) {
|
|
if !globalFilter.ready || text == "" {
|
|
return false, ""
|
|
}
|
|
globalFilter.mu.RLock()
|
|
defer globalFilter.mu.RUnlock()
|
|
runes := []rune(text)
|
|
for i := 0; i < len(runes); i++ {
|
|
node := globalFilter.root
|
|
for j := i; j < len(runes); j++ {
|
|
next, ok := node.children[runes[j]]
|
|
if !ok {
|
|
break
|
|
}
|
|
node = next
|
|
if node.isEnd {
|
|
return true, node.word
|
|
}
|
|
}
|
|
}
|
|
return false, ""
|
|
}
|
|
|
|
// Replace 替换文本中的敏感词为 ***
|
|
func Replace(text string) string {
|
|
if !globalFilter.ready || text == "" {
|
|
return text
|
|
}
|
|
globalFilter.mu.RLock()
|
|
defer globalFilter.mu.RUnlock()
|
|
runes := []rune(text)
|
|
var result strings.Builder
|
|
i := 0
|
|
for i < len(runes) {
|
|
node := globalFilter.root
|
|
matchLen := 0
|
|
for j := i; j < len(runes); j++ {
|
|
next, ok := node.children[runes[j]]
|
|
if !ok {
|
|
break
|
|
}
|
|
node = next
|
|
if node.isEnd {
|
|
matchLen = j - i + 1
|
|
}
|
|
}
|
|
if matchLen > 0 {
|
|
result.WriteString("***")
|
|
i += matchLen
|
|
} else {
|
|
result.WriteRune(runes[i])
|
|
i++
|
|
}
|
|
}
|
|
return result.String()
|
|
}
|
|
|
|
// CheckAndReplace 检测并过滤,返回是否包含敏感词和过滤后的文本
|
|
func CheckAndReplace(text string) (bool, string) {
|
|
found, _ := Check(text)
|
|
if !found {
|
|
return false, text
|
|
}
|
|
return true, Replace(text)
|
|
}
|