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) }