diff --git a/go.mod b/go.mod index d0c3adfa6..32004bd72 100644 --- a/go.mod +++ b/go.mod @@ -176,4 +176,4 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect ) -replace github.com/OpenIMSDK/protocol v0.0.31 => github.com/luhaoling/protocol v0.0.0-20231201032120-22bebe035ae3 +replace github.com/OpenIMSDK/protocol v0.0.31 => github.com/luhaoling/protocol v0.0.0-20231208093819-b7b045d52e56 diff --git a/go.sum b/go.sum index 27304cb72..79ed0da1b 100644 --- a/go.sum +++ b/go.sum @@ -274,8 +274,8 @@ github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205Ah github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w= github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= -github.com/luhaoling/protocol v0.0.0-20231201032120-22bebe035ae3 h1:tWCCSHzacKfJ1v63rNu65fVLytsFV7rCdI+EjwwZmx8= -github.com/luhaoling/protocol v0.0.0-20231201032120-22bebe035ae3/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/luhaoling/protocol v0.0.0-20231208093819-b7b045d52e56 h1:Fwvjj84aTcb+1ELt8HNSqVgWJYCJ6+1ui7yH0dTkVoQ= +github.com/luhaoling/protocol v0.0.0-20231208093819-b7b045d52e56/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= diff --git a/internal/api/msg.go b/internal/api/msg.go index 3f9dc2fdb..c06c66654 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -125,8 +125,8 @@ func (m *MessageApi) GetConversationsHasReadAndMaxSeq(c *gin.Context) { a2r.Call(msg.MsgClient.GetConversationsHasReadAndMaxSeq, m.Client, c) } -func (m *MessageApi) GetConversationsUnreadSeqAndMaxSeq(c *gin.Context) { - a2r.Call(msg.MsgClient.GetConversationsUnreadSeqAndMaxSeq, m.Client, c) +func (m *MessageApi) GetConversationsList(c *gin.Context) { + a2r.Call(msg.MsgClient.GetConversationList, m.Client, c) } func (m *MessageApi) SetConversationHasReadSeq(c *gin.Context) { diff --git a/internal/api/route.go b/internal/api/route.go index 360c42828..44fe344a9 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -175,7 +175,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive msgGroup.POST("/mark_msgs_as_read", m.MarkMsgsAsRead) msgGroup.POST("/mark_conversation_as_read", m.MarkConversationAsRead) msgGroup.POST("/get_conversations_has_read_and_max_seq", m.GetConversationsHasReadAndMaxSeq) - msgGroup.POST("/get_conversations_unread_seq_and_max_seq", m.GetConversationsUnreadSeqAndMaxSeq) + msgGroup.POST("/get_conversations_list", m.GetConversationsList) msgGroup.POST("/set_conversation_has_read_seq", m.SetConversationHasReadSeq) msgGroup.POST("/clear_conversation_msg", m.ClearConversationsMsg) diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 6e6b6d377..fded8802d 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -16,7 +16,6 @@ package friend import ( "context" - "github.com/OpenIMSDK/protocol/sdkws" "github.com/openimsdk/open-im-server/v3/pkg/authverify" @@ -446,3 +445,7 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien } return resp, nil } + +func (s *friendServer) PinFriends(ctx context.Context, req *pbfriend.PinFriendsReq) (*pbfriend.PinFriendsResp, error) { + return nil, nil +} diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index ff4e979b3..ce156f75e 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -17,6 +17,7 @@ package msg import ( "context" cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" + "sort" utils2 "github.com/OpenIMSDK/tools/utils" @@ -71,7 +72,7 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m return resp, nil } -func (m *msgServer) GetConversationsUnreadSeqAndMaxSeq(ctx context.Context, req *msg.GetConversationsUnreadSeqAndMaxSeqReq) (resp *msg.GetConversationsUnreadSeqAndMaxSeqResp, err error) { +func (m *msgServer) GetConversationList(ctx context.Context, req *msg.GetConversationListReq) (resp *msg.GetConversationListResp, err error) { var conversationIDs []string if len(req.ConversationIDs) == 0 { conversationIDs, err = m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID) @@ -81,38 +82,56 @@ func (m *msgServer) GetConversationsUnreadSeqAndMaxSeq(ctx context.Context, req } else { conversationIDs = req.ConversationIDs } - hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs) + + conversations, err := m.Conversation.GetConversations(ctx, req.UserID, conversationIDs) if err != nil { return nil, err } - conversations, err := m.Conversation.GetConversations(ctx, req.UserID, conversationIDs) + + maxSeqs, err := m.MsgDatabase.GetMaxSeqs(ctx, conversationIDs) if err != nil { return nil, err } - conversationMaxSeqMap := make(map[string]int64) - for _, conversation := range conversations { - if conversation.MaxSeq != 0 { - conversationMaxSeqMap[conversation.ConversationID] = conversation.MaxSeq - } + + chatLogs, err := m.MsgDatabase.FindOneByDocIDs(ctx, conversationIDs, maxSeqs) + if err != nil { + return nil, err } - maxSeqs, err := m.MsgDatabase.GetMaxSeqs(ctx, conversationIDs) + + conversationMsg, err := m.getConversationInfo(ctx, chatLogs, req.UserID) if err != nil { return nil, err } - resp = &msg.GetConversationsUnreadSeqAndMaxSeqResp{Seqs: make(map[string]*msg.UnreadSeqs)} - for conversarionID, maxSeq := range maxSeqs { - resp.Seqs[conversarionID] = &msg.UnreadSeqs{ -<<<<<<< HEAD - UnreadSeq: maxSeq - hasReadSeqs[conversarionID], -======= - UnreadSeq: hasReadSeqs[conversarionID], ->>>>>>> c13411da69a6457ba810c071a5d7f462eaa1531e - MaxSeq: maxSeq, - } - if v, ok := conversationMaxSeqMap[conversarionID]; ok { - resp.Seqs[conversarionID].MaxSeq = v + + hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs) + if err != nil { + return nil, err + } + + conversation_unreadCount := make(map[string]int64) + for conversationID, maxSeq := range maxSeqs { + conversation_unreadCount[conversationID] = maxSeq - hasReadSeqs[conversationID] + } + + conversation_isPinkTime := make(map[int64]string) + conversation_notPinkTime := make(map[int64]string) + for _, v := range conversations { + conversationID := v.ConversationID + time := conversationMsg[conversationID].MsgInfo.LatestMsgRecvTime + conversationMsg[conversationID].RecvMsgOpt = v.RecvMsgOpt + if v.IsPinned { + conversationMsg[conversationID].IsPinned = v.IsPinned + conversation_isPinkTime[time] = conversationID + continue } + conversation_notPinkTime[time] = conversationID } + resp = &msg.GetConversationListResp{ + ConversationElems: []*msg.ConversationElem{}, + } + + m.conversationSort(conversation_isPinkTime, resp, conversation_unreadCount, conversationMsg) + m.conversationSort(conversation_notPinkTime, resp, conversation_unreadCount, conversationMsg) return resp, nil } @@ -254,3 +273,100 @@ func (m *msgServer) sendMarkAsReadNotification( } return nil } + +func (m *msgServer) conversationSort( + conversations map[int64]string, + resp *msg.GetConversationListResp, + conversation_unreadCount map[string]int64, + conversationMsg map[string]*msg.ConversationElem, +) { + keys := []int64{} + for key := range conversations { + keys = append(keys, key) + } + + sort.Slice(keys[:], func(i, j int) bool { + return keys[i] > keys[j] + }) + index := 0 + + cons := make([]*msg.ConversationElem, len(conversations)) + for _, v := range keys { + conversationID := conversations[v] + conversationElem := conversationMsg[conversationID] + conversationElem.UnreadCount = conversation_unreadCount[conversationID] + cons[index] = conversationElem + index++ + } + resp.ConversationElems = append(resp.ConversationElems, cons...) +} + +func (m *msgServer) getConversationInfo( + ctx context.Context, + chatLogs map[string]*sdkws.MsgData, + userID string) (map[string]*msg.ConversationElem, error) { + var ( + sendIDs []string + groupIDs []string + sendMap = make(map[string]*sdkws.UserInfo) + groupMap = make(map[string]*sdkws.GroupInfo) + conversationMsg = make(map[string]*msg.ConversationElem) + ) + for _, chatLog := range chatLogs { + switch chatLog.SessionType { + case constant.SingleChatType: + if chatLog.SendID == userID { + sendIDs = append(sendIDs, chatLog.RecvID) + } + sendIDs = append(sendIDs, chatLog.SendID) + case constant.GroupChatType, constant.SuperGroupChatType: + groupIDs = append(groupIDs, chatLog.GroupID) + sendIDs = append(sendIDs, chatLog.SendID) + } + } + if len(sendIDs) != 0 { + sendInfos, err := m.User.GetUsersInfo(ctx, sendIDs) + if err != nil { + return nil, err + } + for _, sendInfo := range sendInfos { + sendMap[sendInfo.UserID] = sendInfo + } + } + if len(groupIDs) != 0 { + groupInfos, err := m.Group.GetGroupInfos(ctx, groupIDs, false) + if err != nil { + return nil, err + } + for _, groupInfo := range groupInfos { + groupMap[groupInfo.GroupID] = groupInfo + } + } + for conversationID, chatLog := range chatLogs { + pbchatLog := &msg.ConversationElem{} + msgInfo := &msg.MsgInfo{} + utils2.CopyStructFields(msgInfo, chatLog) + switch chatLog.SessionType { + case constant.SingleChatType: + if chatLog.SendID == userID { + msgInfo.FaceURL = sendMap[chatLog.RecvID].FaceURL + msgInfo.SenderName = sendMap[chatLog.RecvID].Nickname + break + } + msgInfo.FaceURL = sendMap[chatLog.SendID].FaceURL + msgInfo.SenderName = sendMap[chatLog.SendID].Nickname + case constant.GroupChatType, constant.SuperGroupChatType: + msgInfo.GroupName = groupMap[chatLog.GroupID].GroupName + msgInfo.GroupFaceURL = groupMap[chatLog.GroupID].FaceURL + msgInfo.GroupMemberCount = groupMap[chatLog.GroupID].MemberCount + msgInfo.GroupID = chatLog.GroupID + msgInfo.GroupType = groupMap[chatLog.GroupID].GroupType + msgInfo.SenderName = sendMap[chatLog.SendID].Nickname + } + pbchatLog.ConversationID = conversationID + msgInfo.LatestMsgRecvTime = chatLog.SendTime + pbchatLog.MsgInfo = msgInfo + conversationMsg[conversationID] = pbchatLog + } + return conversationMsg, nil +} diff --git a/pkg/apistruct/manage.go b/pkg/apistruct/manage.go index 1e0ab3214..e57151dd9 100644 --- a/pkg/apistruct/manage.go +++ b/pkg/apistruct/manage.go @@ -64,6 +64,30 @@ type SendMsgReq struct { SendMsg } +type GetConversationListReq struct { + // userID uniquely identifies the user. + UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID,omitempty" binding:"required"` + + // ConversationIDs contains a list of unique identifiers for conversations. + ConversationIDs []string `protobuf:"bytes,2,rep,name=conversationIDs,proto3" json:"conversationIDs,omitempty"` +} + +type GetConversationListResp struct { + // ConversationElems is a map that associates conversation IDs with their respective details. + ConversationElems map[string]*ConversationElem `protobuf:"bytes,1,rep,name=conversationElems,proto3" json:"conversationElems,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +type ConversationElem struct { + // MaxSeq represents the maximum sequence number within the conversation. + MaxSeq int64 `protobuf:"varint,1,opt,name=maxSeq,proto3" json:"maxSeq,omitempty"` + + // UnreadSeq represents the number of unread messages in the conversation. + UnreadSeq int64 `protobuf:"varint,2,opt,name=unreadSeq,proto3" json:"unreadSeq,omitempty"` + + // LastSeqTime represents the timestamp of the last sequence in the conversation. + LastSeqTime int64 `protobuf:"varint,3,opt,name=LastSeqTime,proto3" json:"LastSeqTime,omitempty"` +} + // BatchSendMsgReq defines the structure for sending a message to multiple recipients. type BatchSendMsgReq struct { SendMsg diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index cba0a6bbd..8b734cebe 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "time" "github.com/OpenIMSDK/protocol/constant" @@ -98,6 +99,7 @@ type CommonMsgDatabase interface { SetSendMsgStatus(ctx context.Context, id string, status int32) error GetSendMsgStatus(ctx context.Context, id string) (int32, error) SearchMessage(ctx context.Context, req *pbmsg.SearchMessageReq) (total int32, msgData []*sdkws.MsgData, err error) + FindOneByDocIDs(ctx context.Context, docIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) // to mq MsgToMQ(ctx context.Context, key string, msg2mq *sdkws.MsgData) error @@ -1047,6 +1049,23 @@ func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbmsg.Searc return total, totalMsgs, nil } +func (db *commonMsgDatabase) FindOneByDocIDs(ctx context.Context, conversationIDs []string, seqs map[string]int64) (map[string]*sdkws.MsgData, error) { + totalMsgs := make(map[string]*sdkws.MsgData) + for _, conversationID := range conversationIDs { + len := seqs[conversationID] + seq := len / 100 + index := (len % 100) - 1 + + docID := conversationID + ":" + fmt.Sprintf("%d", seq) + msgs, err := db.msgDocDatabase.FindOneByDocID(ctx, docID) + if err != nil { + return nil, err + } + totalMsgs[conversationID] = convert.MsgDB2Pb(msgs.Msg[index].Msg) + } + return totalMsgs, nil +} + func (db *commonMsgDatabase) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) { db.msgDocDatabase.ConvertMsgsDocLen(ctx, conversationIDs) }