diff --git a/static/templates/chat_page.html b/static/templates/chat_page.html index eabd08d..14c5899 100644 --- a/static/templates/chat_page.html +++ b/static/templates/chat_page.html @@ -15,6 +15,15 @@ +
@@ -29,71 +38,73 @@
- -
-
-
-

客服离线,请留言

-

我们的客服目前不在线,请填写以下表单,客服上线后会尽快回复您。

-
- - - - - - - - - - - - - - - - - + + + +
+
+
+

客服离线,请留言

+

我们的客服目前不在线,请填写以下表单,客服上线后会尽快回复您。

+
- - - - + + + + + + + + + + + + + + + + + + + + + + + + + 提交留言 + + + 尝试重新连接 + + + - - - 提交留言 +
+ + + + 继续留言 - - 尝试重新连接 - - - - -
- - - - 继续留言 - +
-
- - -
+ + +
@@ -143,6 +154,7 @@ Send
+

Announcements

<{kefuInfo.allNotice}>
@@ -739,7 +751,7 @@ }; this.$refs.offlineMessageForm.resetFields(); }, - tryReconnect:function() { + checkKefuStatusByWs:function() { let _this = this; _this.$message({ message: '正在尝试重新连接...', diff --git a/ws/user.go b/ws/user.go index 16cfb80..e3fc542 100644 --- a/ws/user.go +++ b/ws/user.go @@ -55,6 +55,9 @@ func NewKefuServer(c *gin.Context) { } } func AddKefuToList(kefu *User) { + Mux.Lock() + defer Mux.Unlock() + oldUser, ok := KefuList[kefu.Id] if oldUser != nil || ok { msg := TypeMessage{ @@ -69,21 +72,27 @@ func AddKefuToList(kefu *User) { KefuList[kefu.Id] = kefu // 广播客服上线状态给所有该客服的访客 - BroadcastKefuOnlineStatus(kefu.Id, true) + broadcastKefuOnlineStatusLocked(kefu.Id, true) } func RemoveKefuFromList(kefuId string) { + Mux.Lock() + defer Mux.Unlock() + _, ok := KefuList[kefuId] if ok { delete(KefuList, kefuId) // 广播客服离线状态给所有该客服的访客 - BroadcastKefuOnlineStatus(kefuId, false) + broadcastKefuOnlineStatusLocked(kefuId, false) } } // 给指定客服发消息 func OneKefuMessage(toId string, str []byte) { + Mux.RLock() kefu, ok := KefuList[toId] + Mux.RUnlock() + if ok { log.Println("OneKefuMessage lock") kefu.Mux.Lock() @@ -93,6 +102,27 @@ func OneKefuMessage(toId string, str []byte) { tools.Logger().Println("send_kefu_message", error, string(str)) } } + +// 内部函数:在持有锁的情况下广播客服状态 +func broadcastKefuOnlineStatusLocked(kefuId string, isOnline bool) { + kefuInfo := models.FindUser(kefuId) + msg := TypeMessage{ + Type: "kefu_status_change", + Data: map[string]interface{}{ + "kefu_id": kefuId, + "is_online": isOnline, + "nickname": kefuInfo.Nickname, + "avator": kefuInfo.Avator, + }, + } + str, _ := json.Marshal(msg) + + for _, visitor := range ClientList { + if visitor.To_id == kefuId && visitor.Conn != nil { + visitor.Conn.WriteMessage(websocket.TextMessage, str) + } + } +} func KefuMessage(visitorId, content string, kefuInfo models.User) { msg := TypeMessage{ Type: "message", @@ -120,11 +150,25 @@ func SendPingToKefuClient() { // 收集需要删除的客服ID var toRemove []string - for kefuId, kefu := range KefuList { - if kefu == nil { + Mux.RLock() + // 先遍历获取所有需要检查的客服ID + var kefuIds []string + for kefuId := range KefuList { + kefuIds = append(kefuIds, kefuId) + } + Mux.RUnlock() + + // 遍历检查每个客服连接(遍历过程中不持有锁,避免死锁) + for _, kefuId := range kefuIds { + Mux.RLock() + kefu, ok := KefuList[kefuId] + Mux.RUnlock() + + if !ok || kefu == nil { toRemove = append(toRemove, kefuId) continue } + kefu.Mux.Lock() err := kefu.Conn.WriteMessage(websocket.TextMessage, str) kefu.Mux.Unlock() diff --git a/ws/visitor.go b/ws/visitor.go index b3c55ff..a8657d0 100644 --- a/ws/visitor.go +++ b/ws/visitor.go @@ -212,7 +212,11 @@ func SendKefuStatusToVisitor(visitor *User, kefuId string) { if visitor == nil || visitor.Conn == nil { return } + + Mux.RLock() _, online := KefuList[kefuId] + Mux.RUnlock() + kefuInfo := models.FindUser(kefuId) msg := TypeMessage{ Type: "kefu_status", @@ -227,26 +231,6 @@ func SendKefuStatusToVisitor(visitor *User, kefuId string) { visitor.Conn.WriteMessage(websocket.TextMessage, str) } -func BroadcastKefuOnlineStatus(kefuId string, isOnline bool) { - kefuInfo := models.FindUser(kefuId) - msg := TypeMessage{ - Type: "kefu_status_change", - Data: map[string]interface{}{ - "kefu_id": kefuId, - "is_online": isOnline, - "nickname": kefuInfo.Nickname, - "avator": kefuInfo.Avator, - }, - } - str, _ := json.Marshal(msg) - - for _, visitor := range ClientList { - if visitor.To_id == kefuId && visitor.Conn != nil { - visitor.Conn.WriteMessage(websocket.TextMessage, str) - } - } -} - func HandleCheckKefuStatus(conn *websocket.Conn, data interface{}) { var kefuId string switch v := data.(type) { @@ -262,7 +246,10 @@ func HandleCheckKefuStatus(conn *websocket.Conn, data interface{}) { return } + Mux.RLock() _, online := KefuList[kefuId] + Mux.RUnlock() + kefuInfo := models.FindUser(kefuId) msg := TypeMessage{ Type: "kefu_status",