567 lines
16 KiB
Go
567 lines
16 KiB
Go
// package asChat -----------------------------
|
||
// @file : handler.go
|
||
// @author : JJXu
|
||
// @contact : wavingbear@163.com
|
||
// @time : 2022/10/23 11:13:43
|
||
// -------------------------------------------
|
||
package asChat
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"crypto/md5"
|
||
"encoding/hex"
|
||
"errors"
|
||
"fmt"
|
||
"fonchain-fiee/api/accountFiee"
|
||
"fonchain-fiee/pkg/common/jwt"
|
||
"fonchain-fiee/pkg/common/ws"
|
||
"fonchain-fiee/pkg/e"
|
||
"fonchain-fiee/pkg/service"
|
||
"fonchain-fiee/pkg/service/upload"
|
||
"fonchain-fiee/pkg/utils"
|
||
"fonchain-fiee/pkg/utils/stime"
|
||
"github.com/fonchain/utils/voice"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/gorilla/websocket"
|
||
uuid "github.com/satori/go.uuid"
|
||
"go.uber.org/zap"
|
||
"io"
|
||
"log"
|
||
"path"
|
||
"slices"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
var ChatHandlerIns = ChatHandler{
|
||
cache: ChatCache{newMessageStatExpireAfter: 10 * time.Minute},
|
||
}
|
||
|
||
type ChatHandler struct {
|
||
cache ChatCache
|
||
}
|
||
|
||
func (cr ChatHandler) Connection(c *gin.Context) {
|
||
conn, err := ws.UpGrader.Upgrade(c.Writer, c.Request, nil)
|
||
conn.SetReadDeadline(time.Now().Add(time.Second * 10))
|
||
if err != nil {
|
||
log.Fatal("无法升级为websocket连接", zap.Error(err))
|
||
c.String(500, "无法转为websocket连接")
|
||
return
|
||
}
|
||
defer func() {
|
||
if conn != nil {
|
||
conn.Close()
|
||
}
|
||
}()
|
||
_, byteData, err := conn.ReadMessage()
|
||
if err != nil {
|
||
_ = conn.WriteMessage(websocket.TextMessage, ws.WsErrorConnection("null", err.Error(), "conn.ReadMessag1"))
|
||
return
|
||
}
|
||
fmt.Println("22222222222222,AuthorizationVerify")
|
||
var ok bool
|
||
var userInfo *accountFiee.ChatUserData
|
||
userInfo, ok, err = ws.AuthorizationVerify(byteData)
|
||
if err != nil {
|
||
_ = conn.WriteMessage(websocket.TextMessage, ws.WsErrorConnection("null", err.Error(), "AuthorizationVerify2"))
|
||
return
|
||
}
|
||
if !ok {
|
||
_ = conn.WriteMessage(websocket.TextMessage, ws.WsErrorConnection("null", "登录状态失效", "AuthorizationVerify2.1"))
|
||
return
|
||
}
|
||
fmt.Println("33333333333333,RecountNewMessageTotal")
|
||
conn.SetReadDeadline(time.Time{})
|
||
go cr.cache.RecountNewMessageTotal(userInfo.ID)
|
||
|
||
fmt.Println("44444444444444,ws.NewClient")
|
||
//注册ws客户端,并发送clientId给ws客户端
|
||
var cli = ws.NewClient(userInfo.ID, "", conn, ChatRoom)
|
||
cli.Waiter = userInfo.Role == 2
|
||
fmt.Println("55555555555555,GetUserSession")
|
||
//查询是否有历史的sessionId
|
||
cli.SessionId = cr.cache.GetUserSession(userInfo.ID)
|
||
ChatRoom.Register(cli)
|
||
cr.cache.SaveUserSession(userInfo.ID, cli.SessionId)
|
||
fmt.Println("66666666666666666666666666")
|
||
go cli.WriteWait()
|
||
cli.Send <- WsMessageRegisterCallback(cli.ClientId, cli.SessionId)
|
||
fmt.Println("777777777777777777777777")
|
||
// 处理websocket连接的逻辑
|
||
ctx, _ := context.WithCancel(context.Background())
|
||
cli.Reading(ctx, HandleMessage)
|
||
fmt.Println("88888888888888888888888888")
|
||
select {
|
||
case <-ctx.Done():
|
||
return
|
||
}
|
||
}
|
||
|
||
func (cr ChatHandler) NewMessage(c *gin.Context) {
|
||
var request NewMessageRequest
|
||
if err := c.ShouldBindJSON(&request); err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
if request.SessionId == "" {
|
||
service.Error(c, errors.New("sessionId不能为空"))
|
||
return
|
||
}
|
||
if request.MsgType == 0 {
|
||
service.Error(c, errors.New("msgType不能为空"))
|
||
return
|
||
}
|
||
fmt.Println("NewMessage 1111111111111111111111111111111")
|
||
//获取用户信息
|
||
chatUser, code := jwt.ParseToChatUser(c)
|
||
if code != 0 {
|
||
service.ErrWithCode(c, code)
|
||
return
|
||
}
|
||
fmt.Println("NewMessage 22222222222222222222222222222222222")
|
||
//存储入库
|
||
if chatUser.NickName != "" {
|
||
chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID)
|
||
}
|
||
fmt.Println("NewMessage 3333333333333333333333333333333333")
|
||
var data = accountFiee.ChatRecordData{
|
||
SessionId: request.SessionId,
|
||
UserId: chatUser.ID,
|
||
Name: chatUser.NickName,
|
||
Avatar: "",
|
||
MsgType: request.MsgType,
|
||
Content: request.Message.Text,
|
||
LocalStamp: request.LocalStamp,
|
||
Medias: nil,
|
||
}
|
||
if len(request.Message.Media) > 0 {
|
||
for _, media := range request.Message.Media {
|
||
data.Medias = append(data.Medias, &accountFiee.ChatMediaData{
|
||
ID: media.MediaId,
|
||
})
|
||
}
|
||
}
|
||
fmt.Println("NewMessage 4444444444444444444444444444444444")
|
||
resp, err := service.AccountFieeProvider.CreateChatRecord(c, &data)
|
||
if err != nil {
|
||
service.Error(c, errors.New("创建失败"))
|
||
return
|
||
}
|
||
fmt.Printf("CreateChatRecord resp:%+v\n", resp)
|
||
//录入缓存
|
||
err = cr.cache.AddChatRecord(request.SessionId, resp.Data)
|
||
if err != nil {
|
||
service.Error(c, errors.New("创建失败"))
|
||
return
|
||
}
|
||
fmt.Println("NewMessage 5 消息数量+1")
|
||
//新消息数量统计+1
|
||
noticeUserId := ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
|
||
fmt.Println("NewMessage 5.1 消息数量配置结束")
|
||
fmt.Printf("noticeUserId %+v\n", noticeUserId)
|
||
for _, userId := range noticeUserId {
|
||
fmt.Println("userId")
|
||
cr.cache.IncreaseNewMessageTotal(userId, request.SessionId)
|
||
}
|
||
fmt.Println("NewMessage 6")
|
||
//发送websocket消息提醒通知
|
||
var notice = MessageListType{}
|
||
notice.BuildMessage(resp.Data)
|
||
fmt.Printf("ws消息提醒:%+v\n", notice)
|
||
_, err = ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
|
||
if err != nil {
|
||
log.Fatal("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
|
||
}
|
||
fmt.Println("NewMessage 7 -end")
|
||
//发送app推送(无横幅推送)
|
||
//go func() {
|
||
// omitMessage := ""
|
||
// switch request.MsgType {
|
||
// case accountFiee.MsgType_TextMsgType:
|
||
// runMsg := []rune(request.Text)
|
||
// if len(runMsg) > 15 {
|
||
// omitMessage = string(runMsg[:15]) + "..."
|
||
// } else {
|
||
// omitMessage = request.Text
|
||
// }
|
||
// case accountFiee.MsgType_ImageMsgType:
|
||
// omitMessage = "[图片]"
|
||
// case accountFiee.MsgType_AudioMsgType:
|
||
// omitMessage = "[音频]"
|
||
// case accountFiee.MsgType_VideoMsgType:
|
||
// omitMessage = "[视频]"
|
||
// default:
|
||
// omitMessage = "新消息请查收"
|
||
// }
|
||
// for _, userId := range noticeUserId {
|
||
// _ = asPusher.NewArtistinfoUniPush().NewChatMessageNotice(userId, omitMessage)
|
||
// }
|
||
//}()
|
||
service.Success(c)
|
||
}
|
||
|
||
func (cr ChatHandler) MessageList(c *gin.Context) {
|
||
var request MessageListRequest
|
||
if err := c.ShouldBindJSON(&request); err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
domain := c.GetHeader("domain")
|
||
if (request.Direction == 0 && request.Recent == false) || (request.Direction > 0 && request.Recent == true) {
|
||
service.Error(c, errors.New("组合条件校验失败"))
|
||
return
|
||
}
|
||
if request.SessionId == "" {
|
||
service.Error(c, errors.New("sessionId不能为空"))
|
||
return
|
||
}
|
||
if request.PageSize < -1 {
|
||
service.Error(c, errors.New("pageSize校验错误"))
|
||
return
|
||
}
|
||
var resp = make([]*MessageListType, 0)
|
||
if request.CurrentId == 0 && request.Direction == 1 {
|
||
service.Success(c, resp)
|
||
return
|
||
}
|
||
chatUser, code := jwt.ParseToChatUser(c)
|
||
if code != 0 {
|
||
service.ErrWithCode(c, code)
|
||
return
|
||
}
|
||
//if request.SessionId == "" {
|
||
// request.SessionId = cr.cache.GetUserSession(tokenResult.UserInfo.ID)
|
||
// if request.SessionId == "" {
|
||
// service.Success(c, resp)
|
||
// return
|
||
// }
|
||
//}
|
||
//messages := cr.cache.GetChatRecord(request.SessionId)
|
||
messages := []*accountFiee.ChatRecordData{}
|
||
var returnDataIdList = make([]int64, 0)
|
||
defer func() {
|
||
//获取最新数据时,重置新消息数量统计
|
||
if request.Direction == 2 || request.Recent {
|
||
cr.cache.ResetNewMessageTotal(chatUser.ID, request.SessionId)
|
||
}
|
||
//设置消息已被客服阅读,当客服重新通过通过websocket连接时,这些消息将不被纳入新消息数量统计
|
||
if len(returnDataIdList) > 0 && domain == "fontree" {
|
||
for _, hasReadId := range returnDataIdList {
|
||
for i, message := range messages {
|
||
if message.ID == hasReadId {
|
||
messages[i].WaiterRead = 1
|
||
}
|
||
}
|
||
}
|
||
err := cr.cache.CoverChatRecord(request.SessionId, messages)
|
||
if err != nil {
|
||
log.Fatal("设置消息已读失败", zap.Error(err))
|
||
}
|
||
for _, v := range messages {
|
||
_, err = service.AccountFieeProvider.SaveChatRecord(context.Background(), v)
|
||
if err != nil {
|
||
log.Fatal("设置消息已读失败", zap.Error(err))
|
||
}
|
||
}
|
||
}
|
||
}()
|
||
if len(messages) == 0 {
|
||
//从数据库获取
|
||
recordResp, err := service.AccountFieeProvider.GetChatRecordList(c, &accountFiee.GetChatRecordListRequest{
|
||
Query: &accountFiee.ChatRecordData{SessionId: request.SessionId},
|
||
Page: 1,
|
||
PageSize: -1,
|
||
//Where: fmt.Sprintf("id %s %d", utils.IfGec(request.Direction == 1, "<", ">"), request.CurrentId),
|
||
})
|
||
if err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
messages = recordResp.List
|
||
err = cr.cache.CoverChatRecord(request.SessionId, messages)
|
||
if err != nil {
|
||
log.Fatal("覆盖聊天记录失败", zap.Error(err))
|
||
}
|
||
}
|
||
if request.Recent {
|
||
if int64(len(messages)) >= request.PageSize {
|
||
messages = messages[len(messages)-int(request.PageSize):]
|
||
}
|
||
var now = time.Now()
|
||
for _, message := range messages {
|
||
if request.InHour > 0 {
|
||
messageCreatedAt, _ := stime.StringToTime(message.CreatedAt)
|
||
if now.Sub(*messageCreatedAt) >= request.InHour*time.Hour {
|
||
continue
|
||
}
|
||
}
|
||
returnDataIdList = append(returnDataIdList, message.ID)
|
||
var msg = &MessageListType{}
|
||
msg.BuildMessage(message)
|
||
resp = append(resp, msg)
|
||
}
|
||
} else {
|
||
sort.Slice(messages, func(i, j int) bool {
|
||
if request.Direction == 1 {
|
||
return messages[i].ID < messages[j].ID
|
||
} else {
|
||
return messages[i].ID > messages[j].ID
|
||
}
|
||
})
|
||
fmt.Printf("data is %+v\n", messages)
|
||
total := 0
|
||
for i, message := range messages {
|
||
switch request.Direction {
|
||
case 1: //向下查找,找比CurrentId大的数据
|
||
if message.ID <= request.CurrentId {
|
||
continue
|
||
}
|
||
case 2: //向上查找,找比CurrentId小的数据
|
||
if message.ID >= request.CurrentId {
|
||
continue
|
||
}
|
||
}
|
||
message := message
|
||
fmt.Println(i, message.ID)
|
||
if request.PageSize != -1 && int64(total+1) > request.PageSize {
|
||
continue
|
||
}
|
||
total++
|
||
returnDataIdList = append(returnDataIdList, message.ID)
|
||
var msg = &MessageListType{}
|
||
msg.BuildMessage(message)
|
||
resp = append(resp, msg)
|
||
}
|
||
}
|
||
//二次排序
|
||
sort.Slice(resp, func(i, j int) bool {
|
||
return resp[i].ID < resp[j].ID
|
||
})
|
||
//优化空列表
|
||
for i, v := range resp {
|
||
if v.Message.Media == nil {
|
||
resp[i].Message.Media = []MessageMedia{}
|
||
}
|
||
}
|
||
service.Success(c, resp)
|
||
}
|
||
|
||
func (cr ChatHandler) Upload(c *gin.Context) {
|
||
fmt.Println("111111111111")
|
||
//获取用户信息
|
||
chatUser, code := jwt.ParseToChatUser(c)
|
||
if code != 0 {
|
||
service.ErrWithCode(c, code)
|
||
return
|
||
}
|
||
//获取文件对象
|
||
file, err := c.FormFile("file")
|
||
if err != nil {
|
||
log.Fatal("ERROR: upload file failed. ", zap.Error(err))
|
||
return
|
||
}
|
||
duration := c.PostForm("duration")
|
||
fmt.Println(duration)
|
||
ext := c.PostForm("ext")
|
||
fileExt := strings.ToLower(path.Ext(file.Filename))
|
||
if ext != "" {
|
||
fileExt = ext
|
||
}
|
||
fileType := e.DetectFileTypeByExtension(fileExt)
|
||
if fileType == e.Audio {
|
||
if !slices.Contains([]string{".mp4", ".aac", ".mp3", ".opus", ".wav"}, fileExt) {
|
||
service.Error(c, errors.New("不支持的格式"))
|
||
return
|
||
}
|
||
}
|
||
//计算md5
|
||
tmp, err := file.Open()
|
||
if err != nil {
|
||
service.Error(c, errors.New("上传失败"))
|
||
return
|
||
}
|
||
fileContent, err := io.ReadAll(tmp)
|
||
if err != nil {
|
||
service.Error(c, errors.New("文件读取失败"))
|
||
return
|
||
}
|
||
hash := md5.New()
|
||
_, err = hash.Write(fileContent)
|
||
if err != nil {
|
||
service.Error(c, errors.New("文件读取失败"))
|
||
return
|
||
}
|
||
md5Bytes := hash.Sum(nil) // 获取 MD5 字节切片
|
||
md5String := hex.EncodeToString(md5Bytes) // 转换为十六进制字符串表示
|
||
//检查文件是否存在
|
||
checkResp, err := service.AccountFieeProvider.GetChatMediaList(c, &accountFiee.GetChatMediaListRequest{Query: &accountFiee.ChatMediaData{Md5: md5String}, Page: 1, PageSize: 1})
|
||
if err != nil {
|
||
log.Fatal("md5查询附件失败", zap.Error(err))
|
||
}
|
||
if checkResp.Total > 0 {
|
||
service.Success(c, checkResp.List[0])
|
||
return
|
||
}
|
||
|
||
//文件不存在则上传文件
|
||
filename, _ := uuid.NewV4()
|
||
defer tmp.Close()
|
||
fileBuffer := bytes.NewBuffer(fileContent)
|
||
var bosUrl string
|
||
bosUrl, err = upload.UploadWithBuffer(fileBuffer, fmt.Sprintf("%d/%v%v", chatUser.ID, filename, fileExt))
|
||
if err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
//存到数据库
|
||
var durationInt64, _ = strconv.ParseInt(duration, 10, 64)
|
||
var mediaData = accountFiee.ChatMediaData{
|
||
Url: bosUrl,
|
||
Md5: md5String,
|
||
Size: fmt.Sprintf("%d", file.Size),
|
||
Ext: fileExt,
|
||
Duration: durationInt64,
|
||
}
|
||
resp, err := service.AccountFieeProvider.CreateChatMedia(c, &mediaData)
|
||
if err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
service.Success(c, resp.Data)
|
||
}
|
||
|
||
func (cr ChatHandler) UserMessageStat(c *gin.Context) {
|
||
//获取用户信息
|
||
chatUser, code := jwt.ParseToChatUser(c)
|
||
if code != 0 {
|
||
service.ErrWithCode(c, code)
|
||
return
|
||
}
|
||
result := cr.cache.GetNewMessageStat(c, chatUser.ID)
|
||
if len(result) == 0 {
|
||
service.Success(c, result)
|
||
return
|
||
}
|
||
fmt.Printf("cache stat:%+v\n", result)
|
||
//获取实名信息
|
||
var protoReq = accountFiee.GetChatUserListRequest2{
|
||
Page: 1,
|
||
PageSize: int64(len(result)),
|
||
}
|
||
for i, item := range result {
|
||
if item.UserId == 0 {
|
||
sessionId, _ := strconv.Atoi(item.SessionId)
|
||
item.UserId = int64(sessionId)
|
||
result[i].UserId = int64(sessionId)
|
||
}
|
||
protoReq.UserIdIn = append(protoReq.UserIdIn, item.UserId)
|
||
}
|
||
fmt.Printf("protoReq.UserIdIn:%+v\n", protoReq.UserIdIn)
|
||
listRes, err := service.AccountFieeProvider.GetChatUserList2(c, &protoReq)
|
||
if err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
fmt.Printf("GetChatUserList:%+v\n", listRes)
|
||
for i, item := range result {
|
||
for _, user := range listRes.List {
|
||
if item.UserId == user.UserId {
|
||
user := user
|
||
result[i].Name = user.Name
|
||
//result[i].ArtistUid = user.ArtistUid
|
||
break
|
||
}
|
||
}
|
||
if result[i].Name == "" {
|
||
result[i].Name = beautifulZeroName(result[i].Name, result[i].UserId)
|
||
}
|
||
}
|
||
reverse(result)
|
||
service.Success(c, result)
|
||
}
|
||
func reverse(slice []UserMsgStatic) {
|
||
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
|
||
slice[i], slice[j] = slice[j], slice[i]
|
||
}
|
||
}
|
||
func (cr ChatHandler) VoiceToText(c *gin.Context) {
|
||
var req VoiceToTextRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
detail, err := service.AccountFieeProvider.GetChatMediaDetail(c, &accountFiee.GetChatMediaByIdRequest{Id: req.MediaId})
|
||
if err != nil {
|
||
service.Error(c, err)
|
||
return
|
||
}
|
||
if detail.ConvText != "" {
|
||
service.Success(c, map[string]string{"convText": detail.ConvText})
|
||
return
|
||
}
|
||
voiceApi := voice.NewVoiceApi()
|
||
detail.ConvText, err = voiceApi.ToTextFromUrl(detail.Url)
|
||
if err != nil {
|
||
service.Error(c, errors.New("语音转文字失败"))
|
||
return
|
||
}
|
||
defer func() {
|
||
service.AccountFieeProvider.UpdateChatMedia(context.Background(), detail)
|
||
}()
|
||
service.Success(c, map[string]string{"convText": detail.ConvText})
|
||
}
|
||
|
||
//func (cr ChatHandler) ArtistDetail(c *gin.Context) {
|
||
// var req ArtistInfoRequest
|
||
// if err := c.ShouldBindJSON(&req); err != nil {
|
||
// service.Error(c, err)
|
||
// return
|
||
// }
|
||
// if req.UserId == 0 {
|
||
// service.Success(c, ArtistInfo{})
|
||
// return
|
||
// }
|
||
// detail, err := service.GrpcArtistInfoUserImpl.FindUsersUserView(c, &artistInfoUser.FindUsersRequest{UserId: req.UserId})
|
||
// if err != nil {
|
||
// service.Error(c, err)
|
||
// return
|
||
// }
|
||
// var (
|
||
// tnum string
|
||
// artistName string
|
||
// age int64
|
||
// sex string
|
||
// nativePlace string
|
||
// telNum string
|
||
// recentPhoto string
|
||
// )
|
||
// if len(detail.Data) > 0 {
|
||
// tnum = detail.Data[0].Tnum
|
||
// artistName = beautifulZeroName(detail.Data[0].RealName, req.UserId)
|
||
// age = detail.Data[0].Age
|
||
// sex = detail.Data[0].Sex
|
||
// nativePlace = detail.Data[0].NativePlace
|
||
// telNum = detail.Data[0].TelNum
|
||
// recentPhoto = detail.Data[0].Photo
|
||
// }
|
||
// resp := ArtistInfo{
|
||
// Tnum: tnum,
|
||
// ArtistName: artistName,
|
||
// Age: age,
|
||
// Sex: sex,
|
||
// NativePlace: nativePlace,
|
||
// TelNum: telNum,
|
||
// RecentPhoto: recentPhoto,
|
||
// }
|
||
// service.Success(c, resp)
|
||
//}
|
||
|
||
// 对没有名字的name进行优化
|
||
func beautifulZeroName(name string, userId int64) string {
|
||
return utils.IfGec(name == "", fmt.Sprintf("未实名用户:%d", userId), name)
|
||
}
|