fonchain-fiee/pkg/service/asChat/handler.go
2025-06-12 19:31:40 +08:00

567 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}