955 lines
28 KiB
Go
955 lines
28 KiB
Go
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
|
||
}
|