package ws import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" "kefu/models" "kefu/types" "log" "net/http" "sync" "time" ) type User struct { Conn *websocket.Conn Name string Id string Avator string To_id string Ent_id string Role_id string Mux sync.Mutex UpdateTime time.Time } type UserConnection struct { Mux sync.Mutex Users []*User } // 客服链接全局变量 var KefuList = make(map[string]*UserConnection) // 客服 func NewKefuServer(c *gin.Context) { conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { http.NotFound(c.Writer, c.Request) log.Print("upgrade:", err) return } kefuId, _ := c.Get("kefu_id") kefuName, _ := c.Get("kefu_name") kefuInfo := models.FindUserById(kefuId) if kefuInfo.ID == 0 { errStr := "客服用户不存在:" + kefuName.(string) log.Println(errStr) conn.WriteMessage(websocket.TextMessage, []byte(errStr)) conn.Close() return } var kefu User kefu.Id = kefuInfo.Name kefu.Name = kefuInfo.Nickname kefu.Avator = kefuInfo.Avator kefu.Role_id = kefuInfo.RoleId if kefuInfo.RoleId <= "2" { kefu.Ent_id = fmt.Sprintf("%d", kefuInfo.ID) } else { kefu.Ent_id = fmt.Sprintf("%d", kefuInfo.Pid) } kefu.Conn = conn AddKefuToList(&kefu) //更新客服正在接待数量 go models.UpdateUserRecNumZero(kefuInfo.Name, VisitorCount(kefuInfo.Name)) for { //接受消息 //var receive []byte _, _, err := conn.ReadMessage() //每次读到消息,循环一圈我的conn kefuConnection, ok := KefuList[kefuInfo.Name] if !ok || len(kefuConnection.Users) == 0 { conn.Close() return } //kefuConnection.Users = removeUsersConn(kefuConnection.Users, conn) var newKefuConns []*User flag := false for _, kefuConn := range kefuConnection.Users { if kefuConn.Conn != conn { newKefuConns = append(newKefuConns, kefuConn) } else { //找到我了 flag = true } } //如果链接关闭了,把其他conn加进去就可以了 if err != nil { log.Println("客服ws链接失败:ws/user.go,客服离开 ", kefuInfo.Name, err) conn.Close() kefuConnection.Users = newKefuConns kefuConnection.Mux.Lock() KefuList[kefuInfo.Name] = kefuConnection kefuConnection.Mux.Unlock() return } ////链接没关闭,但是循环一圈没我这个链接 if !flag { conn.Close() return } } } func AddKefuToList(kefu *User) { var newKefuConns = []*User{kefu} kefuConnection, ok := KefuList[kefu.Id] if !ok { kefuConnection = &UserConnection{ Users: newKefuConns, } KefuList[kefu.Id] = kefuConnection return } unreadNum := models.CountMessage("kefu_id = ? and mes_type='visitor' and status = 'unread'", kefu.Id) msg := TypeMessage{ Type: "unread_num", Data: unreadNum, } str, _ := json.Marshal(msg) err := kefu.Conn.WriteMessage(websocket.TextMessage, str) if err != nil { kefu.Conn.Close() log.Println("客服ws链接失败:", kefu.Id, err) } if len(kefuConnection.Users) != 0 { for _, otherKefu := range kefuConnection.Users { err := otherKefu.Conn.WriteMessage(websocket.TextMessage, str) if err == nil { newKefuConns = append(newKefuConns, otherKefu) } else { otherKefu.Conn.Close() log.Println("客服ws链接失败:", kefu.Id, err) } } } kefuConnection.Users = newKefuConns log.Println("客服ws链接成功:客服来了 ", kefu.Id, kefu.Name) kefuConnection.Mux.Lock() KefuList[kefu.Id] = kefuConnection kefuConnection.Mux.Unlock() } // 给超管发消息 func SuperAdminMessage(str []byte) { return //给超管发 } // 访客给客服发消息 func VisitorSendToKefu(kefuName string, visitorInfo models.Visitor, content string) { msgId := models.CreateMessage(kefuName, visitorInfo.VisitorId, content, "visitor", visitorInfo.EntId, "read") msg := TypeMessage{ Type: "message", Data: ClientMessage{ MsgId: msgId, Avator: visitorInfo.Avator, Id: visitorInfo.VisitorId, VisitorId: visitorInfo.VisitorId, Name: visitorInfo.Name, ToId: kefuName, Content: content, Time: time.Now().Format("2006-01-02 15:04:05"), IsKefu: "no", }, } str, _ := json.Marshal(msg) OneKefuMessage(kefuName, str) } // 给指定客服发消息 func OneKefuMessage(toId string, str []byte) { //新版 kefuConnection, ok := KefuList[toId] if ok && len(kefuConnection.Users) > 0 { for _, kefu := range kefuConnection.Users { kefu.Mux.Lock() error := kefu.Conn.WriteMessage(websocket.TextMessage, str) kefu.Mux.Unlock() if error != nil { log.Println("send_kefu_message", error, string(str)) } } } SuperAdminMessage(str) } func KefuMessage(visitorId, content string, kefuInfo models.User) { msg := TypeMessage{ Type: "message", Data: ClientMessage{ Name: kefuInfo.Nickname, Avator: kefuInfo.Avator, Id: visitorId, Time: time.Now().Format("2006-01-02 15:04:05"), ToId: visitorId, Content: content, IsKefu: "yes", }, } str, _ := json.Marshal(msg) OneKefuMessage(kefuInfo.Name, str) } // 给客服客户端发送消息判断客户端是否在线 func SendPingToKefuClient() { for kefuId, kefuConnection := range KefuList { var newKefuConns []*User //lastDay := tools.GetPastDate(10) //unreadNum := models.CountMessage("kefu_id = ? and created_at > ? and mes_type='visitor' and status = 'unread'", kefuId, lastDay) var ( entId string ) for _, kefuConn := range kefuConnection.Users { entId = kefuConn.Ent_id if kefuConn == nil { continue } msg := TypeMessage{ Type: "ping", //Data: unreadNum, } str, _ := json.Marshal(msg) kefuConn.Mux.Lock() err := kefuConn.Conn.WriteMessage(websocket.TextMessage, str) kefuConn.Mux.Unlock() if err == nil { newKefuConns = append(newKefuConns, kefuConn) } else { log.Println("客服ws链接失败:", err) } } kefuConnection.Users = newKefuConns if len(newKefuConns) > 0 { kefuConnection.Mux.Lock() KefuList[kefuId] = kefuConnection kefuConnection.Mux.Unlock() } else { online := &models.UpDownLine{ EntId: entId, KefuName: kefuId, OnlineStatus: 2, ClientIp: "system clean", CreatedAt: types.Time{}, } online.AddUpDownLine() go models.UpdateUserRecNumZero(kefuId, 0) delete(KefuList, kefuId) } } } // 删除切片数据 func removeUsersConn(s []*User, conn *websocket.Conn) []*User { for i := 0; i < len(s); i++ { if s[i].Conn == conn { s = append(s[:i], s[i+1:]...) i-- } } return s }