feat(ws): 实现客服状态实时推送功能

- 移除原有的HTTP轮询检查客服在线状态接口
- 通过WebSocket实时推送客服上下线状态给访客
- 新增客服状态检查和处理逻辑
- 前端适配新的状态推送机制
pull/56/head
CGW406 5 months ago
parent a196947646
commit 6a19942798

@ -6,7 +6,6 @@ import (
"github.com/gin-gonic/gin"
"goflylivechat/models"
"goflylivechat/tools"
"goflylivechat/ws"
"log"
"strconv"
"time"
@ -420,44 +419,6 @@ func BatchMarkOfflineMessagesAsRead(c *gin.Context) {
})
}
func CheckKefuOnline(c *gin.Context) {
kefuId := c.Query("kefu_id")
if kefuId == "" {
c.JSON(200, gin.H{
"code": 400,
"msg": "客服ID不能为空",
})
return
}
kefuInfo := models.FindUser(kefuId)
if kefuInfo.ID == 0 {
c.JSON(200, gin.H{
"code": 404,
"msg": "客服不存在",
})
return
}
isOnline := false
for name := range ws.KefuList {
if name == kefuId {
isOnline = true
break
}
}
c.JSON(200, gin.H{
"code": 200,
"msg": "ok",
"result": gin.H{
"kefu_id": kefuId,
"nickname": kefuInfo.Nickname,
"avator": kefuInfo.Avator,
"is_online": isOnline,
},
})
}
func SendOfflineMessageNotice(kefuName, visitorName, content string) {
smtp := models.FindConfig("NoticeEmailSmtp")

Binary file not shown.

@ -93,8 +93,7 @@ func InitApiRouter(engine *gin.Engine) {
}
//离线留言接口 - 访客端
engine.POST("/offline_message", middleware.Ipblack, controller.SubmitOfflineMessage)
engine.GET("/kefu/online", controller.CheckKefuOnline)
//离线留言接口 - 客服端(需要认证)
offlineMessageGroup := engine.Group("/offline_messages")
offlineMessageGroup.Use(middleware.JwtApiMiddleware)

@ -232,6 +232,14 @@
this.socketClosed=false;
this.focusSendConn=false;
const redata = JSON.parse(e.data);
if (redata.type == "kefu_status") {
let data = redata.data;
this.handleKefuStatus(data);
}
if (redata.type == "kefu_status_change") {
let data = redata.data;
this.handleKefuStatus(data);
}
if (redata.type == "kfOnline") {
let msg = redata.data
if(this.showKfonline && this.visitor.to_id==msg.id){
@ -305,6 +313,19 @@
}
window.parent.postMessage(redata,"*");
},
handleKefuStatus:function(data) {
if(data && data.is_online) {
this.showOfflineMessageForm = false;
if(data.avator) {
this.kefuInfo.avatar = data.avator;
}
} else {
this.showOfflineMessageForm = true;
if(data && data.avator) {
this.kefuInfo.avatar = data.avator;
}
}
},
chatToUser:function() {
var messageContent=this.messageContent.trim("\r\n");
messageContent=messageContent.replace("\n","");
@ -667,24 +688,6 @@
$(".chatBox").append("<div class='chatNotice'><div class=\"chatNoticeContent\"><span>"+title+"</span></div></div>");
this.scrollBottom();
},
checkKefuOnline:function() {
let _this = this;
$.get("/kefu/online?kefu_id="+KEFU_ID, function(res) {
if(res.code == 200 && res.result) {
if(res.result.is_online) {
_this.showOfflineMessageForm = false;
_this.getUserInfo();
} else {
_this.showOfflineMessageForm = true;
_this.kefuInfo.avatar = res.result.avator;
}
} else {
_this.showOfflineMessageForm = true;
}
}).fail(function() {
_this.showOfflineMessageForm = true;
});
},
submitOfflineMessage:function() {
let _this = this;
this.$refs.offlineMessageForm.validate(function(valid) {
@ -742,7 +745,15 @@
message: '正在尝试重新连接...',
type: 'info'
});
this.checkKefuOnline();
if(this.socket && this.socket.readyState === WebSocket.OPEN) {
let msg = {
type: "check_kefu_status",
data: KEFU_ID
};
this.socket.send(JSON.stringify(msg));
} else {
this.initConn();
}
},
},
mounted:function() {
@ -751,7 +762,7 @@
},
created: function () {
this.init();
this.checkKefuOnline();
this.getUserInfo();
}
})
</script>

