复制项目
This commit is contained in:
422
pkg/common/db/model/chat/sensitive_word.go
Normal file
422
pkg/common/db/model/chat/sensitive_word.go
Normal file
@@ -0,0 +1,422 @@
|
||||
// Copyright © 2023 OpenIM open source community. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/db/table/chat"
|
||||
"github.com/openimsdk/tools/db/mongoutil"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func NewSensitiveWord(db *mongo.Database) (chat.SensitiveWordInterface, error) {
|
||||
coll := db.Collection("sensitive_words")
|
||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "word", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{
|
||||
{Key: "status", Value: 1},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// 创建配置集合
|
||||
configColl := db.Collection("sensitive_word_configs")
|
||||
_, err = configColl.Indexes().CreateOne(context.Background(), mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "enable_filter", Value: 1},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// 检查并初始化默认配置
|
||||
ctx := context.Background()
|
||||
count, err := configColl.CountDocuments(ctx, bson.M{})
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
// 创建默认配置
|
||||
defaultConfig := &chat.SensitiveWordConfig{
|
||||
ID: "default",
|
||||
EnableFilter: true,
|
||||
FilterMode: 1,
|
||||
ReplaceChar: "***",
|
||||
WhitelistUsers: []string{},
|
||||
WhitelistGroups: []string{},
|
||||
LogEnabled: true,
|
||||
AutoApprove: false,
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
_, err = configColl.InsertOne(ctx, defaultConfig)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return &SensitiveWord{coll: coll, configColl: configColl}, nil
|
||||
}
|
||||
|
||||
type SensitiveWord struct {
|
||||
coll *mongo.Collection
|
||||
configColl *mongo.Collection
|
||||
}
|
||||
|
||||
// ==================== 敏感词管理 ====================
|
||||
|
||||
func (o *SensitiveWord) CreateSensitiveWord(ctx context.Context, word *chat.SensitiveWord) error {
|
||||
word.CreateTime = time.Now()
|
||||
word.UpdateTime = time.Now()
|
||||
return mongoutil.InsertMany(ctx, o.coll, []*chat.SensitiveWord{word})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) UpdateSensitiveWord(ctx context.Context, id string, data map[string]any) error {
|
||||
data["update_time"] = time.Now()
|
||||
return mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": id}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) DeleteSensitiveWord(ctx context.Context, ids []string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWord(ctx context.Context, id string) (*chat.SensitiveWord, error) {
|
||||
return mongoutil.FindOne[*chat.SensitiveWord](ctx, o.coll, bson.M{"_id": id})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) SearchSensitiveWords(ctx context.Context, keyword string, action int32, status int32, pagination pagination.Pagination) (int64, []*chat.SensitiveWord, error) {
|
||||
filter := bson.M{}
|
||||
if keyword != "" {
|
||||
filter["word"] = bson.M{"$regex": keyword, "$options": "i"}
|
||||
}
|
||||
if action > 0 {
|
||||
filter["action"] = action
|
||||
}
|
||||
if status > 0 {
|
||||
filter["status"] = status
|
||||
}
|
||||
|
||||
return mongoutil.FindPage[*chat.SensitiveWord](ctx, o.coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetAllSensitiveWords(ctx context.Context) ([]*chat.SensitiveWord, error) {
|
||||
return mongoutil.Find[*chat.SensitiveWord](ctx, o.coll, bson.M{})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetEnabledSensitiveWords(ctx context.Context) ([]*chat.SensitiveWord, error) {
|
||||
return mongoutil.Find[*chat.SensitiveWord](ctx, o.coll, bson.M{"status": chat.SensitiveStatusEnabled})
|
||||
}
|
||||
|
||||
// ==================== 敏感词检测和过滤 ====================
|
||||
|
||||
func (o *SensitiveWord) CheckSensitiveWords(ctx context.Context, content string) ([]*chat.SensitiveWord, error) {
|
||||
words, err := o.GetEnabledSensitiveWords(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var matchedWords []*chat.SensitiveWord
|
||||
contentLower := strings.ToLower(content)
|
||||
|
||||
for _, word := range words {
|
||||
if strings.Contains(contentLower, strings.ToLower(word.Word)) {
|
||||
matchedWords = append(matchedWords, word)
|
||||
}
|
||||
}
|
||||
|
||||
return matchedWords, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) FilterContent(ctx context.Context, content string) (string, []*chat.SensitiveWord, error) {
|
||||
words, err := o.CheckSensitiveWords(ctx, content)
|
||||
if err != nil {
|
||||
return content, nil, err
|
||||
}
|
||||
|
||||
if len(words) == 0 {
|
||||
return content, words, nil
|
||||
}
|
||||
|
||||
filteredContent := content
|
||||
for _, word := range words {
|
||||
replaceChar := "***"
|
||||
filteredContent = strings.ReplaceAll(filteredContent, word.Word, replaceChar)
|
||||
}
|
||||
|
||||
return filteredContent, words, nil
|
||||
}
|
||||
|
||||
// ==================== 敏感词日志管理 ====================
|
||||
|
||||
func (o *SensitiveWord) CreateSensitiveWordLog(ctx context.Context, log *chat.SensitiveWordLog) error {
|
||||
log.CreateTime = time.Now()
|
||||
return mongoutil.InsertMany(ctx, o.coll.Database().Collection("sensitive_word_logs"), []*chat.SensitiveWordLog{log})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordLogs(ctx context.Context, userID, groupID string, pagination pagination.Pagination) (int64, []*chat.SensitiveWordLog, error) {
|
||||
filter := bson.M{}
|
||||
if userID != "" {
|
||||
filter["user_id"] = userID
|
||||
}
|
||||
if groupID != "" {
|
||||
filter["group_id"] = groupID
|
||||
}
|
||||
|
||||
coll := o.coll.Database().Collection("sensitive_word_logs")
|
||||
return mongoutil.FindPage[*chat.SensitiveWordLog](ctx, coll, filter, pagination)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) DeleteSensitiveWordLogs(ctx context.Context, ids []string) error {
|
||||
coll := o.coll.Database().Collection("sensitive_word_logs")
|
||||
return mongoutil.DeleteMany(ctx, coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
// ==================== 敏感词分组管理 ====================
|
||||
|
||||
func (o *SensitiveWord) CreateSensitiveWordGroup(ctx context.Context, group *chat.SensitiveWordGroup) error {
|
||||
group.CreateTime = time.Now()
|
||||
group.UpdateTime = time.Now()
|
||||
return mongoutil.InsertMany(ctx, o.coll.Database().Collection("sensitive_word_groups"), []*chat.SensitiveWordGroup{group})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) UpdateSensitiveWordGroup(ctx context.Context, id string, data map[string]any) error {
|
||||
data["update_time"] = time.Now()
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.UpdateOne(ctx, coll, bson.M{"_id": id}, bson.M{"$set": data}, false)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) DeleteSensitiveWordGroup(ctx context.Context, ids []string) error {
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.DeleteMany(ctx, coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordGroup(ctx context.Context, id string) (*chat.SensitiveWordGroup, error) {
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.FindOne[*chat.SensitiveWordGroup](ctx, coll, bson.M{"_id": id})
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetAllSensitiveWordGroups(ctx context.Context) ([]*chat.SensitiveWordGroup, error) {
|
||||
coll := o.coll.Database().Collection("sensitive_word_groups")
|
||||
return mongoutil.Find[*chat.SensitiveWordGroup](ctx, coll, bson.M{})
|
||||
}
|
||||
|
||||
// ==================== 敏感词配置管理 ====================
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordConfig(ctx context.Context) (*chat.SensitiveWordConfig, error) {
|
||||
// 查找配置(默认配置已在初始化时创建)
|
||||
config, err := mongoutil.FindOne[*chat.SensitiveWordConfig](ctx, o.configColl, bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) UpdateSensitiveWordConfig(ctx context.Context, config *chat.SensitiveWordConfig) error {
|
||||
config.UpdateTime = time.Now()
|
||||
if config.ID == "" {
|
||||
config.ID = "default"
|
||||
}
|
||||
filter := bson.M{"_id": config.ID}
|
||||
fmt.Println("UpdateSensitiveWordConfig", "_________55", config, o.configColl.Name())
|
||||
err := mongoutil.UpdateOne(ctx, o.configColl, filter, bson.M{"$set": config}, true)
|
||||
fmt.Println("UpdateSensitiveWordConfig", "_________44", config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) IsFilterEnabled(ctx context.Context) (bool, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return config.EnableFilter, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetFilterMode(ctx context.Context) (int32, error) {
|
||||
// 简化实现,返回默认值
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetReplaceChar(ctx context.Context) (string, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return "***", err
|
||||
}
|
||||
if config.ReplaceChar == "" {
|
||||
return "***", nil
|
||||
}
|
||||
return config.ReplaceChar, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) IsUserInWhitelist(ctx context.Context, userID string) (bool, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, id := range config.WhitelistUsers {
|
||||
if id == userID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) IsGroupInWhitelist(ctx context.Context, groupID string) (bool, error) {
|
||||
config, err := o.GetSensitiveWordConfig(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, id := range config.WhitelistGroups {
|
||||
if id == groupID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ==================== 批量操作 ====================
|
||||
|
||||
func (o *SensitiveWord) BatchCreateSensitiveWords(ctx context.Context, words []*chat.SensitiveWord) error {
|
||||
now := time.Now()
|
||||
for _, word := range words {
|
||||
word.CreateTime = now
|
||||
word.UpdateTime = now
|
||||
}
|
||||
return mongoutil.InsertMany(ctx, o.coll, words)
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) BatchUpdateSensitiveWords(ctx context.Context, updates map[string]map[string]any) error {
|
||||
for id, data := range updates {
|
||||
data["update_time"] = time.Now()
|
||||
err := mongoutil.UpdateOne(ctx, o.coll, bson.M{"_id": id}, bson.M{"$set": data}, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) BatchDeleteSensitiveWords(ctx context.Context, ids []string) error {
|
||||
return mongoutil.DeleteMany(ctx, o.coll, bson.M{"_id": bson.M{"$in": ids}})
|
||||
}
|
||||
|
||||
// ==================== 统计信息 ====================
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordStats(ctx context.Context) (map[string]int64, error) {
|
||||
stats := make(map[string]int64)
|
||||
|
||||
// 总数
|
||||
total, err := mongoutil.Count(ctx, o.coll, bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["total"] = total
|
||||
|
||||
// 启用数量
|
||||
enabled, err := mongoutil.Count(ctx, o.coll, bson.M{"status": chat.SensitiveStatusEnabled})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["enabled"] = enabled
|
||||
|
||||
// 禁用数量
|
||||
disabled, err := mongoutil.Count(ctx, o.coll, bson.M{"status": chat.SensitiveStatusDisabled})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["disabled"] = disabled
|
||||
|
||||
// 替换模式数量
|
||||
replace, err := mongoutil.Count(ctx, o.coll, bson.M{"action": chat.SensitiveActionReplace})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["replace"] = replace
|
||||
|
||||
// 拦截模式数量
|
||||
block, err := mongoutil.Count(ctx, o.coll, bson.M{"action": chat.SensitiveActionBlock})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["block"] = block
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (o *SensitiveWord) GetSensitiveWordLogStats(ctx context.Context, startTime, endTime time.Time) (map[string]int64, error) {
|
||||
stats := make(map[string]int64)
|
||||
coll := o.coll.Database().Collection("sensitive_word_logs")
|
||||
|
||||
filter := bson.M{
|
||||
"create_time": bson.M{
|
||||
"$gte": startTime,
|
||||
"$lte": endTime,
|
||||
},
|
||||
}
|
||||
|
||||
// 总数
|
||||
total, err := mongoutil.Count(ctx, coll, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["total"] = total
|
||||
|
||||
// 替换数量
|
||||
replaceFilter := bson.M{}
|
||||
for k, v := range filter {
|
||||
replaceFilter[k] = v
|
||||
}
|
||||
replaceFilter["action"] = chat.SensitiveActionReplace
|
||||
replace, err := mongoutil.Count(ctx, coll, replaceFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["replace"] = replace
|
||||
|
||||
// 拦截数量
|
||||
blockFilter := bson.M{}
|
||||
for k, v := range filter {
|
||||
blockFilter[k] = v
|
||||
}
|
||||
blockFilter["action"] = chat.SensitiveActionBlock
|
||||
block, err := mongoutil.Count(ctx, coll, blockFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["block"] = block
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
Reference in New Issue
Block a user