Files
hero920103 57d9866dab feat: PhotoWall 毕业照存储系统初始版本
后端: 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
2026-09-03 05:55:23 +08:00

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