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

存储用户上次发言内容

last_msg:{userID}

发言内容(文本)

List

存储用户最近 N 条发言(用于雷同率计算)

msg_history:{userID}

发言内容列表

Sorted Set (ZSET)

存储用户私聊对象及时间戳(用于私聊频率检测)

private_chats:{userID}

{targetUserID}:timestamp

String

存储用户禁言状态及解禁时间

mute:{userID}

禁言截止时间戳


(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 String(上次发言)、List(发言历史)、ZSET(私聊对象)

计算雷同率

简单字符匹配(可优化为 LevenshteinJaccard

检查违规规则

5分钟/1分钟/3分钟/5分钟窗口 + 雷同率/私聊人数检测

禁言机制

Redis String 存储禁言截止时间,下次发言时检查

优化建议

  1. 更精确的时间记录:在 msg_history 中存储 时间戳+内容,而不是仅内容。
  2. 更智能的雷同率计算:使用 Levenshtein 距离TF-IDF + 余弦相似度
  3. 分布式锁:防止并发问题(如 Redlock)。
  4. 异步处理:将违规检测放到 消息队列(如 Kafka/RabbitMQ) 异步处理。

这样,你就可以 用 Redis 高效实现发言监控和自动禁言! 🚀