You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

245 lines
9.3 KiB

// Copyright © 2023 OpenIM SDK. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package conversation_msg
import (
"context"
"github.com/openimsdk/openim-sdk-core/v3/internal/util"
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
"github.com/jinzhu/copier"
pbMsg "github.com/openimsdk/protocol/msg"
"github.com/openimsdk/protocol/sdkws"
"github.com/openimsdk/tools/log"
)
// Delete the local and server
// Delete the local, do not change the server data
// To delete the server, you need to change the local message status to delete
func (c *Conversation) clearConversationFromLocalAndSvr(ctx context.Context, conversationID string, f func(ctx context.Context, conversationID string) error) error {
_, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
return err
}
// Use conversationID to remove conversations and messages from the server first
err = c.clearConversationMsgFromSvr(ctx, conversationID)
if err != nil {
return err
}
if err := c.clearConversationAndDeleteAllMsg(ctx, conversationID, false, f); err != nil {
return err
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{conversationID}}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
return nil
}
func (c *Conversation) clearConversationAndDeleteAllMsg(ctx context.Context, conversationID string, markDelete bool, f func(ctx context.Context, conversationID string) error) error {
err := c.getConversationMaxSeqAndSetHasRead(ctx, conversationID)
if err != nil {
return err
}
if markDelete {
err = c.db.MarkDeleteConversationAllMessages(ctx, conversationID)
} else {
err = c.db.DeleteConversationAllMessages(ctx, conversationID)
}
if err != nil {
return err
}
log.ZDebug(ctx, "reset conversation", "conversationID", conversationID)
err = f(ctx, conversationID)
if err != nil {
return err
}
return nil
}
// To delete session information, delete the server first, and then invoke the interface.
// The client receives a callback to delete all local information.
func (c *Conversation) clearConversationMsgFromSvr(ctx context.Context, conversationID string) error {
var apiReq pbMsg.ClearConversationsMsgReq
apiReq.UserID = c.loginUserID
apiReq.ConversationIDs = []string{conversationID}
return util.ApiPost(ctx, constant.ClearConversationMsgRouter, &apiReq, nil)
}
// Delete all messages
func (c *Conversation) deleteAllMsgFromLocalAndSvr(ctx context.Context) error {
// Delete the server first (high error rate), then delete it.
err := c.deleteAllMessageFromSvr(ctx)
if err != nil {
return err
}
err = c.deleteAllMsgFromLocal(ctx, false)
if err != nil {
return err
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
return nil
}
// Delete all server messages
func (c *Conversation) deleteAllMessageFromSvr(ctx context.Context) error {
var apiReq pbMsg.UserClearAllMsgReq
apiReq.UserID = c.loginUserID
err := util.ApiPost(ctx, constant.ClearAllMsgRouter, &apiReq, nil)
if err != nil {
return err
}
return nil
}
// Delete all messages from the local
func (c *Conversation) deleteAllMsgFromLocal(ctx context.Context, markDelete bool) error {
conversations, err := c.db.GetAllConversationListDB(ctx)
if err != nil {
return err
}
var successCids []string
log.ZDebug(ctx, "deleteAllMsgFromLocal", "conversations", conversations, "markDelete", markDelete)
for _, v := range conversations {
if err := c.clearConversationAndDeleteAllMsg(ctx, v.ConversationID, markDelete, c.db.ClearConversation); err != nil {
log.ZError(ctx, "clearConversation err", err, "conversationID", v.ConversationID)
continue
}
successCids = append(successCids, v.ConversationID)
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: successCids}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
return nil
}
// Delete a message from the local
func (c *Conversation) deleteMessage(ctx context.Context, conversationID string, clientMsgID string) error {
if err := c.deleteMessageFromSvr(ctx, conversationID, clientMsgID); err != nil {
return err
}
return c.deleteMessageFromLocal(ctx, conversationID, clientMsgID)
}
// The user deletes part of the message from the server
func (c *Conversation) deleteMessageFromSvr(ctx context.Context, conversationID string, clientMsgID string) error {
_, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
if err != nil {
return err
}
localMessage, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
if err != nil {
return err
}
if localMessage.Status == constant.MsgStatusSendFailed {
log.ZInfo(ctx, "delete msg status is send failed, do not need delete", "msg", localMessage)
return nil
}
if localMessage.Seq == 0 {
log.ZInfo(ctx, "delete msg seq is 0, try again", "msg", localMessage)
return sdkerrs.ErrMsgHasNoSeq
}
var apiReq pbMsg.DeleteMsgsReq
apiReq.UserID = c.loginUserID
apiReq.Seqs = []int64{localMessage.Seq}
apiReq.ConversationID = conversationID
return util.ApiPost(ctx, constant.DeleteMsgsRouter, &apiReq, nil)
}
// Delete messages from local
func (c *Conversation) deleteMessageFromLocal(ctx context.Context, conversationID string, clientMsgID string) error {
s, err := c.db.GetMessage(ctx, conversationID, clientMsgID)
if err != nil {
return err
}
if err := c.db.DeleteConversationMsgs(ctx, conversationID, []string{clientMsgID}); err != nil {
return err
}
if !s.IsRead && s.SendID != c.loginUserID {
if err := c.db.DecrConversationUnreadCount(ctx, conversationID, 1); err != nil {
return err
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{ConID: conversationID, Action: constant.ConChange, Args: []string{conversationID}}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
}
conversation, err := c.db.GetConversation(ctx, conversationID)
if err != nil {
return err
}
var latestMsg sdk_struct.MsgStruct
utils.JsonStringToStruct(conversation.LatestMsg, &latestMsg)
if latestMsg.ClientMsgID == clientMsgID {
log.ZDebug(ctx, "latesetMsg deleted", "seq", latestMsg.Seq, "clientMsgID", latestMsg.ClientMsgID)
msgs, err := c.db.GetMessageListNoTime(ctx, conversationID, 1, false)
if err != nil {
return err
}
latestMsgSendTime := latestMsg.SendTime
latestMsgStr := ""
if len(msgs) > 0 {
copier.Copy(&latestMsg, msgs[0])
err := c.msgConvert(&latestMsg)
if err != nil {
log.ZError(ctx, "parsing data error", err, latestMsg)
}
latestMsgStr = utils.StructToJsonString(latestMsg)
latestMsgSendTime = latestMsg.SendTime
}
if err := c.db.UpdateColumnsConversation(ctx, conversationID, map[string]interface{}{"latest_msg": latestMsgStr, "latest_msg_send_time": latestMsgSendTime}); err != nil {
return err
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: []string{conversationID}}})
}
c.msgListener().OnMsgDeleted(utils.StructToJsonString(s))
return nil
}
func (c *Conversation) doDeleteMsgs(ctx context.Context, msg *sdkws.MsgData) {
tips := sdkws.DeleteMsgsTips{}
utils.UnmarshalNotificationElem(msg.Content, &tips)
log.ZDebug(ctx, "doDeleteMsgs", "seqs", tips.Seqs)
for _, v := range tips.Seqs {
msg, err := c.db.GetMessageBySeq(ctx, tips.ConversationID, v)
if err != nil {
log.ZError(ctx, "GetMessageBySeq err", err, "conversationID", tips.ConversationID, "seq", v)
continue
}
var s sdk_struct.MsgStruct
copier.Copy(&s, msg)
err = c.msgConvert(&s)
if err != nil {
log.ZError(ctx, "parsing data error", err, "msg", msg)
}
if err := c.deleteMessageFromLocal(ctx, tips.ConversationID, msg.ClientMsgID); err != nil {
log.ZError(ctx, "deleteMessageFromLocal err", err, "conversationID", tips.ConversationID, "seq", v)
}
}
}
func (c *Conversation) doClearConversations(ctx context.Context, msg *sdkws.MsgData) {
tips := sdkws.ClearConversationTips{}
utils.UnmarshalNotificationElem(msg.Content, &tips)
log.ZDebug(ctx, "doClearConversations", "tips", tips)
for _, v := range tips.ConversationIDs {
if err := c.clearConversationAndDeleteAllMsg(ctx, v, false, c.db.ClearConversation); err != nil {
log.ZError(ctx, "clearConversation err", err, "conversationID", v)
}
}
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.ConChange, Args: tips.ConversationIDs}})
c.doUpdateConversation(common.Cmd2Value{Value: common.UpdateConNode{Action: constant.TotalUnreadMessageChanged}})
}