feat(live): 按用户名禁言、后台右侧管控栏、前台抖音式布局与禁言 Toast

Made-with: Cursor
This commit is contained in:
whm
2026-03-27 09:11:39 +08:00
parent fe8d5a34cc
commit 435fbfd47e
10 changed files with 419 additions and 221 deletions

View File

@@ -3,6 +3,7 @@ package weblive
import (
"fmt"
"sort"
"strings"
"sync"
"sync/atomic"
"time"
@@ -10,18 +11,20 @@ import (
var (
modMu sync.RWMutex
muteAll bool
mutedIP = make(map[string]bool)
ipWindow = make(map[string][]int64) // 每个 IP 最近窗口内的发送时间戳(ms)
muteAll bool
mutedIP = make(map[string]bool)
mutedUsers = make(map[string]bool) // key: normMuteUsername
ipWindow = make(map[string][]int64) // 每个 IP 最近窗口内的发送时间戳(ms)
onlineMap = make(map[string]*onlineSession)
seq uint64
)
type ModerationSnapshot struct {
MuteAll bool `json:"mute_all"`
MutedIPs []string `json:"muted_ips"`
OnlineIPs []IPOnlineItem `json:"online_ips"`
OnlineUsers []OnlineUserItem `json:"online_users"`
MuteAll bool `json:"mute_all"`
MutedIPs []string `json:"muted_ips"`
MutedUsernames []string `json:"muted_usernames"`
OnlineIPs []IPOnlineItem `json:"online_ips"`
OnlineUsers []OnlineUserItem `json:"online_users"`
RateLimit struct {
WindowMs int `json:"window_ms"`
MaxHits int `json:"max_hits"`
@@ -73,6 +76,25 @@ func SetIPMuted(ip string, enabled bool) {
modMu.Unlock()
}
func normMuteUsername(u string) string {
return strings.ToLower(strings.TrimSpace(u))
}
// SetUserMuted 按登录用户名禁言(弹幕/礼物);与 IP 禁言、全体禁言叠加。
func SetUserMuted(username string, enabled bool) {
key := normMuteUsername(username)
if key == "" {
return
}
modMu.Lock()
if enabled {
mutedUsers[key] = true
} else {
delete(mutedUsers, key)
}
modMu.Unlock()
}
const (
ipSendWindowMs = 3000
ipSendMaxHits = 10
@@ -93,6 +115,22 @@ func IsMutedForIP(ip string) bool {
return mutedIP[ip]
}
// IsMutedForSend 发弹幕/礼物前全体禁言、IP 禁言、或已登录用户名被禁。
func IsMutedForSend(ip, username string) bool {
modMu.RLock()
defer modMu.RUnlock()
if muteAll {
return true
}
if mutedIP[ip] {
return true
}
if k := normMuteUsername(username); k != "" && mutedUsers[k] {
return true
}
return false
}
func ModerationStateSnapshot() ModerationSnapshot {
modMu.RLock()
muteAllNow := muteAll
@@ -100,8 +138,13 @@ func ModerationStateSnapshot() ModerationSnapshot {
for ip := range mutedIP {
muted = append(muted, ip)
}
mutedNames := make([]string, 0, len(mutedUsers))
for u := range mutedUsers {
mutedNames = append(mutedNames, u)
}
modMu.RUnlock()
sort.Strings(muted)
sort.Strings(mutedNames)
counts := onlineIPCountsLocked()
online := make([]IPOnlineItem, 0, len(counts))
@@ -215,7 +258,7 @@ func onlineUsersLocked() []OnlineUserItem {
ConnectedAt: s.Connected.Format(time.RFC3339),
OnlineSec: int64(now.Sub(s.Connected).Seconds()),
IdleSec: int64(now.Sub(s.LastAt).Seconds()),
Muted: mutedIP[s.IP] || muteAll,
Muted: IsMutedForSend(s.IP, s.Username),
})
}
sort.Slice(out, func(i, j int) bool {