hub.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package websocket
  2. import (
  3. "log"
  4. "sync"
  5. )
  6. // OnClientConnectCallback 客户端连接时的回调函数。
  7. // conversationID: 对话ID
  8. // isVisitor: 是否是访客
  9. // visitorCount: 该对话当前的访客连接数
  10. type OnClientConnectCallback func(conversationID uint, isVisitor bool, visitorCount int)
  11. // OnClientDisconnectCallback 客户端断开连接时的回调函数。
  12. // conversationID: 对话ID
  13. // isVisitor: 是否是访客
  14. // visitorCount: 该对话当前的访客连接数(断开后)
  15. type OnClientDisconnectCallback func(conversationID uint, isVisitor bool, visitorCount int)
  16. // Hub 管理所有 WebSocket 连接
  17. // 每个对话(conversation)可以有多个人连接(访客和客服)
  18. type Hub struct {
  19. // 每个对话ID对应的客户端连接列表
  20. // conversationID -> []*Client
  21. conversations map[uint]map[*Client]bool
  22. // 注册新客户端(当有人连接时)
  23. register chan *Client
  24. // 注销客户端(当有人断开连接时)
  25. unregister chan *Client
  26. // 广播消息(当有新消息时,推送给所有相关的客户端)
  27. broadcast chan *Message
  28. // 互斥锁(保护并发访问)
  29. mu sync.RWMutex
  30. // 回调函数
  31. onConnect OnClientConnectCallback
  32. onDisconnect OnClientDisconnectCallback
  33. }
  34. // Message 是要广播的消息
  35. type Message struct {
  36. ConversationID uint `json:"conversation_id"`
  37. Data interface{} `json:"data"` // 消息内容(可以是 Message 对象)
  38. Type string `json:"type"` // 消息类型:new_message, conversation_update 等
  39. }
  40. // NewHub 创建一个新的 Hub
  41. func NewHub(onConnect OnClientConnectCallback, onDisconnect OnClientDisconnectCallback) *Hub {
  42. return &Hub{
  43. conversations: make(map[uint]map[*Client]bool),
  44. register: make(chan *Client),
  45. unregister: make(chan *Client),
  46. broadcast: make(chan *Message, 256),
  47. onConnect: onConnect,
  48. onDisconnect: onDisconnect,
  49. }
  50. }
  51. // Run 启动 Hub,处理所有事件
  52. func (h *Hub) Run() {
  53. for {
  54. select {
  55. // 新客户端连接
  56. case client := <-h.register:
  57. h.mu.Lock()
  58. // 如果这个对话还没有客户端,创建一个新的 map
  59. if h.conversations[client.conversationID] == nil {
  60. h.conversations[client.conversationID] = make(map[*Client]bool)
  61. }
  62. // 把这个客户端加入到对话中
  63. h.conversations[client.conversationID][client] = true
  64. // 统计该对话的访客连接数
  65. visitorCount := 0
  66. for c := range h.conversations[client.conversationID] {
  67. if c.isVisitor {
  68. visitorCount++
  69. }
  70. }
  71. h.mu.Unlock()
  72. // 调用连接回调函数
  73. if h.onConnect != nil {
  74. h.onConnect(client.conversationID, client.isVisitor, visitorCount)
  75. }
  76. // 客户端断开连接
  77. case client := <-h.unregister:
  78. h.mu.Lock()
  79. // 从对话中移除这个客户端
  80. wasVisitor := client.isVisitor
  81. if clients, ok := h.conversations[client.conversationID]; ok {
  82. if _, ok := clients[client]; ok {
  83. delete(clients, client)
  84. // 关闭发送通道(避免重复关闭导致 panic)
  85. select {
  86. case _, ok := <-client.send:
  87. if !ok {
  88. // 通道已经关闭,不需要再次关闭
  89. }
  90. default:
  91. // 通道未关闭,关闭它
  92. close(client.send)
  93. }
  94. // 统计该对话的访客连接数(断开后)
  95. visitorCount := 0
  96. for c := range clients {
  97. if c.isVisitor {
  98. visitorCount++
  99. }
  100. }
  101. // 如果这个对话没有客户端了,删除对话
  102. if len(clients) == 0 {
  103. delete(h.conversations, client.conversationID)
  104. }
  105. h.mu.Unlock()
  106. // 调用断开回调函数
  107. if h.onDisconnect != nil {
  108. h.onDisconnect(client.conversationID, wasVisitor, visitorCount)
  109. }
  110. } else {
  111. h.mu.Unlock()
  112. log.Printf("⚠️ 客户端断开时未找到: 对话ID=%d", client.conversationID)
  113. }
  114. } else {
  115. h.mu.Unlock()
  116. log.Printf("⚠️ 客户端断开时对话不存在: 对话ID=%d", client.conversationID)
  117. }
  118. // 广播消息
  119. case message := <-h.broadcast:
  120. h.mu.RLock()
  121. // 找到这个对话的所有客户端
  122. clients, ok := h.conversations[message.ConversationID]
  123. if !ok {
  124. h.mu.RUnlock()
  125. log.Printf("⚠️ 广播消息失败: 对话ID=%d 没有客户端连接", message.ConversationID)
  126. continue
  127. }
  128. // 创建一个客户端列表的副本(避免在遍历时修改)
  129. clientList := make([]*Client, 0, len(clients))
  130. for client := range clients {
  131. clientList = append(clientList, client)
  132. }
  133. h.mu.RUnlock()
  134. // 给所有客户端发送消息
  135. for _, client := range clientList {
  136. select {
  137. case client.send <- message:
  138. default:
  139. // 如果发送失败(客户端可能已经断开),关闭连接
  140. log.Printf("⚠️ 发送消息失败: 对话ID=%d, 客户端断开", client.conversationID)
  141. close(client.send)
  142. h.mu.Lock()
  143. delete(h.conversations[client.conversationID], client)
  144. h.mu.Unlock()
  145. }
  146. }
  147. }
  148. }
  149. }
  150. // BroadcastMessage 广播消息到指定对话的所有客户端
  151. func (h *Hub) BroadcastMessage(conversationID uint, messageType string, data interface{}) {
  152. h.broadcast <- &Message{
  153. ConversationID: conversationID,
  154. Type: messageType,
  155. Data: data,
  156. }
  157. }
  158. // BroadcastToAllAgents 广播消息到所有客服客户端(不管连接到哪个对话)
  159. // 用于 visitor_status_update 等需要所有客服都收到的事件
  160. func (h *Hub) BroadcastToAllAgents(messageType string, data interface{}) {
  161. h.mu.RLock()
  162. // 收集所有客服客户端(isVisitor == false)
  163. allAgents := make([]*Client, 0)
  164. for _, clients := range h.conversations {
  165. for client := range clients {
  166. if !client.isVisitor {
  167. allAgents = append(allAgents, client)
  168. }
  169. }
  170. }
  171. h.mu.RUnlock()
  172. // 为每个客服客户端创建消息并发送
  173. for _, client := range allAgents {
  174. message := &Message{
  175. ConversationID: client.conversationID, // 使用客户端连接的对话ID
  176. Type: messageType,
  177. Data: data,
  178. }
  179. select {
  180. case client.send <- message:
  181. default:
  182. // 如果发送失败(客户端可能已经断开),关闭连接
  183. log.Printf("⚠️ 发送消息到客服失败: 对话ID=%d, 客户端断开", client.conversationID)
  184. close(client.send)
  185. h.mu.Lock()
  186. if clients, ok := h.conversations[client.conversationID]; ok {
  187. delete(clients, client)
  188. if len(clients) == 0 {
  189. delete(h.conversations, client.conversationID)
  190. }
  191. }
  192. h.mu.Unlock()
  193. }
  194. }
  195. }