@ -41,6 +41,7 @@ func NewKefuServer(c *gin.Context) {
messageType, receive, err := conn.ReadMessage()
if err != nil {
log.Println("ws/user.go ", err)
RemoveKefuFromList(kefuInfo.Name)
conn.Close()
return
}
@ -66,6 +67,18 @@ func AddKefuToList(kefu *User) {
}
}
KefuList[kefu.Id] = kefu
// 广播客服上线状态给所有该客服的访客
BroadcastKefuOnlineStatus(kefu.Id, true)
}
func RemoveKefuFromList(kefuId string) {
_, ok := KefuList[kefuId]
if ok {
delete(KefuList, kefuId)
// 广播客服离线状态给所有该客服的访客
BroadcastKefuOnlineStatus(kefuId, false)
}
}
// 给指定客服发消息
@ -103,16 +116,26 @@ func SendPingToKefuClient() {
Type: "many pong",
}
str, _ := json.Marshal(msg)
// 收集需要删除的客服ID
var toRemove []string
for kefuId, kefu := range KefuList {
if kefu == nil {
toRemove = append(toRemove, kefuId)
continue
}
kefu.Mux.Lock()
defer kefu.Mux.Unlock()
err := kefu.Conn.WriteMessage(websocket.TextMessage, str)
kefu.Mux.Unlock()
if err != nil {
log.Println("定时发送ping给客服失败", err.Error())
delete(KefuList, kefuId)
toRemove = append(toRemove, kefuId)
}
}
// 从列表中移除并广播离线状态
for _, kefuId := range toRemove {
RemoveKefuFromList(kefuId)
}
}

@ -79,6 +79,10 @@ func AddVisitorToList(user *User) {
}
}
ClientList[user.Id] = user
// 发送客服在线状态给访客
SendKefuStatusToVisitor(user, user.To_id)
lastMessage := models.FindLastMessageByVisitorId(user.Id)
userInfo := make(map[string]string)
userInfo["uid"] = user.Id
@ -203,3 +207,72 @@ func CleanVisitorExpire() {
}
}()
}
func SendKefuStatusToVisitor(visitor *User, kefuId string) {
if visitor == nil || visitor.Conn == nil {
return
}
_, online := KefuList[kefuId]
kefuInfo := models.FindUser(kefuId)
msg := TypeMessage{
Type: "kefu_status",
Data: map[string]interface{}{
"kefu_id": kefuId,
"is_online": online,
"nickname": kefuInfo.Nickname,
"avator": kefuInfo.Avator,
},
}
str, _ := json.Marshal(msg)
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) {
case string:
kefuId = v
case map[string]interface{}:
if id, ok := v["kefu_id"].(string); ok {
kefuId = id
}
}
if kefuId == "" {
return
}
_, online := KefuList[kefuId]
kefuInfo := models.FindUser(kefuId)
msg := TypeMessage{
Type: "kefu_status",
Data: map[string]interface{}{
"kefu_id": kefuId,
"is_online": online,
"nickname": kefuInfo.Nickname,
"avator": kefuInfo.Avator,
},
}
str, _ := json.Marshal(msg)
conn.WriteMessage(websocket.TextMessage, str)
}

@ -134,6 +134,8 @@ func WsServerBackend() {
if tools.LimitFreqSingle("inputing:"+from, 1, 2) {
OneKefuMessage(to, message.content)
}
case "check_kefu_status":
HandleCheckKefuStatus(conn, typeMsg.Data)
}
}

Loading…
Cancel
Save