1. 核心需求分析
规则 | 检测条件 | 惩罚 |
规则1 | 5分钟内发布5条信息,雷同率 ≥90% | 无感禁言 12 小时 |
规则2 | 1分钟内发布3条信息,雷同率 ≥90% | 无感禁言 6 小时 |
规则3 | 3分钟内大量私聊人数 ≥5 人 | 无感禁言 24 小时 |
规则4 | 5分钟内大量私聊人数 ≥9 人 | 无感禁言 48 小时 |
2. 技术方案
(1) Redis 数据结构设计
数据类型 | 用途 | Key 设计 | Value 设计 |
String | 存储用户上次发言内容 |
| 发言内容(文本) |
List | 存储用户最近 N 条发言(用于雷同率计算) |
| 发言内容列表 |
Sorted Set (ZSET) | 存储用户私聊对象及时间戳(用于私聊频率检测) |
|
|
String | 存储用户禁言状态及解禁时间 |
|
|
(2) 实现步骤
① 存储用户发言内容
每次用户发言时,存储:
- 上次发言内容(
last_msg:{userID}
) - 最近 N 条发言(
msg_history:{userID}
,用于雷同率计算) - 私聊对象记录(
private_chats:{userID}
,用于私聊频率检测)
② 检查雷同率(规则1 & 规则2)
- 计算最近 N 条发言的雷同率(如使用
Levenshtein 距离
或Jaccard 相似度
)。 - 如果 5分钟内 ≥5 条且雷同率 ≥90% → 禁言 12 小时(规则1)。
- 如果 1分钟内 ≥3 条且雷同率 ≥90% → 禁言 6 小时(规则2)。
③ 检查私聊频率(规则3 & 规则4)
- 统计 3分钟内私聊人数 ≥5 → 禁言 24 小时(规则3)。
- 统计 5分钟内私聊人数 ≥9 → 禁言 48 小时(规则4)。
④ 执行禁言
- 设置
mute:{userID}
为 禁言截止时间戳(如time.Now().Add(12 * time.Hour).Unix()
)。 - 下次发言时检查
mute:{userID}
是否存在,若存在则拒绝发言。
3. Golang 代码实现
(1) 依赖
import ("context""fmt""github.com/redis/go-redis/v9""strings""time"
)
(2) Redis 客户端初始化
var rdb *redis.Clientfunc initRedis() {rdb = redis.NewClient(&redis.Options{Addr: "localhost:6379", // Redis 地址Password: "", // 密码DB: 0, // 数据库})
}
(3) 存储用户发言
func storeMessage(ctx context.Context, userID, content string, isPrivate bool, targetUserID string) error {// 1. 存储上次发言内容lastMsgKey := fmt.Sprintf("last_msg:%s", userID)err := rdb.Set(ctx, lastMsgKey, content, 0).Err()if err != nil {return err}// 2. 存储最近发言历史(用于雷同率计算)msgHistoryKey := fmt.Sprintf("msg_history:%s", userID)err = rdb.LPush(ctx, msgHistoryKey, content).Err()if err != nil {return err}// 只保留最近 10 条(避免内存过大)err = rdb.LTrim(ctx, msgHistoryKey, 0, 9).Err()if err != nil {return err}// 3. 如果是私聊,记录私聊对象if isPrivate {privateChatKey := fmt.Sprintf("private_chats:%s", userID)now := time.Now().Unix()err = rdb.ZAdd(ctx, privateChatKey, &redis.Z{Score: float64(now),Member: targetUserID,}).Err()if err != nil {return err}// 只保留最近 20 个私聊对象err = rdb.ZRemRangeByRank(ctx, privateChatKey, 0, -21).Err()if err != nil {return err}}// 4. 检查违规行为checkViolationRules(ctx, userID, isPrivate, targetUserID)return nil
}
(4) 计算雷同率(简化版,可用 Levenshtein 或 Jaccard)
func calculateSimilarity(msg1, msg2 string) float64 {// 简单示例:计算相同字符比例(实际可用更复杂的算法)same := 0minLen := min(len(msg1), len(msg2))for i := 0; i < minLen; i++ {if msg1[i] == msg2[i] {same++}}return float64(same) / float64(minLen)
}func checkMessageSimilarity(ctx context.Context, userID string) float64 {msgHistoryKey := fmt.Sprintf("msg_history:%s", userID)messages, err := rdb.LRange(ctx, msgHistoryKey, 0, -1).Result()if err != nil {return 0}if len(messages) < 2 {return 0}// 计算最近 5 条消息的平均相似度similarities := []float64{}for i := 1; i < len(messages); i++ {sim := calculateSimilarity(messages[i-1], messages[i])similarities = append(similarities, sim)}// 计算平均相似度var total float64for _, sim := range similarities {total += sim}return total / float64(len(similarities))
}
(5) 检查违规规则
func checkViolationRules(ctx context.Context, userID, isPrivate string, targetUserID string) {// 1. 检查雷同率规则(规则1 & 规则2)similarity := checkMessageSimilarity(ctx, userID)msgHistoryKey := fmt.Sprintf("msg_history:%s", userID)msgCount, err := rdb.LLen(ctx, msgHistoryKey).Result()if err != nil {return}now := time.Now()// 规则1:5分钟内 ≥5 条且雷同率 ≥90%if msgCount >= 5 && similarity >= 0.9 {fiveMinAgo := now.Add(-5 * time.Minute).Unix()firstMsgTime, err := rdb.LIndex(ctx, msgHistoryKey, 0).Result() // 简化:实际应记录时间戳if err == nil && firstMsgTime != "" { // 需要更精确的时间记录// 如果 5 分钟内 ≥5 条且雷同率 ≥90%,禁言 12 小时muteUntil := now.Add(12 * time.Hour).Unix()rdb.Set(ctx, fmt.Sprintf("mute:%s", userID), muteUntil, 0)}}// 规则2:1分钟内 ≥3 条且雷同率 ≥90%if msgCount >= 3 && similarity >= 0.9 {oneMinAgo := now.Add(-1 * time.Minute).Unix()// 类似规则1的逻辑muteUntil := now.Add(6 * time.Hour).Unix()rdb.Set(ctx, fmt.Sprintf("mute:%s", userID), muteUntil, 0)}// 2. 检查私聊频率规则(规则3 & 规则4)if isPrivate == "true" {privateChatKey := fmt.Sprintf("private_chats:%s", userID)// 3分钟内 ≥5 个不同私聊对象threeMinAgo := now.Add(-3 * time.Minute).Unix()count, err := rdb.ZCount(ctx, privateChatKey, float64(threeMinAgo), float64(now.Unix())).Result()if err == nil && count >= 5 {muteUntil := now.Add(24 * time.Hour).Unix()rdb.Set(ctx, fmt.Sprintf("mute:%s", userID), muteUntil, 0)}// 5分钟内 ≥9 个不同私聊对象fiveMinAgo := now.Add(-5 * time.Minute).Unix()count, err = rdb.ZCount(ctx, privateChatKey, float64(fiveMinAgo), float64(now.Unix())).Result()if err == nil && count >= 9 {muteUntil := now.Add(48 * time.Hour).Unix()rdb.Set(ctx, fmt.Sprintf("mute:%s", userID), muteUntil, 0)}}
}
(6) 检查用户是否被禁言
func isUserMuted(ctx context.Context, userID string) (bool, error) {muteKey := fmt.Sprintf("mute:%s", userID)muteUntil, err := rdb.Get(ctx, muteKey).Int64()if err == redis.Nil {return false, nil // 未禁言} else if err != nil {return false, err}now := time.Now().Unix()if now < muteUntil {return true, nil // 仍被禁言} else {rdb.Del(ctx, muteKey) // 禁言已过期,删除记录return false, nil}
}
4. 总结
功能 | 实现方式 |
存储发言内容 | Redis |
计算雷同率 | 简单字符匹配(可优化为 |
检查违规规则 | 5分钟/1分钟/3分钟/5分钟窗口 + 雷同率/私聊人数检测 |
禁言机制 | Redis |
优化建议
- 更精确的时间记录:在
msg_history
中存储 时间戳+内容,而不是仅内容。 - 更智能的雷同率计算:使用
Levenshtein 距离
或TF-IDF + 余弦相似度
。 - 分布式锁:防止并发问题(如
Redlock
)。 - 异步处理:将违规检测放到 消息队列(如 Kafka/RabbitMQ) 异步处理。
这样,你就可以 用 Redis 高效实现发言监控和自动禁言! 🚀