package controller import ( "encoding/json" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/tidwall/gjson" "kefu/common" "kefu/models" v2 "kefu/models/v2" "kefu/tools" "kefu/types" "kefu/ws" "log" "math/rand" "net/url" "sort" "strconv" "strings" "time" ) type VisitorLoginForm struct { VisitorId string `form:"visitor_id" json:"visitor_id" uri:"visitor_id" xml:"visitor_id"` Refer string `form:"refer" json:"refer" uri:"refer" xml:"refer"` ReferUrl string `form:"refer_url" json:"refer" uri:"refer" xml:"refer"` Title string `form:"title" json:"title" uri:"title" xml:"title"` Url string `form:"url" json:"url" uri:"url" xml:"url"` ToId string `form:"to_id" json:"to_id" uri:"to_id" xml:"to_id"` EntId string `form:"ent_id" json:"ent_id" uri:"ent_id" xml:"ent_id" binding:"required"` Avator string `form:"avator" json:"avator" uri:"avator" xml:"avator"` UserAgent string `form:"user_agent" json:"user_agent" uri:"user_agent" xml:"user_agent"` Extra string `form:"extra" json:"extra" uri:"extra" xml:"extra"` ClientIp string `form:"client_ip" json:"client_ip" uri:"client_ip" xml:"client_ip"` CityAddress string `form:"city_address" json:"city_address" uri:"city_address" xml:"city_address"` VisitorName string `form:"visitor_name" json:"visitor_name" uri:"visitor_name" xml:"visitor_name"` Language string `form:"language" json:"language" uri:"language" xml:"language"` } func PostVisitorLogin(c *gin.Context) { var form VisitorLoginForm err := c.Bind(&form) if err != nil { HandleError(c, types.ApiCode.FAILED, types.ApiCode.GetMessage(types.ApiCode.INVALID), err) return } //限流 if models.FindConfig("SystemFreqLimt") != "true" && !tools.LimitFreqSingle("visitor_login:"+c.ClientIP(), 1, 2) { HandleError(c, types.ApiCode.FREQ_LIMIT, types.ApiCode.GetMessage(types.ApiCode.FREQ_LIMIT), errors.New("访问接口频率限制")) return } isWechat := false originVisitorId := "" // 如果访客ID为空 if form.VisitorId == "" { // 判断访客ID的生成规则 idRule := models.FindConfig("VisitorIdRule") if idRule == "ip" { // 使用IP生成访客ID的MD5哈希值 form.VisitorId = tools.Md5(c.ClientIP()) } else { // 使用UUID生成访客ID form.VisitorId = tools.Uuid() } form.VisitorId = fmt.Sprintf("%s|%s", form.EntId, form.VisitorId) } else { originVisitorId = form.VisitorId // 验证访客是否在黑名单中 if !CheckVisitorBlack(form.VisitorId) { HandleError(c, types.ApiCode.VISITOR_BAN, types.ApiCode.GetMessage(types.ApiCode.VISITOR_BAN), errors.New("访客在黑名单中")) return } visitorIds := strings.Split(form.VisitorId, "|") if len(visitorIds) > 1 { if visitorIds[0] != "wx" && visitorIds[0] != "mini" { // 如果不是微信或小程序,则重新构造访客ID form.VisitorId = strings.Join(visitorIds[1:], "|") form.VisitorId = fmt.Sprintf("%s|%s", form.EntId, form.VisitorId) } else { isWechat = true } } else { form.VisitorId = fmt.Sprintf("%s|%s", form.EntId, form.VisitorId) } } // 获取请求的User-Agent信息 form.UserAgent = c.GetHeader("User-Agent") // 如果Avator为空且不是微信登录 if form.Avator == "" && !isWechat { // 根据User-Agent判断是移动设备还是桌面设备,并设置默认头像 if tools.IsMobile(form.UserAgent) { form.Avator = "/static/images/phone.png" } else { form.Avator = "/static/images/computer.png" } } else { // 解码Avator中的URL转义字符 form.Avator, _ = url.QueryUnescape(form.Avator) } // 解析IP地址获取城市信息 ipCity, _ := tools.ParseIpNew(c.ClientIP()) if ipCity != nil { form.CityAddress = ipCity.CountryName + ipCity.RegionName + ipCity.CityName + ipCity.AreaName } else { form.CityAddress = "Unknown Area" } // 如果VisitorName为空且不是微信登录 if form.VisitorName == "" && !isWechat { // 如果IP所在国家是中国,则使用省市区作为访客名称,否则使用完整城市地址 if ipCity.CountryName == "中国" { form.VisitorName = ipCity.RegionName + ipCity.CityName + ipCity.AreaName } else { form.VisitorName = form.CityAddress } } else { // 解码VisitorName中的URL转义字符 form.VisitorName, _ = url.QueryUnescape(form.VisitorName) } form.ClientIp = c.ClientIP() //验证ip黑名单 ipblack := models.FindIp(form.ClientIp) if ipblack.IP != "" && ipblack.EntId == form.EntId { HandleError(c, types.ApiCode.IP_BAN, types.ApiCode.GetMessage(types.ApiCode.IP_BAN), errors.New("IP在黑名单中")) return } makeVisitorLoginForm(&form) allOffline := true //查询企业信息 entKefuInfo := models.FindUserRoleById(form.EntId) if entKefuInfo.ID == 0 || entKefuInfo.RoleId == tools.Int2Str(types.Constant.AgentRoleId) { HandleError(c, types.ApiCode.ENT_ERROR, types.ApiCode.GetMessage(types.ApiCode.ENT_ERROR), errors.New("商户ID不存在")) return } kefuCommonGin := gin.H{ "username": entKefuInfo.Nickname, "avatar": entKefuInfo.Avator, "intro_pic": entKefuInfo.CompanyPic, } //查询企业配置项 configs := models.GetEntConfigsMap(form.EntId, "KefuIntroduce", "RobotStatus", "TurnToMan", "RobotNoAnswer", "RobotName", "RobotAvator", "RobotCard", "VisitorNotice", "AutoWelcome", "VisitorCookie", "VisitorMaxNum", "VisitorMaxNumNotice", "ScanWechatQrcode", "VisitorAuthApi", "CloseNotice", "QdrantAIStatus") //访客Cookie有效期 if configs["VisitorCookie"] == "" { configs["VisitorCookie"] = fmt.Sprintf("%d", 24*3600*365*100) } //机器人信息 RobotCommonGin := gin.H{ "RobotStatus": configs["RobotStatus"], "TurnToMan": configs["TurnToMan"], "RobotNoAnswer": configs["RobotNoAnswer"], "RobotName": configs["RobotName"], "RobotAvator": configs["RobotAvator"], "RobotCard": configs["RobotCard"], "RobotSwitch": tools.Ifelse(configs["RobotStatus"] == "2", "true", "false"), } //查看过期时间 nowSecond := time.Now().Unix() expireSecond := entKefuInfo.ExpiredAt.Unix() if models.FindConfig("KefuExpired") == "on" && expireSecond < nowSecond { c.JSON(200, gin.H{ "code": types.ApiCode.ACCOUNT_EXPIRED, "msg": types.ApiCode.GetMessage(types.ApiCode.ACCOUNT_EXPIRED), "kefu": kefuCommonGin, }) return } //查看状态 if entKefuInfo.Status == 1 { c.JSON(200, gin.H{ "code": types.ApiCode.ACCOUNT_FORBIDDEN, "msg": types.ApiCode.GetMessage(types.ApiCode.ACCOUNT_FORBIDDEN), "kefu": kefuCommonGin, }) return } //访客认证回调 if configs["VisitorAuthApi"] != "" { api := configs["VisitorAuthApi"] + "?visitor_id=" + originVisitorId + "&sign=" + tools.Md5(originVisitorId+"VisitorAuthApi") res := tools.Get(api) avatar := gjson.Get(res, "avatar").String() name := gjson.Get(res, "name").String() if res == "" || name == "" || avatar == "" { log.Println("访客认证回调失败:", api, res) c.JSON(200, gin.H{ "code": types.ApiCode.VISITOR_FORBIDDEN, "msg": types.ApiCode.GetMessage(types.ApiCode.VISITOR_FORBIDDEN), "kefu": kefuCommonGin, }) return } form.Avator = avatar form.VisitorName = name } dstKefu := entKefuInfo if form.ToId != "" && form.ToId != entKefuInfo.Name { tmpInfo := models.FindUser(form.ToId) if tmpInfo.Name != "" && (int64(tmpInfo.ID) == tools.Str2Int64(form.EntId) || int64(tmpInfo.Pid) == tools.Str2Int64(form.EntId)) { dstKefu = tmpInfo } } //判断是否在线,判断指定客服是否在线 _, ok := ws.KefuList[dstKefu.Name] if dstKefu.OnlineStatus == 1 && ok { allOffline = false } else { //查找在线的子账号 kefus := models.FindUsersWhere("pid = ? and online_status=1", form.EntId) //kefus := models.FindUsersByPid(form.EntId) if len(kefus) == 0 { allOffline = true } else { //随机化 rand.Seed(time.Now().Unix()) sort.Sort(models.ByRecNumRand(kefus)) for _, kefu := range kefus { if _, ok := ws.KefuList[kefu.Name]; ok { form.ToId = kefu.Name allOffline = false dstKefu = kefu break } } } } //如果子账号全都不在线,并且主账号未设置离线,分给主账号 if allOffline == true && form.ToId != entKefuInfo.Name && entKefuInfo.OnlineStatus == 1 { form.ToId = entKefuInfo.Name dstKefu = entKefuInfo _, ok := ws.KefuList[dstKefu.Name] if ok { allOffline = false } } //判断是否开启AI自动回复 if configs["QdrantAIStatus"] == "true" { allOffline = false } kefuCommonGin["staffNickname"] = dstKefu.Nickname kefuCommonGin["staffAvatar"] = dstKefu.Avator //判断是否绑定手机 if models.FindConfig("KefuBindTel") == "true" && dstKefu.Tel == "" { c.JSON(200, gin.H{ "code": types.ApiCode.BIND_TEL_FAILD, "msg": types.ApiCode.GetMessage(types.ApiCode.BIND_TEL_FAILD), "kefu": kefuCommonGin, }) return } //同时接待人数 visitorMaxNum := int(dstKefu.RecNum) visitorMaxNumConfig, _ := strconv.Atoi(configs["VisitorMaxNum"]) if visitorMaxNumConfig == 0 { visitorMaxNumConfig = 100000 } if visitorMaxNum >= visitorMaxNumConfig { c.JSON(200, gin.H{ "code": types.ApiCode.VISITOR_MAX_FORBIDDEN, "msg": types.ApiCode.GetMessage(types.ApiCode.VISITOR_MAX_FORBIDDEN), "visitorMaxNum": visitorMaxNum, "robotNoAnswer": configs["RobotNoAnswer"], "turnToMan": configs["TurnToMan"], "visitorMaxNumNotice": configs["VisitorMaxNumNotice"], "result": gin.H{ "avator": form.Avator, "name": form.VisitorName, }, "kefu": kefuCommonGin, }) return } visitor := models.FindVisitorByVistorId(form.VisitorId) visitor.ToId = dstKefu.Name visitNum := visitor.VisitNum + 1 if visitor.ID != 0 { if form.Avator == "" && isWechat { form.Avator = visitor.Avator } if form.VisitorName == "" && isWechat { form.VisitorName = visitor.Name } if visitor.RealName != "" { form.VisitorName = visitor.RealName } //更新状态上线 models.UpdateVisitor(form.EntId, form.VisitorName, form.Avator, form.VisitorId, dstKefu.Name, visitor.Status, c.ClientIP(), c.ClientIP(), form.Refer, form.Extra, form.CityAddress, visitNum, ) } else { visitor = *models.CreateVisitor(form.VisitorName, form.Avator, c.ClientIP(), dstKefu.Name, form.VisitorId, form.Refer, form.CityAddress, form.ClientIp, form.EntId, form.Extra) } if form.VisitorName != "" { visitor.Name = form.VisitorName } go models.AddVisitorExt(visitor.VisitorId, form.Refer, form.UserAgent, form.Url, form.ReferUrl, form.Title, form.ClientIp, form.CityAddress, form.EntId, form.Language) //如果没有开启机器人才发送通知 if configs["RobotStatus"] != "2" { noticeContent := fmt.Sprintf("%s访问%d次", visitor.Name, visitNum) go SendVisitorLoginNotice(form.ToId, visitor.Name, visitor.Avator, noticeContent, visitor.VisitorId) //go SendWechatVisitorTemplate(form.ToId, visitor.Name, noticeContent, visitor.EntId) //go SendWechatKefuNotice(form.ToId, "[访客]"+visitor.Name+",访问:"+form.Refer, visitor.EntId) go SendNoticeEmail(visitor.Name, "[访客]"+visitor.Name, form.EntId, noticeContent) go SendAppGetuiPush(dstKefu.Name, "[访客]"+visitor.Name, noticeContent) } else { //开启仅机器人接待时,不显示离线状态 allOffline = false } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "alloffline": allOffline, "entIntroduce": configs["KefuIntroduce"], "robotSwitch": tools.Ifelse(configs["RobotStatus"] == "2", "true", "false"), "turnToMan": configs["TurnToMan"], "robotNoAnswer": configs["RobotNoAnswer"], "visitorNotice": configs["VisitorNotice"], "autoWelcome": configs["AutoWelcome"], "visitorCookie": configs["VisitorCookie"], "scanWechatQrcode": configs["ScanWechatQrcode"], "closeNotice": configs["CloseNotice"], "result": visitor, "kefu": kefuCommonGin, "robot": RobotCommonGin, }) } // 组合扩展信息 func makeVisitorLoginForm(form *VisitorLoginForm) { //扩展信息 extraJson := tools.Base64Decode(form.Extra) if extraJson != "" { var extraObj VisitorExtra err := json.Unmarshal([]byte(extraJson), &extraObj) if err == nil { if extraObj.VisitorName != "" { form.VisitorName = extraObj.VisitorName } if extraObj.VisitorAvatar != "" { form.Avator = extraObj.VisitorAvatar } if extraObj.VisitorId != "" { form.VisitorId = extraObj.VisitorId } } } } func GetVisitor(c *gin.Context) { visitorId := c.Query("visitorId") entId, _ := c.Get("ent_id") vistor := models.FindVisitorByVistorId(visitorId) exts := models.FindVisitorExtByVistorId(visitorId, 1, 1) sameIPCount := models.CountVisitorsByEntid(fmt.Sprintf("%v", entId), " and client_ip = ? ", vistor.ClientIp) osVersion := "" browser := "" ua := "" if len(exts) != 0 { ext := exts[0] ua = ext.Ua uaParser := tools.NewUaParser(ua) osVersion = uaParser.OsVersion browser = uaParser.Browser } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "create_time": vistor.CreatedAt.Format("2006-01-02 15:04:05"), "last_time": vistor.UpdatedAt.Format("2006-01-02 15:04:05"), "os_version": osVersion, "browser": browser, "ua": ua, "same_ip": sameIPCount, "result": vistor, }) } /* * 获取访客访问动态 */ func GetVisitorExt(c *gin.Context) { visitorId := c.Query("visitor_id") page, pagesize := HandlePagePageSize(c) list := make([]VisitorExtend, 0) count := models.CountVisitorExtByVistorId(visitorId) exts := models.FindVisitorExtByVistorId(visitorId, page, pagesize) for _, ext := range exts { uaParser := tools.NewUaParser(ext.Ua) item := VisitorExtend{ CreatedAt: ext.CreatedAt.Format("2006-01-02 15:04:05"), ID: ext.ID, VisitorId: ext.VisitorId, Url: ext.Url, Ua: ext.Ua, Title: ext.Title, ClientIp: ext.ClientIp, OsVersion: uaParser.OsVersion, Browser: uaParser.Browser, Refer: ext.Refer, City: ext.City, ReferUrl: ext.ReferUrl, Language: ext.Language, } list = append(list, item) } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "list": list, "count": count, "pagesize": pagesize, }, }) } /* * 获取访客访问动态列表 */ func GetVisitorExtList(c *gin.Context) { entId, _ := c.Get("ent_id") roleId, _ := c.Get("role_id") query := "1=1 " var arg = []interface{}{} if roleId.(float64) != 1 { query += "and ent_id = ? " arg = append(arg, entId) } title := tools.UrlDecode(c.Query("title")) referTitle := tools.UrlDecode(c.Query("refer_title")) if title != "" { query += "and title like ? " arg = append(arg, title+"%") } if referTitle != "" { query += "and refer_title like ? " arg = append(arg, referTitle+"%") } page, pagesize := HandlePagePageSize(c) count := models.CountVisitorExtByWhere(query, arg...) exts := models.FindVisitorExtByWhere(page, pagesize, query, arg...) c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "list": exts, "count": count, "pagesize": pagesize, "page": page, }, }) } /* * 获取访客访问动态列表 */ func GetRateList(c *gin.Context) { entId, _ := c.Get("ent_id") roleId, _ := c.Get("role_id") query := "1=1 " var arg = []interface{}{} if roleId.(float64) != 1 { query += "and ent_id = ? " arg = append(arg, entId) } page, pagesize := HandlePagePageSize(c) count := models.CountRateWhere(query, arg...) exts := models.FindRateByWhere(page, pagesize, query, arg...) c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "list": exts, "count": count, "pagesize": pagesize, "page": page, }, }) } // @Summary 获取访客列表接口 // @Produce json // @Accept multipart/form-data // @Param page query string true "分页" // @Param token header string true "认证token" // @Success 200 {object} controller.Response // @Failure 200 {object} controller.Response // @Router /visitors [get] func GetVisitors(c *gin.Context) { page, _ := strconv.Atoi(c.Query("page")) var pagesize uint myPagesize, _ := strconv.Atoi(c.Query("pagesize")) if myPagesize != 0 { pagesize = uint(myPagesize) } else { pagesize = common.VisitorPageSize } kefuId, _ := c.Get("kefu_name") vistors := models.FindVisitorsByKefuId(uint(page), pagesize, kefuId.(string)) visitorIds := make([]string, 0) for _, visitor := range vistors { visitorIds = append(visitorIds, visitor.VisitorId) } messagesMap := models.FindLastMessageMap(visitorIds) unreadMap := models.FindUnreadMessageNumByVisitorIds(visitorIds, "visitor") //log.Println(unreadMap) users := make([]VisitorOnline, 0) for _, visitor := range vistors { var unreadNum uint32 if num, ok := unreadMap[visitor.VisitorId]; ok { unreadNum = num } user := VisitorOnline{ Id: visitor.ID, VisitorId: visitor.VisitorId, Avator: visitor.Avator, Ip: visitor.SourceIp, Username: fmt.Sprintf("#%d %s", visitor.ID, visitor.Name), LastMessage: messagesMap[visitor.VisitorId], UpdatedAt: visitor.UpdatedAt, UnreadNum: unreadNum, Status: visitor.Status, } users = append(users, user) } count := models.CountVisitorsByKefuId(kefuId.(string)) c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "list": users, "count": count, "pagesize": pagesize, }, }) } func GetVisitorsList(c *gin.Context) { page, _ := strconv.Atoi(c.Query("page")) pagesize, _ := strconv.Atoi(c.Query("pagesize")) entId, _ := c.Get("ent_id") search := "" args := []interface{}{} //通过访客名搜索 visitorName := c.Query("visitorName") if tools.IsIP(visitorName) { search += " and client_ip = ? " args = append(args, visitorName) } else if visitorName != "" { search += " and (name like ? or visitor_id like ?)" args = append(args, "%"+visitorName+"%") args = append(args, "%"+visitorName+"%") } //通过客服名搜索 kefuName := c.Query("kefuName") if kefuName != "" { search += " and to_id = ? " args = append(args, kefuName) } //根据创建时间 createTime := c.Query("createTime") if createTime != "" { timeArr := strings.Split(createTime, "~") search += " and created_at>= ? and created_at <= ? " args = append(args, timeArr[0], timeArr[1]) } //根据活跃时间 updateTime := c.Query("updateTime") if updateTime != "" { timeArr := strings.Split(updateTime, "~") search += " and updated_at>= ? and updated_at <= ? " args = append(args, timeArr[0], timeArr[1]) } //根据tag找出visitor visitorTag := c.Query("visitorTag") var visitorIdsArr []string if visitorTag != "" { visitorTags := v2.GetVisitorTags("tag_id = ? ", visitorTag) for _, visitorTag := range visitorTags { visitorIdsArr = append(visitorIdsArr, visitorTag.VisitorId) } if len(visitorIdsArr) != 0 { search += " and visitor_id in (?) " args = append(args, visitorIdsArr) } } //根据内容找出visitor visitorMessage := c.Query("visitorMessage") if visitorMessage != "" { visitors := models.FindMessageByQuery("ent_id = ? and content like ?", entId, "%"+visitorMessage+"%") for _, visitor := range visitors { visitorIdsArr = append(visitorIdsArr, visitor.VisitorId) } if len(visitorIdsArr) != 0 { search += " and visitor_id in (?) " args = append(args, visitorIdsArr) } } //根据已读未读找出visitor //根据内容找出visitor readType := c.Query("readType") if readType != "" { visitors := models.FindMessageByQuery("ent_id = ? and mes_type='visitor' and status = ?", entId, readType) visitorIdsArr = []string{} for _, visitor := range visitors { visitorIdsArr = append(visitorIdsArr, visitor.VisitorId) } if len(visitorIdsArr) != 0 { search += " and visitor_id in (?) " args = append(args, visitorIdsArr) } } //排序参数 orderBy := c.Query("orderBy") if orderBy == "" { orderBy = "updated_at desc" } else { orderBy = orderBy + " desc" } vistors := models.FindVisitorsByEntId(uint(page), uint(pagesize), fmt.Sprintf("%v", entId), orderBy, search, args...) count := models.CountVisitorsByEntid(fmt.Sprintf("%v", entId), search, args...) //获取最后一条消息内容 visitorIds := make([]string, 0) for _, visitor := range vistors { visitorIds = append(visitorIds, visitor.VisitorId) } messagesMap := models.FindLastMessageMap(visitorIds) //获取访客未读数 unreadMap := models.FindUnreadMessageNumByVisitorIds(visitorIds, "visitor") //log.Println(unreadMap) users := make([]VisitorOnline, 0) for _, visitor := range vistors { var unreadNum uint32 if num, ok := unreadMap[visitor.VisitorId]; ok { unreadNum = num } username := fmt.Sprintf("#%d %s", visitor.ID, visitor.Name) if visitor.RealName != "" { username = visitor.RealName } user := VisitorOnline{ Id: visitor.ID, VisitorId: visitor.VisitorId, City: visitor.City, Avator: visitor.Avator, Ip: visitor.SourceIp, Username: username, LastMessage: messagesMap[visitor.VisitorId], UpdatedAt: visitor.UpdatedAt, CreatedAt: visitor.CreatedAt, UnreadNum: unreadNum, Status: visitor.Status, } users = append(users, user) } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "list": users, "count": count, "pagesize": uint(pagesize), "page": page, }, }) } func GetVisitorMessageByKefu(c *gin.Context) { entId, _ := c.Get("ent_id") visitorId := c.Query("visitor_id") log.Println(entId, visitorId) messages := models.FindMessageByWhere("message.visitor_id=? and message.ent_id=?", visitorId, entId) chatMessages := make([]ChatMessage, 0) for _, message := range messages { var chatMessage ChatMessage chatMessage.Time = message.CreatedAt.Format("2006-01-02 15:04:05") chatMessage.Content = message.Content chatMessage.MesType = message.MesType if message.MesType == "kefu" { chatMessage.Name = message.KefuName chatMessage.Avator = message.KefuAvator } else { chatMessage.Name = message.VisitorName chatMessage.Avator = message.VisitorAvator } chatMessages = append(chatMessages, chatMessage) } models.ReadMessageByVisitorId(visitorId, "visitor") c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": chatMessages, }) } // @Summary 获取在线访客列表接口 // @Produce json // @Success 200 {object} controller.Response // @Failure 200 {object} controller.Response // @Router /visitors_online [get] func GetVisitorOnlines(c *gin.Context) { users := make([]map[string]string, 0) visitorIds := make([]string, 0) for uid, visitor := range ws.ClientList { userInfo := make(map[string]string) userInfo["uid"] = uid userInfo["name"] = visitor.Name userInfo["avator"] = visitor.Avatar users = append(users, userInfo) visitorIds = append(visitorIds, visitor.Id) } //查询最新消息 messages := models.FindLastMessage(visitorIds) temp := make(map[string]string, 0) for _, mes := range messages { temp[mes.VisitorId] = mes.Content } for _, user := range users { user["last_message"] = temp[user["uid"]] } tcps := make([]string, 0) for ip, _ := range clientTcpList { tcps = append(tcps, ip) } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": gin.H{ "ws": users, "tcp": tcps, }, }) } /* * 设置访客属性 */ func PostVisitorAttrs(c *gin.Context) { entId, _ := c.Get("ent_id") entIdStr := "" if entId == nil { entIdStr = c.Query("ent_id") } else { entIdStr = entId.(string) } if entIdStr == "" { c.JSON(200, gin.H{ "code": 400, "msg": "ent_id 不能为空", }) return } var param VisitorAttrParams if err := c.BindJSON(¶m); err != nil { c.String(200, err.Error()) } saveVisitorAttr(entIdStr, param) c.JSON(200, gin.H{ "code": 200, "msg": "ok", }) } func saveVisitorAttr(entId string, param VisitorAttrParams) { attrInfo := models.GetVisitorAttrByVisitorId(param.VisitorId, fmt.Sprintf("%v", entId)) if attrInfo.ID == 0 { models.CreateVisitorAttr( fmt.Sprintf("%v", entId), param.VisitorId, param.VisitorAttr.RealName, param.VisitorAttr.Tel, param.VisitorAttr.Email, param.VisitorAttr.QQ, param.VisitorAttr.Wechat, param.VisitorAttr.Comment) } else { var attr = &models.Visitor_attr{ RealName: param.VisitorAttr.RealName, Tel: param.VisitorAttr.Tel, Email: param.VisitorAttr.Email, QQ: param.VisitorAttr.QQ, Wechat: param.VisitorAttr.Wechat, Comment: param.VisitorAttr.Comment, } models.SaveVisitorAttrByVisitorId(attr, param.VisitorId, fmt.Sprintf("%v", entId)) } if param.VisitorAttr.RealName != "" { models.UpdateVisitorRealName(param.VisitorAttr.RealName, fmt.Sprintf("%v", entId), param.VisitorId) go ws.UpdateVisitorName(param.VisitorId, param.VisitorAttr.RealName) } } /* * 设置访客属性 */ func GetVisitorAttr(c *gin.Context) { entId, _ := c.Get("ent_id") visitorId := c.Query("visitor_id") attrInfo := models.GetVisitorAttrByVisitorId(visitorId, fmt.Sprintf("%v", entId)) c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": attrInfo, }) } /* * 设置访客ID */ func PostChangeVisitorId(c *gin.Context) { entId := c.PostForm("ent_id") visitorId := c.PostForm("visitor_id") newId := c.PostForm("new_id") if entId == "" || visitorId == "" || newId == "" { c.JSON(200, gin.H{ "code": types.ApiCode.INVALID, "msg": types.ApiCode.GetMessage(types.ApiCode.INVALID), }) } go models.UpdateVisitorVisitorId(visitorId, newId) go models.UpdateMessageVisitorId(visitorId, newId) go ws.VisitorChangeId(visitorId, newId) c.JSON(200, gin.H{ "code": 200, "msg": "ok", }) } // @Summary 获取客服的在线访客列表接口 // @Produce json // @Success 200 {object} controller.Response // @Failure 200 {object} controller.Response // @Router /visitors_kefu_online [get] func GetKefusVisitorOnlines(c *gin.Context) { kefuName, _ := c.Get("kefu_name") entId, _ := c.Get("ent_id") kefuInfo := models.FindUserByUid(entId) if kefuInfo.Status != 0 && kefuInfo.Status == 1 { c.JSON(200, gin.H{ "code": 400, "msg": "账户禁用", }) return } users := make([]*VisitorOnline, 0) visitorIds := make([]string, 0) clientList := sortMapToSlice(ws.ClientList) for _, visitor := range clientList { if visitor.ToId != kefuName { continue } userInfo := new(VisitorOnline) userInfo.UpdatedAt = visitor.UpdateTime userInfo.VisitorId = visitor.Id userInfo.Username = visitor.Name userInfo.Avator = visitor.Avatar users = append(users, userInfo) visitorIds = append(visitorIds, visitor.Id) } //查询最新消息 messages := models.FindLastMessageMap(visitorIds) //查未读数 unreadMap := models.FindUnreadMessageNumByVisitorIds(visitorIds, "visitor") for _, user := range users { user.LastMessage = messages[user.VisitorId] if user.LastMessage == "" { user.LastMessage = "new visitor" } var unreadNum uint32 if num, ok := unreadMap[user.VisitorId]; ok { unreadNum = num } user.UnreadNum = unreadNum user.LastTime = user.UpdatedAt.Format("2006-01-02 15:04:05") } c.JSON(200, gin.H{ "code": 200, "msg": "ok", "result": users, }) } func DelVisitor(c *gin.Context) { visitorId := c.Query("visitor_id") entIdStr, _ := c.Get("ent_id") entId, _ := strconv.Atoi(entIdStr.(string)) models.DelVisitor("ent_id = ? and visitor_id = ?", entId, visitorId) models.DelMessage("ent_id = ? and visitor_id = ?", entId, visitorId) models.DelVisitorAttr("ent_id = ? and visitor_id = ?", entId, visitorId) c.JSON(200, gin.H{ "code": types.ApiCode.SUCCESS, "msg": types.ApiCode.GetMessage(types.ApiCode.SUCCESS), }) } func sortMapToSlice(youMap map[string]*ws.VisitorConnection) []*ws.VisitorConnection { keys := make([]string, 0) for k, _ := range youMap { keys = append(keys, k) } myMap := make([]*ws.VisitorConnection, 0) sort.Strings(keys) for _, k := range keys { myMap = append(myMap, youMap[k]) } return myMap }