Files
2026-06-25 16:47:54 +08:00

133 lines
2.8 KiB
Go

package ws
import (
"encoding/json"
"sync"
)
// OnlineUser 在线成员信息
type OnlineUser struct {
UserID int64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
}
// Room 按 family_id 分组的聊天室
type Room struct {
clients map[int64]*Client // user_id → Client
mu sync.RWMutex
}
// Hub 管理所有 Room
type Hub struct {
rooms map[int64]*Room // family_id → Room
mu sync.RWMutex
}
var defaultHub = &Hub{
rooms: make(map[int64]*Room),
}
func GetHub() *Hub {
return defaultHub
}
// getOrCreateRoom 获取或创建 Room
func (h *Hub) getOrCreateRoom(familyID int64) *Room {
h.mu.Lock()
defer h.mu.Unlock()
room, ok := h.rooms[familyID]
if !ok {
room = &Room{
clients: make(map[int64]*Client),
}
h.rooms[familyID] = room
}
return room
}
// Join 用户加入房间,广播 online 事件
func (h *Hub) Join(familyID int64, client *Client) {
room := h.getOrCreateRoom(familyID)
room.mu.Lock()
room.clients[client.userID] = client
onlineUsers := h.collectOnlineUsers(room)
room.mu.Unlock()
h.broadcastOnlineUsers(familyID, onlineUsers)
}
// Leave 用户离开房间,广播 offline 事件
func (h *Hub) Leave(familyID int64, userID int64) {
room := h.getOrCreateRoom(familyID)
room.mu.Lock()
delete(room.clients, userID)
onlineUsers := h.collectOnlineUsers(room)
room.mu.Unlock()
// 广播 offline 给房间内其他人
msg, _ := json.Marshal(map[string]any{
"type": "offline",
"user_id": userID,
})
h.broadcastRaw(familyID, msg)
// 广播更新后的在线列表
h.broadcastOnlineUsers(familyID, onlineUsers)
}
// Broadcast 向房间内所有连接广播消息
func (h *Hub) Broadcast(familyID int64, msg any) {
data, err := json.Marshal(msg)
if err != nil {
return
}
h.broadcastRaw(familyID, data)
}
// broadcastRaw 向房间内所有连接广播原始字节
func (h *Hub) broadcastRaw(familyID int64, data []byte) {
h.mu.RLock()
room, ok := h.rooms[familyID]
h.mu.RUnlock()
if !ok {
return
}
room.mu.RLock()
defer room.mu.RUnlock()
for _, client := range room.clients {
select {
case client.send <- data:
default:
// 发送通道满则跳过
}
}
}
// collectOnlineUsers 收集在线用户列表(调用方需持有 room.mu 锁)
func (h *Hub) collectOnlineUsers(room *Room) []OnlineUser {
users := make([]OnlineUser, 0, len(room.clients))
for _, client := range room.clients {
users = append(users, OnlineUser{
UserID: client.userID,
Nickname: client.nickname,
Avatar: client.avatar,
})
}
return users
}
// broadcastOnlineUsers 广播在线用户列表
func (h *Hub) broadcastOnlineUsers(familyID int64, users []OnlineUser) {
msg, _ := json.Marshal(map[string]any{
"type": "online",
"users": users,
})
h.broadcastRaw(familyID, msg)
}