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.
383 lines
17 KiB
383 lines
17 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.
|
|
|
|
//go:build !js
|
|
// +build !js
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/db/model_struct"
|
|
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
|
|
|
|
"github.com/openimsdk/tools/errs"
|
|
"github.com/openimsdk/tools/log"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func (d *DataBase) GetConversationByUserID(ctx context.Context, userID string) (*model_struct.LocalConversation, error) {
|
|
var conversation model_struct.LocalConversation
|
|
err := utils.Wrap(d.conn.WithContext(ctx).Where("user_id=?", userID).Find(&conversation).Error, "GetConversationByUserID error")
|
|
return &conversation, err
|
|
}
|
|
|
|
func (d *DataBase) GetAllConversationListDB(ctx context.Context) ([]*model_struct.LocalConversation, error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
var conversationList []*model_struct.LocalConversation
|
|
err := utils.Wrap(d.conn.WithContext(ctx).Where("latest_msg_send_time > ?", 0).Order("case when is_pinned=1 then 0 else 1 end,max(latest_msg_send_time,draft_text_time) DESC").Find(&conversationList).Error,
|
|
"GetAllConversationList failed")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return conversationList, err
|
|
}
|
|
func (d *DataBase) FindAllConversationConversationID(ctx context.Context) (conversationIDs []string, err error) {
|
|
return conversationIDs, utils.Wrap(d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("latest_msg_send_time > ?", 0).Pluck("conversation_id", &conversationIDs).Error, "")
|
|
}
|
|
func (d *DataBase) GetHiddenConversationList(ctx context.Context) ([]*model_struct.LocalConversation, error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
var conversationList []model_struct.LocalConversation
|
|
err := utils.Wrap(d.conn.WithContext(ctx).Where("latest_msg_send_time = ?", 0).Find(&conversationList).Error,
|
|
"GetHiddenConversationList failed")
|
|
var transfer []*model_struct.LocalConversation
|
|
for _, v := range conversationList {
|
|
v1 := v
|
|
transfer = append(transfer, &v1)
|
|
}
|
|
return transfer, err
|
|
}
|
|
|
|
func (d *DataBase) GetAllConversations(ctx context.Context) ([]*model_struct.LocalConversation, error) {
|
|
var conversationList []*model_struct.LocalConversation
|
|
err := utils.Wrap(d.conn.WithContext(ctx).Find(&conversationList).Error, "GetAllConversations failed")
|
|
return conversationList, err
|
|
}
|
|
|
|
func (d *DataBase) GetAllConversationIDList(ctx context.Context) (result []string, err error) {
|
|
d.groupMtx.Lock()
|
|
defer d.groupMtx.Unlock()
|
|
var c model_struct.LocalConversation
|
|
err = d.conn.WithContext(ctx).Model(&c).Pluck("conversation_id", &result).Error
|
|
return result, utils.Wrap(err, "GetAllConversationIDList failed ")
|
|
}
|
|
|
|
func (d *DataBase) GetAllSingleConversationIDList(ctx context.Context) (result []string, err error) {
|
|
d.groupMtx.Lock()
|
|
defer d.groupMtx.Unlock()
|
|
var c model_struct.LocalConversation
|
|
err = d.conn.WithContext(ctx).Model(&c).Where("conversation_type = ?", constant.SingleChatType).Pluck("conversation_id", &result).Error
|
|
return result, utils.Wrap(err, "GetAllConversationIDList failed ")
|
|
}
|
|
|
|
func (d *DataBase) GetConversationListSplitDB(ctx context.Context, offset, count int) ([]*model_struct.LocalConversation, error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
var conversationList []model_struct.LocalConversation
|
|
err := utils.Wrap(d.conn.WithContext(ctx).Where("latest_msg_send_time > ?", 0).Order("case when is_pinned=1 then 0 else 1 end,max(latest_msg_send_time,draft_text_time) DESC").Offset(offset).Limit(count).Find(&conversationList).Error,
|
|
"GetFriendList failed")
|
|
var transfer []*model_struct.LocalConversation
|
|
for _, v := range conversationList {
|
|
v1 := v
|
|
transfer = append(transfer, &v1)
|
|
}
|
|
return transfer, err
|
|
}
|
|
func (d *DataBase) BatchInsertConversationList(ctx context.Context, conversationList []*model_struct.LocalConversation) error {
|
|
if conversationList == nil {
|
|
return nil
|
|
}
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
|
|
return utils.Wrap(d.conn.WithContext(ctx).Create(conversationList).Error, "BatchInsertConversationList failed")
|
|
}
|
|
|
|
func (d *DataBase) UpdateOrCreateConversations(ctx context.Context, conversationList []*model_struct.LocalConversation) error {
|
|
var conversationIDs []string
|
|
if err := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Pluck("conversation_id", &conversationIDs).Error; err != nil {
|
|
return err
|
|
}
|
|
var notExistConversations []*model_struct.LocalConversation
|
|
var existConversations []*model_struct.LocalConversation
|
|
for i, v := range conversationList {
|
|
if utils.IsContain(v.ConversationID, conversationIDs) {
|
|
existConversations = append(existConversations, v)
|
|
continue
|
|
} else {
|
|
notExistConversations = append(notExistConversations, conversationList[i])
|
|
}
|
|
}
|
|
if len(notExistConversations) > 0 {
|
|
if err := d.conn.WithContext(ctx).Create(notExistConversations).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, v := range existConversations {
|
|
if err := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("conversation_id = ?", v.ConversationID).Updates(map[string]interface{}{"unread_count": v.UnreadCount}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *DataBase) InsertConversation(ctx context.Context, conversationList *model_struct.LocalConversation) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
return utils.Wrap(d.conn.WithContext(ctx).Create(conversationList).Error, "InsertConversation failed")
|
|
}
|
|
|
|
func (d *DataBase) DeleteConversation(ctx context.Context, conversationID string) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
return utils.Wrap(d.conn.WithContext(ctx).Where("conversation_id = ?", conversationID).Delete(&model_struct.LocalConversation{}).Error, "DeleteConversation failed")
|
|
}
|
|
|
|
func (d *DataBase) GetConversation(ctx context.Context, conversationID string) (*model_struct.LocalConversation, error) {
|
|
var c model_struct.LocalConversation
|
|
return &c, utils.Wrap(d.conn.WithContext(ctx).Where("conversation_id = ?",
|
|
conversationID).Take(&c).Error, "GetConversation failed, conversationID: "+conversationID)
|
|
}
|
|
|
|
func (d *DataBase) UpdateConversation(ctx context.Context, c *model_struct.LocalConversation) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
d.conn.WithContext(ctx).Logger.LogMode(6)
|
|
t := d.conn.WithContext(ctx).Updates(c)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "UpdateConversation failed")
|
|
}
|
|
|
|
func (d *DataBase) UpdateConversationForSync(ctx context.Context, c *model_struct.LocalConversation) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
t := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("conversation_id = ?", c.ConversationID).
|
|
Updates(map[string]interface{}{"recv_msg_opt": c.RecvMsgOpt, "is_pinned": c.IsPinned, "is_private_chat": c.IsPrivateChat,
|
|
"group_at_type": c.GroupAtType, "is_not_in_group": c.IsNotInGroup, "update_unread_count_time": c.UpdateUnreadCountTime, "ex": c.Ex, "attached_info": c.AttachedInfo,
|
|
"burn_duration": c.BurnDuration, "msg_destruct_time": c.MsgDestructTime, "is_msg_destruct": c.IsMsgDestruct})
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "UpdateConversation failed")
|
|
}
|
|
|
|
func (d *DataBase) BatchUpdateConversationList(ctx context.Context, conversationList []*model_struct.LocalConversation) error {
|
|
for _, v := range conversationList {
|
|
err := d.UpdateConversation(ctx, v)
|
|
if err != nil {
|
|
return utils.Wrap(err, "BatchUpdateConversationList failed")
|
|
}
|
|
|
|
}
|
|
return nil
|
|
}
|
|
func (d *DataBase) ConversationIfExists(ctx context.Context, conversationID string) (bool, error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
var count int64
|
|
t := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("conversation_id = ?",
|
|
conversationID).Count(&count)
|
|
if t.Error != nil {
|
|
return false, utils.Wrap(t.Error, "ConversationIfExists get failed")
|
|
}
|
|
if count != 1 {
|
|
return false, nil
|
|
} else {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
// Reset the conversation is equivalent to deleting the conversation,
|
|
// and the GetAllConversation or GetConversationListSplit interface will no longer be obtained.
|
|
func (d *DataBase) ResetConversation(ctx context.Context, conversationID string) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
c := model_struct.LocalConversation{ConversationID: conversationID, UnreadCount: 0, LatestMsg: "", LatestMsgSendTime: 0, DraftText: "", DraftTextTime: 0}
|
|
t := d.conn.WithContext(ctx).Select("unread_count", "latest_msg", "latest_msg_send_time", "draft_text", "draft_text_time").Updates(c)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "ResetConversation failed")
|
|
}
|
|
|
|
// ResetAllConversation Reset ALL conversation is equivalent to deleting the conversation,
|
|
// and the GetAllConversation or GetConversationListSplit interface will no longer be obtained.
|
|
func (d *DataBase) ResetAllConversation(ctx context.Context) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
c := model_struct.LocalConversation{UnreadCount: 0, LatestMsg: "", LatestMsgSendTime: 0, DraftText: "", DraftTextTime: 0}
|
|
t := d.conn.WithContext(ctx).Session(&gorm.Session{AllowGlobalUpdate: true}).Select("unread_count", "latest_msg", "latest_msg_send_time", "draft_text", "draft_text_time").Updates(c)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "ResetConversation failed")
|
|
}
|
|
|
|
// Clear the conversation, which is used to delete the conversation history message and clear the conversation at the same time.
|
|
// The GetAllConversation or GetConversationListSplit interface can still be obtained,
|
|
// but there is no latest message.
|
|
func (d *DataBase) ClearConversation(ctx context.Context, conversationID string) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
c := model_struct.LocalConversation{ConversationID: conversationID, UnreadCount: 0, LatestMsg: "", DraftText: "", DraftTextTime: 0}
|
|
t := d.conn.WithContext(ctx).Select("unread_count", "latest_msg", "draft_text", "draft_text_time").Updates(c)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "ClearConversation failed")
|
|
}
|
|
|
|
func (d *DataBase) SetConversationDraftDB(ctx context.Context, conversationID, draftText string) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
nowTime := utils.GetCurrentTimestampByMill()
|
|
t := d.conn.WithContext(ctx).Exec("update local_conversations set draft_text=?,draft_text_time=?,latest_msg_send_time=case when latest_msg_send_time=? then ? else latest_msg_send_time end where conversation_id=?",
|
|
draftText, nowTime, 0, nowTime, conversationID)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "SetConversationDraft failed")
|
|
}
|
|
func (d *DataBase) RemoveConversationDraft(ctx context.Context, conversationID, draftText string) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
c := model_struct.LocalConversation{ConversationID: conversationID, DraftText: draftText, DraftTextTime: 0}
|
|
t := d.conn.WithContext(ctx).Select("draft_text", "draft_text_time").Updates(c)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "RemoveConversationDraft failed")
|
|
}
|
|
func (d *DataBase) UnPinConversation(ctx context.Context, conversationID string, isPinned int) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
t := d.conn.WithContext(ctx).Exec("update local_conversations set is_pinned=?,draft_text_time=case when draft_text=? then ? else draft_text_time end where conversation_id=?",
|
|
isPinned, "", 0, conversationID)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "UnPinConversation failed")
|
|
}
|
|
|
|
func (d *DataBase) UpdateColumnsConversation(ctx context.Context, conversationID string, args map[string]interface{}) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
t := d.conn.WithContext(ctx).Model(model_struct.LocalConversation{ConversationID: conversationID}).Updates(args)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errs.ErrRecordNotFound, "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "UpdateColumnsConversation failed")
|
|
}
|
|
func (d *DataBase) UpdateAllConversation(ctx context.Context, conversation *model_struct.LocalConversation) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
if conversation.ConversationID != "" {
|
|
return utils.Wrap(errors.New("not update all conversation"), "UpdateAllConversation failed")
|
|
}
|
|
t := d.conn.WithContext(ctx).Model(conversation).Updates(conversation)
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "UpdateColumnsConversation failed")
|
|
}
|
|
func (d *DataBase) IncrConversationUnreadCount(ctx context.Context, conversationID string) error {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
c := model_struct.LocalConversation{ConversationID: conversationID}
|
|
t := d.conn.WithContext(ctx).Model(&c).Update("unread_count", gorm.Expr("unread_count+?", 1))
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "IncrConversationUnreadCount failed")
|
|
}
|
|
func (d *DataBase) GetTotalUnreadMsgCountDB(ctx context.Context) (totalUnreadCount int32, err error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
var result []int64
|
|
err = d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("recv_msg_opt < ? and latest_msg_send_time > ?", constant.ReceiveNotNotifyMessage, 0).Pluck("unread_count", &result).Error
|
|
if err != nil {
|
|
return totalUnreadCount, utils.Wrap(errors.New("GetTotalUnreadMsgCount err"), "GetTotalUnreadMsgCount err")
|
|
}
|
|
for _, v := range result {
|
|
totalUnreadCount += int32(v)
|
|
}
|
|
return totalUnreadCount, nil
|
|
}
|
|
|
|
func (d *DataBase) SetMultipleConversationRecvMsgOpt(ctx context.Context, conversationIDList []string, opt int) (err error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
t := d.conn.WithContext(ctx).Model(&model_struct.LocalConversation{}).Where("conversation_id IN ?", conversationIDList).Updates(map[string]interface{}{"recv_msg_opt": opt})
|
|
if t.RowsAffected == 0 {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
return utils.Wrap(t.Error, "SetMultipleConversationRecvMsgOpt failed")
|
|
}
|
|
|
|
func (d *DataBase) GetMultipleConversationDB(ctx context.Context, conversationIDList []string) (result []*model_struct.LocalConversation, err error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
var conversationList []model_struct.LocalConversation
|
|
err = utils.Wrap(d.conn.WithContext(ctx).Where("conversation_id IN ?", conversationIDList).Find(&conversationList).Error, "GetMultipleConversation failed")
|
|
for _, v := range conversationList {
|
|
v1 := v
|
|
result = append(result, &v1)
|
|
}
|
|
return result, err
|
|
}
|
|
func (d *DataBase) DecrConversationUnreadCount(ctx context.Context, conversationID string, count int64) (err error) {
|
|
d.mRWMutex.Lock()
|
|
defer d.mRWMutex.Unlock()
|
|
tx := d.conn.WithContext(ctx).Begin()
|
|
c := model_struct.LocalConversation{ConversationID: conversationID}
|
|
t := tx.Model(&c).Update("unread_count", gorm.Expr("unread_count-?", count))
|
|
if t.Error != nil {
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
if err := tx.Where("conversation_id = ?",
|
|
conversationID).Take(&c).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Wrap(errors.New("get conversation err"), "")
|
|
}
|
|
if c.UnreadCount < 0 {
|
|
log.ZWarn(ctx, "decr unread count < 0", nil, "conversationID", conversationID, "count", count)
|
|
if t = tx.Model(&c).Update("unread_count", 0); t.Error != nil {
|
|
tx.Rollback()
|
|
return utils.Wrap(errors.New("RowsAffected == 0"), "no update")
|
|
}
|
|
}
|
|
tx.Commit()
|
|
return nil
|
|
}
|
|
func (d *DataBase) SearchConversations(ctx context.Context, searchParam string) ([]*model_struct.LocalConversation, error) {
|
|
// Define the search condition based on the searchParam
|
|
condition := fmt.Sprintf("show_name like %q ", "%"+searchParam+"%")
|
|
|
|
var conversationList []model_struct.LocalConversation
|
|
err := d.conn.WithContext(ctx).Where(condition).Order("latest_msg_send_time DESC").Find(&conversationList).Error
|
|
var transfer []*model_struct.LocalConversation
|
|
for _, v := range conversationList {
|
|
v1 := v // Create a copy to avoid referencing the loop variable
|
|
transfer = append(transfer, &v1)
|
|
}
|
|
|
|
return transfer, utils.Wrap(err, "SearchConversation failed ")
|
|
}
|
|
|